如何使用 WebAssembly 扩展后端应用
1. WebAssembly 简介
随着互联网的发展,越来越多的应用借助 Javascript 转到了 Web 端,但人们也发现,随着移动互联网的兴起,需要把大量的应用迁移到手机端,随着手端的应用逻辑越来越复杂,Javascript 的解析、编译消耗了大量时间,导致页面加载慢,应用性能低下的很多问题。
为了解决这些问题,Mozilla 的工程师 Alon Zakai 在 2012 年提出了 Asm.js。之后,经过几年的发展,最终在 2015 年演变成了 WebAssembly。
WebAssembly(简写为 Wasm)是一种用于堆栈式虚拟机的二进制指令格式。它的设计目的,是成为其它编程语言的一个可移植的编译目标,以便在 Web 上布署客户端和服务端应用。
这是 WebAssembly 官网上的定义,从这个定义中我们可以知道,WebAssembly 是一种二进制指令格式。但在日常的讨论中,我们也常常把 WebAssembly Text Format 称为 WebAssembly,而这种文本格式实际上是一种编程语言。
正式发布后,WebAssembly 迎来了迅猛的发展,到 2017 年 11 月,Mozilla 宣布包括 Chrome、Firefox、Safari 等在内的所有主流浏览器都已经支持 WebAssembly,而根据 2021 年 7 月的数据,用户正在使用的浏览器中,已经有 94% 支持了 WebAssembly。
在得到了浏览器的广泛支持之后,一些重量级的应用也被逐渐被移植到了 Web 端,其中包括:
Google Earth — 一个3D的地图应用
AutoCAD — 一个工业制图的应用
Doom — 一个经典的第一人称的射击游戏
TensorFlow — Google 开源的机器学习框架
…
这些案例也说明 WebAssembly 达到了自己的设计目标——在 Web 上部署桌面上的原生应用。而 WebAssembly 能够获得如此快速的发展,得益于它的几个特点:
1.1 性能好
接近机器代码的运行速度,评测表明,WebAssembly 只比原生代码慢大约 10%。
1.2 体积小
加载速度快,WebAssembly 是一种紧凑的二进制格式,体积通常远小于完成同样功能的 Javascript 代码。
1.3 安全高
WebAssembly 代码运行在沙箱内,默认无法进行任何外部访问。
1.4 多语言
WebAssembly 并不限制用户使用何种语言开发,只要有相应的编译器,任何语言都可以被编译成 WebAssembly。
2. WebAssembly 在后端的应用
在 WebAssembly 的官方定义中,“用于堆栈式虚拟机的”这个定语也非常值得关注,因为它导致 WebAssembly 这项最初以 Web 端为应用场景,以至于名字中都包含“Web”这个词的技术,慢慢进入了后端应用的领域。
这是因为,从早期的 VMWare WorkStation、VirtualBox,到今天的 Docker,虚拟化技术一直是云计算的重要基础。所以,WebAssembly 作为一种具有很多特点的虚拟机代码格式,进入后端应用领域是必然趋势。Docker 的创始人 Solomon Hykes 在 2019 年说“如果 2008 年就有 WASM 和 WASI,我们就不用发明 Docker 了”,对其在后端应用的前景之看好,可见一斑。
当然,Solon Hykes 后来也说他的意思不是“WebAssembly 会取代 Docker”,这也是当今业界普遍的观点:WebAssembly 和 Docker 各有优势,互为补充。具体来说:
WebAssembly 程序的体积通常只有1M左右,而 Docker 镜像则动辄超过100M,所以 WebAssembly 具有快得多的加载速度。
WebAssembly 程序的冷启动速度大约是 Docker 容器的 100 倍。
WebAssembly 默认运行在沙箱内,任何与外部的交互,都只有获得明确的许可之后才能进行,具有极佳的安全性。
WebAssembly 模块仅仅是二进制的程序代码,不包含操作系统环境,所以不可能像在Docker 中那样简单的编译下就能执行。
下面,通过实际的示例介绍下如何在后端应用中使用 WebAssembly。
2.1. 在应用中嵌入 WebAssembly
如下图所示,不管是 Web 应用还是非 Web 应用,要使用 WebAssembly,都需要在宿主程序中嵌入 WebAssembly 运行时引擎,区别只在于,在 Web 应用中,这个宿主程序是浏览器,而在非 Web 应用中,宿主程序是我们自己开发的,具体到本文关注的后端应用,宿主程序则是我们的后端服务程序。
目前可选的 WebAssembly 运行时引擎有 Wasmtime, WasmEdge, WAVM, Wasmer等很多种,各有自己的优势和缺陷。本文将以 Wasmtime 为例,介绍如何在以 Go 语言开发的宿主程序中嵌入 WebAssembly。
嵌入 WebAssembly 运行时引擎并实例化 WebAssembly 模块本身非常简单,在省略错误处理的情况下,只要下面几行代码即可:
func createWasmVM(code []byte) {
engine := wasmtime.NewEngine()
module, _ := wasmtime.NewModule(engine, code)
store := wasmtime.NewStore(engine)
linker := wasmtime.NewLinker(engine)
inst, _ := linker.Instantiate(store, module)
_ = inst
}
其中涉及了实际开发中需要了解的几个重要概念,简单介绍如下:
引擎(engine):用于编译和管理模块的全局上下文。
模块(module):编译后的 WebAssembly 模块。
仓库(store):WebAssembly 程序和宿主之间的纽带,维护各种关联信息。
实例(instance):实例化的模块,是真正可以执行的程序。
链接器(linker):wasmtime 中的一个工具对象,用于实例化模块。
虽然上面的代码创建了 WebAssembly 模块的实例,并且根据 WebAssembly 的规范,模块实例化时就可以执行 WebAssembly 代码,但由于安全性的限制,其执行结果无法对外输出,所以这种“执行”毫无意义。因此,我们需要实现宿主程序和 WebAssembly 程序的互操作,为 WebAssembly 程序提供输入/输出接口。
2.2. 宿主调用 WebAssembly
假设我们的WebAssembly 程序中有一个名为 sum 的函数,接收两个整形变量作为参数,返回它们的和,则宿主程序可以使用下面的代码来调用这个函数:
fn := inst.GetExport(store, “sum”).Func()
r, _ := fn.Call(store, 1, 2)
fmt.Println(r.(int32))
虽然不同的宿主开发语言和 WebAssembly 运行时引擎具体的调用方式有区别,但运行时引擎的文档一般都有相关说明,所以这一步照着文档做就好,没有难度。
这里的难点在于,如何才能在 WebAssembly 程序中暴露出这个函数,以便宿主程序能找到并调用它。前面说过,只要有相应的编译器,各种语言都可以编译成 WebAssembly,但大多数语言设计时并没有考虑 WebAssembly 的需要,也就没有提供暴露函数的方法。所以这个问题只能通过特定编译器的非标准扩展来解决。也就是说,找到这个非标准扩展是解决问题最关键的一步。但也正由于“非标准”,所以相关的资料有时并不容易找到。
作为示例,下面给出的是使用 C/C++(编译器是 emscripten)和 AssemblyScript 时对外暴露函数的方法:
// C/C++
EMSCRIPTEN_KEEPALIVE int sum( int a, int b ) {
return a + b;
}
// AssemblyScript
export function sum( a: i32, b: i32 ): i32 {
return a + b
}
2.3. WebAssembly 调用宿主
与宿主调用 WebAssembly 类似,运行时引擎的文档中一般会介绍宿主端如何暴露一个函数给 WebAssembly 程序。
fn := func() {
fmt.Println(“hello wasm”)
}
linker.DefineFunc(store, “easegress”, “hello”, fn)
问题的难度同样在于 WebAssembly 程序中如何使用语言的非标准扩展来导入这个函数,下面是 C/C++ 和 AssemblyScript 中的具体方法:
// C/C++
attribute((import_module(“easegress”), import_name(“hello”))) void hello();
void call_hello() {
hello();
}
// AssemblyScript
@external(“easegress”, “hello”) declare function hello(): void
export function callHello(): void {
hello()
}
2.4. 复杂参数传递
宿主和 WebAssembly 程序相互调用对方的函数时,也需要传递参数和返回值,如果是整数等简单数据类型,直接传递即可。但当数据类型是字符串等复杂类型时,就会遇到新的问题,具体有两点:
宿主程序和 WebAssembly 程序的开发语言一般并不相同,所以复杂参数的内存布局也不同,直接传递的话,接收方根本理解不了,也就没有办法使用。
由于安全性设计,宿主程序和 WebAssembly 程序的内存是隔离开的,WebAssembly 程序访问不了宿主的内存。
因为宿主程序可以访问 WebAssembly 的内存,所以第二个问题的解决方法是 WebAssembly 程序暴露内存管理的相关函数,让宿主来操作 WebAssembly 的内存,例如:
// C/C++
EMSCRIPTEN_KEEPALIVE void* wasm_alloc( int size ) {
return malloc( size );
}
EMSCRIPTEN_KEEPALIVE void wasm_free( void* p ) {
free( p );
}
// AssemblyScript
export function wasm_alloc(size: i32): number {
let buf = new ArrayBuffer(size)
let ptr = changetype(buffer)
return __pin(ptr)
}
export function wasm_free(ptr: number): void {
__unpin(ptr)
}
之后,我们可以借助这些内存管理函数,并通过序列化/反序列化相关数据类型来传递它们,比如在下面 WebAssembly 调用宿主函数的代码中,参数和返回值本来各是一个字符串,但通过序列化/反序列化之后,只要传递它们在 WebAssembly 内存中的地址(整数)即可:
// Host: Go
func foo(addr int32) int32 {
// …
mem := inst.GetExport(store, “memory”).Memory().UnsafeData(store)
start := addr
for mem[addr] != 0 {
addr++
}
data := make([]byte, addr-start)
copy(data, mem[start:addr])
param := string(data)
// …
result := “result”
vaddr, _ := inst.GetExport(store, “wasm_alloc”).Func().Call(store, len(result)+1)
addr = vaddr.(in32)
copy(mem[addr:], []byte(result))
mem[addr + int32(len(result))] = 0
return addr
}
// WebAssembly: AssemblyScript
@external(“easegress”, “foo”) declare function foo(addr: number): number
export func callFoo(name: string): string {
let buf = String.UTF8.encode(name + ‘’)
let addr = changetype(buf)
addr = foo(addr)
buf = changetype(addr)
wasm_free(addr)
buf = buf.slice(0, buf.byteLength - 1)
return String.UTF8.decode(buf)
}
2.5. SDK
看了上面宿主程序和 WebAssembly 程序互操作的过程,相信大家已经发现它和 RPC 调用的过程非常像。不同的是,在 RPC 调用中,一系列非常繁琐的序列化/反序列化操作,都由工具自动生成代码实现,使用者根本无需关心。
而在 WebAssembly 应用的开发中,用户也不希望每次都处理那么多的细节。所以,作为宿主程序的开发者,我们需要为用户提供相关 SDK,屏蔽掉底层细节,让用户能够专注于业务逻辑的开发。
由于用户可以使用多种语言开发 WebAssembly 应用,所以我们需要提供针对不同语言的 SDK,或者至少也需要覆盖目标用户使用的主流语言。
2.6. 错误处理
像普通程序一样,WebAssembly 程序也会有各种错误,虽然作为宿主程序的开发者,我们无法预知具体的错误,但我们必须把这些错误的影响范围限制在 WebAssembly 虚拟机内部,不能让它们影响宿主程序的功能。
宿主程序要防范的第一类错误是 WebAssembly 程序中的死循环。实际场景中,其实宿主程序并没有办法判断是否真的出现了死循环,所以,折衷的解决办法是给 WebAssembly 程序设置一个最大运行时间,一旦超过了这个时间还没有结束,就认为里面出现了死循环,并终止它的执行。终止执行的示例代码如下:
ih, _ := store.InterruptHandle()
ih.Interrupt()
3. Easegress 中的 WebAssembly
Easegress 是 MegaEase 开发的下一代流量型网关,具有云原生、高可用、可观测、可扩展等特点。在 Easegress 之前,市场上已经有包括 nginx 在内的多个成熟网关产品。但 MegaEase 认为,流量调度型网关并不仅仅是一个反向代理,还需要能够动态的进行流量编排和调度。此外,在具体应用场景中,还涉及各种各样的业务逻辑侵入,因此,必须高度可扩展。
基于以上观点,Easegress 从诞生之日就将可扩展性放到了重要位置,并在多个层面进行了针对性的设计。
首先,在开发语言的选择上,我们有C/C++/Java/Rust/Go这些主流的静态语言的选项:
使用 C/C++ 或 Rust 肯定会给 Easegress 带来最好的性能,但这些语言门槛太高,一般用户很难掌握,尤其在写业务逻辑的代码上效率实在是太低。也就谈不上通过修改代码来扩展业务逻辑了
Java 易学易用,也非常适合写业务逻辑,但是体积大,而且性能不能满足要求;
相对而言,Go 简单易学,性能也比较好,特别是在 Easegress 所处的网络应用领域,由于语言本身的特殊设计,很多场景下与 C/C++ 的性能差距基本可以忽略。所以,Easegress 最终选择了 Go 作为开发语言。但不管使用什么语言,源码级的扩展都不可避免的将用户限制到一门特定的语言,而且会涉及重新编译、重新布署、重新启动,造成服务的中断。
第二,通过外部调用的方式扩展,如FaaS。通过云原生的 FaaS 方式的好处是不限制用户的开发语言,而且可以让整个架构具有很好的可伸缩性,但其缺点是需要引用 Kubernetes 等的比较重的外部依赖,导致整个运维非常复杂(注:Easegress 目前已经支持了 Knative)
第三,通过嵌入其它语言的解释器进行扩展。这方面,Lua 本身就是为嵌入其它程序而设计的,具有相应的优势。但我们认为 Lua 也有两个缺点,一是本身表现力不够,不适合写复杂的业务逻辑;二是太小众,不是主流语言,有相关经验的程序员太少。所以,经过权衡,Easegress 最终选择了嵌入 WebAssembly,主要基于两点考虑:一是接近于原生代码的高性能;二是不限制用户的开发语言,用户可以使用自己喜欢或熟悉的语言开发业务逻辑。
作为使用 WebAssembly 扩展业务逻辑的样例,我们之前已经发布了《使用 Easegress + WebAssembly 做秒杀》,欢迎大家阅读并向我们反馈更多的实际案例。
当然,选择了 WebAssembly 意味着我们需要开发多种语言的 SDK,目前已经完成了 AssemblyScript SDK 的开发,相信在 MegaEase 和整个开源社区的共同努力下,我们支持的语言会越来越多。欢迎大家关注我们的开源社区:https://github.com/megaease/
相关文章:

如何使用 WebAssembly 扩展后端应用
1. WebAssembly 简介 随着互联网的发展,越来越多的应用借助 Javascript 转到了 Web 端,但人们也发现,随着移动互联网的兴起,需要把大量的应用迁移到手机端,随着手端的应用逻辑越来越复杂,Javascript 的解析…...

BaseCTF_web_week3
复读机 输入的东西会在下方显示出来,wp说是简单的SSTI,这里来学习一下SSTI SSTI模板注入 根据My4n师傅的《超详细SSTI模板注入漏洞原理讲解_ssti注入-CSDN博客》写的 用户的输入返回时会经过一个模板渲染,SSTI漏洞就是用户插入了可以破坏模板…...

模型数据算法概论
模型数据算法是一类使用数据来构建、优化或训练数学模型的算法,常用于数据科学、机器学习和人工智能领域。它们的核心目标是从数据中提取模式或关系,以便在预测、分类或优化任务中应用。以下是一个简要的概述: 1. 模型 模型是用来描述数据特…...

什么是3DEXPERIENCE SOLIDWORKS,它有哪些角色和功能?
将业界领先的 SOLIDWORKS 3D CAD 解决方案连接到基于单一云端产品开发环境 3DEXPERIENCE 平台。您的团队、数据和流程全部连接到一个平台进行高效的协作工作,从而能快速的做出更好的决策。 目 录: ★ 1 什么是3DEXPERIENCE SOLIDWORKS ★ 2 3DEXPERIE…...

Sigrity System SI Parallel Bus Analysis模式进行DDR3仿真分析-传输线模型
Sigrity System SI Parallel Bus Analysis模式进行DDR3仿真分析-传输线模型 Sigrity System SI Parallel Bus Analysis模式提供了简易的已搭建好的模板,方便用户直接在模板上替换数据文件进行仿真分析,下面介绍pba_simple_em模板的使用,具体操作如下 双击启动System SI启动界…...

MacOS下PostIn安装配置指南
PostIn是一款开源免费的接口管理工具, 下面介绍私有部署版本的MacOS下安装与配置。私有部署版本更适合有严格数据安全要求的企业,实现对数据和系统的完全控制。   1、MacOS服务端安装 Mac安装包下载地址:下载Mac安…...

【Leetcode 每日一题】2545. 根据第 K 场考试的分数排序
问题背景 班里有 m m m 位学生,共计划组织 n n n 场考试。给你一个下标从 0 0 0 开始、大小为 m n m \times n mn 的整数矩阵 s c o r e score score,其中每一行对应一位学生,而 s c o r e [ i ] [ j ] score[i][j] score[i][j] 表示…...

一文速通 IIC I2C子系统驱动 通信协议原理 硬件 时序 深度剖析
本文作为一个引入,作用是让读者理解熟知IIC协议关键内容,结合实际手册内容,深度解析协议本质,作为后续嵌入式linux驱动IIC子系统的一个铺垫。 目录 1. 硬件连接 2. IIC传输时序 2.1.写操作 2.2.读操作 2.3.I2C信号 3.IIC协议…...

HarmonyOS(72)事件拦截处理详解
事件拦截 1、参考资料2、HitTestMode3、onTouchIntercept、onTouch、onClick事件执行顺序3.1、系统默认事件传递顺序3.2、子组件拦截事件1、参考资料 HarmonyOS(71) 自定义事件分发之TouchTestStrategy使用说明HarmonyOS(70) ArkUI 事件分发拦截,事件冲突解决方案HitTestModea…...

docker(wsl)命令 帮助文档
WSL wsl使用教程 wsl -l -v 列出所有已安装的 Linux 发行版 wsl -t Ubuntu-22.04 --shutdown 关闭所有正在运行的WSL发行版。如果你只想关闭特定的发行版 wsl -d Ubuntu-22.04 登录到Ubuntu环境 wsl --list --running 查看正在wsl中运行的linux发行版 wsl --unregister (系统名…...

nginx 拦截指定ip访问指定 url
nginx 拦截指定ip访问指定 url 这里需要注意的是一定要用$http_x_forwarded_for 这个变量 upstream myapp1 { # 定义一个名为myapp1的服务器组 server backend1.example.com weight5; # 添加一个服务器,并设置权重为5 server backend2.example.com; # 添加另…...

git仓库的基本概念和流程以及一些基本命令
什么是版本库?版本库又名仓库,英文名repository,你可以简单的理解一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改,删除,Git都能跟踪,以便任何时刻都可以追踪历史ÿ…...

Intel-ECI之Codesys PLC + Ethercat 远端IO + Codesys IDE编程
目录 一、 准备工作 二、安装Codesys 软件 PLC 三、 使用Codesys IDE 编程测试 CODESYS* 是领先的独立于制造商的 IEC 61131-3 自动化软件,适用于工程控制系统。它用于 Intel Edge Controls for Industrial(Intel ECI 或 ECI),…...

互联网医院系统,互联网医院系统源码可供
互联网医院系统开发,其功能特点和优势在于实现了线上医疗服务与信息技术的深度融合。此系统旨在构建一个更为高效、便捷的医疗服务平台,提升患者的就医体验和医疗服务的效率。 一、功能特点 1、预约挂号与在线咨询 患者可通过系统进行预约挂号…...

Marin说PCB之POC电路layout设计仿真案例---06
我们书接上回啊,对于上面的出现原因我这个美女同事安娜说会不会你把POC电感下面的相邻两层的CUT_OUT的尺寸再去加大一些会不会变得更好呢?这个难道说是真的有用吗?小编我先自己算一卦看下结果。 本期文章我们就接着验证通过改善我们的单板POC…...

windwos defender实现白名单效果(除了指定应用或端口其它一律禁止)禁止服务器上网
一、应用场景说明 当我们的一台windows服务器中毒,变成别人肉鸡,不断向外请示非法网站或攻击其它服务器。 要彻底清除相关木马或病毒往往需要的时间比较长,比较有效的方法是禁止服务器主动向外发包除了网站端口和远程程序除外。 其实这就是一…...

Fiddler勾选https后google浏览器网页访问不可用
一、说明 最近电脑重新安装系统后,之前的所有工具都需要重新安装和配置,有个项目需要抓包https包查看一下请求的内容,通过Fiddler工具,但是开启后,发现https的无法抓取,同时google浏览器也不无法访问互联网…...

机器视觉检测相机基础知识 | 颜色 | 光源 | 镜头 | 分辨率 / 精度 / 公差
注:本文为 “keyence 视觉沙龙中机器视觉检测基础知识” 文章合辑。 机器视觉检测基础知识(一)颜色篇 视觉检测硬件构成的基本部分包括:处理器、相机、镜头、光源。 其中,和光源相关的最重要的两个参数就是光源颜色和…...

解决pytorch安装中的三个错误
查明已安装python版本为3.12.7后,创建虚拟环境。 报错内容:ArgumentError: one of the arguments -n/–name -p/–prefix is required 解决方式: 输入 conda create -n pytorch python3.8即可安装成功。 参考文章:https://blo…...

用Python开发高级游戏:实现3D迷宫游戏
Python虽然被认为是一门简单易学的语言,但它在游戏开发领域同样具有强大的潜力,尤其是结合诸如Pygame、Panda3D、PyOpenGL等框架,可以开发出复杂的游戏。 在本文中,我们将通过一个示例,介绍如何使用Python开发一个高级3D迷宫游戏。本文使用的框架是 Panda3D,一个专为3D游…...

基于 uniapp 开发 android 播放 webrtc 流
一、播放rtsp协议流 如果 webrtc 流以 rtsp 协议返回,流地址如:rtsp://127.0.0.1:5115/session.mpg,uniapp的 <video> 编译到android上直接就能播放,但通常会有2-3秒的延迟。 二、播放webrtc协议流 如果 webrtc 流以 webrt…...

Unity引擎学习总结------动画控件
左侧窗格可以在参数视图和图层视图之间切换。参数视图允许您创建、查看和编辑动画控制器参数。这些是您定义的变量,用作状态机的输入。要添加参数,请单击加号图标并从弹出菜单中选择参数类型。要删除参数,请在列表中选择该参数并按删除键&…...

Pytorch | 从零构建GoogleNet对CIFAR10进行分类
Pytorch | 从零构建GoogleNet对CIFAR10进行分类 CIFAR10数据集GoogleNet网络结构特点网络整体架构应用与影响Inceptionv1到Inceptionv2 GoogleNet结构代码详解结构代码代码详解Inception 类初始化方法前向传播 forward GoogleNet 类初始化方法前向传播 forward 训练过程和测试结…...

基于SIFT的目标识别算法
基于SIFT(Scale-Invariant Feature Transform)的目标识别算法是一种经典的计算机视觉算法,用于在图像中寻找和匹配具有尺度不变性的特征点,从而实现目标的快速而准确的识别。 SIFT算法的主要步骤包括以下几个阶段: 尺…...

计算机组成原理的学习笔记(4)--数据的表示与运算·其三 补码的乘法以及原码补码的除法
学习笔记 前言 本文主要是对于b站尚硅谷的计算机组成原理的学习笔记,仅用于学习交流。 1.补码乘法 基本操作 与正常原码乘法差不多,逐位乘,随后相加,而与符号位有关的一项也叫校正项 Booth算法 从乘数的最低位开始,…...

压缩glb模型文件
使用?gltf-pipeline进行压缩: GitHub地址[这里是图片001]https://github.com/CesiumGS/gltf-pipeline 1. 安装gltf-pipeline npm install -g gltf-pipeline2. 在glb文件目录打开cmd进行命令行压缩: // cmd: gltf-pipeline -i glb.glb -d -s以下是 -…...

vertx idea快速使用
目录 1.官网下载项目 2.修改代码 2.1拷贝代码方式 为了能够快速使用,我另外创建一个新的maven项目,将下载项目的src文件和pom文件拷贝到新建的maven项目。 2.2删除.mvn方式 3.更新配置 4.配置application 5.idea启动项目 1.官网下载项目 从vert…...

如何创建属于自己的大语言模型:从零开始的指南
如何创建属于自己的大语言模型:从零开始的指南 为什么要创建自己的大语言模型? 随着人工智能的快速发展,大语言模型(LLM)在各种场景中表现出了卓越的能力,例如文本生成、对话交互和内容总结等。虽然市场上…...

debian linux 连网自动调整时间 (报错 Unit systemd-timesyncd.service could not be found.)
debian linux 连网自动调整时间 如果有报错 Unit systemd-timesyncd.service could not be found. 就用 apt 装一下 systemd-timesyncd 吧 参考: https://github.com/MichaIng/DietPi/issues/5472 sudo apt-get install systemd-timesyncd... ┌──(kali㉿kali)-[~] └─$ t…...

监控易在汽车制造行业信息化运维中的应用案例
引言 随着汽车制造行业的数字化转型不断深入,信息化类IT软硬件设备的运行状态监控、故障告警、报表报告以及网络运行状态监控等成为了企业运维管理的关键环节。监控易作为一款全面、高效的信息化运维管理工具,在汽车制造行业中发挥着重要作用。本文将结合…...