大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!
高级前端进阶
今天给大家带来的主题是最近因为 Bun运行时而大火的 µWebSockets,话不多说,直接进入正题。
1.什么是µWebSockets
µWebSockets是简单、安全且符合标准的 Web 服务器,适用于要求最苛刻的应用程序。
图片来自:https://morioh.com/p/d76ceffb2a17
µWebSockets具有以下特点:
- 优化的安全性:µWebSockets 针对速度和内存占用进行了精心优化,其速度足以执行加密的 TLS 1.3 消息传递,甚至比大多数替代服务器执行未加密的明文消息传递的速度还要快。
- 快速编写脚本:µWebSockets 完全用 C 和 C++ 编写,但与 Node.js 后端无缝集成。 这允许使用广泛的能力快速编写功能强大的应用程序的脚本。 除了支持与 Node.js 集成之外,开发者还可以使用 Bun,其中 µWebSockets 是 Bun 运行时内置 Web 服务器。
- 大量实战证明:从 2016 年以来,µWebSockets一直完全符合标准,拥有完美的 Autobahn|Testsuite 分数。µWebSockets 为世界上许多最大的加密货币交易所提供支持,每天处理数十亿美元的交易量。
- 开箱即用:围绕具有通配符和参数支持的方便 URL 路由器设计 ,与高效的 WebSockets 发布/订阅功能相结合。 µWebSockets 应该是任何具有高要求的实时 Web 项目的明智选择。
目前 µWebSockets 在Github上通过 Apache-2.0 协议开源,有超过 15.5k 的star、1.7k的fork、代码贡献者100+,是一个值得关注的前端开源项目。
2.详述 µWebSockets 特性
符合标准
与其他“发布/订阅代理”不同,µWS不推动任何特定的应用程序协议,而是仅在原始的标准 WebSockets 上运行。 开发者只需要一个符合标准的 Web 浏览器和一些符合标准的 JavaScript 即可与之通信。
µWebSockets也不需要或强制执行特定的客户端库,这与 Socket.IO 等解决方案不同,在 Socket.IO 中,开发者最终会被锁定到一组性能糟糕的专有非标准协议。
Socket.IO 支持实时双向基于事件的通信,由一个 Node.js 服务器和一个用于浏览器(或 Node.js 客户端)的 Javascript 客户端库组成。 支持众多语言实现,包括:Java、C++、Swift、Dart、Python、.NET、Rust。具有诸多优秀特性,比如:可靠、自动重新连接、断开检测、二进制支持、跨浏览器、多路复用支持等等。
高性能
µWebSockets实现仅包含头文件 C++17,跨平台并编译为几千字节的微小二进制文件,底层依赖于 µSockets。
µSockets 是 µWebSockets 使用的非阻塞、每个 CPU 一个线程(thread-per-CPU)的基础库。 µSockets 提供优化的网络,在所有支持的传输、事件循环和平台上使用相同的不透明 API编程接口(QUIC 正在开发中,io_uring 也是如此)。
借助于 µSockets,µWebSockets 应用程序可以在诸多平台上运行,不需要任何代码更改或特殊的执行路径。从而实现通过 TCP 传输数据与通过 QUIC 传输数据一样简单。
µWebSockets的高性能
在性能方面,µWebSockets 相对于其他解决方案具有明显的优势,使用 SSL 的 µWS 明显优于运行非 SSL 的最快 Golang 服务器的消息案例。 从某种意义上说,开发者可以免费获得 SSL(证明对于每条消息最多 4 kB 的消息传递是正确的)。
µWebSockets 公开展示了单个 Raspberry Pi 4 可以同时为超过 10 万个非常活跃的 TLS 1.3 WebSockets 提供服务的详细案例,并且具有出色的稳定性。 这对于绝大多数替代解决方案来说是完全不可能的。 在如此有限的硬件上,大多数解决方案都会变得不可靠。 同时,µWebSockets 与 Node.js 相比,支持每秒提供 12 倍的 HTTP 请求。
Raspberry Pi 4是一款单板计算机,所谓单板机是具有计算机功能的微型计算机,印刷板上有中央处理机、内存储器、外围设备、接口等主要部件。
使用简单
µWebSockets的另一个目标是极简主义、简单和优雅。 它遵循类似 ExpressJS 的界面,开发者可以在其中将回调附加到不同的 URL 路由, 这样就可以在几行代码中轻松构建完整的 REST/WebSocket 服务。
心跳超时、背压处理等繁琐的逻辑由库本身高效、轻松处理。 同时,该项目是异步的,并且在一个线程本地运行。 开发者将其扩展为单独的线程,就像 Node.js 扩展为单独的进程一样(也就是说,实现只看到一个线程并且不是线程安全的)。 但是,如果确实需要,有一些简单的方法可以通过异步委托进行线程处理。
编译
µWebSockets 可以在任何平台上编译, 但是正如前面所说它依赖于 µSockets,即在 Linux、Windows 和 macOS 上运行的特定于平台的 C 代码。
µSockets 有一些编译标志,但 µSockets 和 µWebSockets 标志之间有以下共同点:
- LIBUS_NO_SSL :为 uSockets 和 uWebSockets 构建禁用 OpenSSL 依赖/功能
- UWS_NO_ZLIB : 为 uWebSockets 禁用 Zlib 依赖/功能
3.µWebSockets 如何用于Node.js环境
µWebSockets.js 是 Node.js 中的 Web 服务器实现,它在高度优化的 C++ 中重新实现事件、网络、加密、Web 协议、路由和发布/订阅。 因此,µWebSockets.js 可以为 Node.js 提供 Web 服务,是 Fastify 的 8.5 倍,至少是 Socket.IO 的 10 倍, µWebSockets.js 也是 Bun 内置的 Web 服务器。
µWebSockets.js的目标是用符合标准、轻量级和高性能的堆栈完全取代 ExpressJS 和 Socket.io。 目前 µWebSockets.js 确实在性能上也完胜其他更流行的库,比如: websockets 和 ws。
下面是µWebSockets.js的使用示例:
/* Non-SSL is simply App() */
require('uWebSockets.js').SSLApp({
/* 这里支持众多的SSL配置 */
key_file_name: 'misc/key.pem',
cert_file_name: 'misc/cert.pem',
}).ws('/*', {
/* 有许多常见的帮助功能 */
idleTimeout: 32,
maxBackpressure: 1024,
maxPayloadLength: 512,
compression: DEDICATED_COMPRESSOR_3KB,
/* 为简洁起见,跳过其他事件,比如:upgrade, open, ping, pong, close */
message: (ws, message, isBinary) => {
/* 也可以执行 app.publish('sensors/home/temperature', '22C') 类型的发布/订阅*/
/* 在这里可以回显消息,还可以压缩*/
let ok = ws.send(message, isBinary, true);
}
}).get('/*', (res, req) => {
/* It does Http as well */
res.writeStatus('200 OK').writeHeader('IsExample', 'Yes').end('Hello there!');
}).listen(9001, (listenSocket) => {
if (listenSocket) {
console.log('Listening to port 9001');
}
});
3.1 编译 µWebSockets 二进制文件
在大多数情况下,开发者可以直接执行 npm install uNetworking/uWebSockets.js#v17.3.0 并直接获取预构建的二进制文件。 但是,如果遇到 µWS 版本与 Node.js 构建不兼容的问题,则必须在目标机器上编译二进制文件:
// 克隆仓库 w/ submodules
git clone --recursive https://github.com/uNetworking/uWebSockets.js.git
//Cd 到指定目录
cd uWebSockets.js
// build编译
make
如果要部署到 CentOS 服务器上,则必须在 build.c 中将编译器从 clang 更改为 gcc:
// Change from
build("clang", "clang++", "-static-libstdc++ -static-libgcc -s", OS, "x64");
// To this
build("gcc", "g++", "-static-libstdc++ -static-libgcc -s", OS, "x64");
3.2 µWebSockets用于服务器端
初始设置
下面从一个简单的模板开始:
// server.js
const uWS = require('./uws.js');
const { uuid } = require('uuidv4');
const port = 7777;
let SOCKETS = [];
const app = uWS.App()
.ws('/ws', {
// config
compression: 0,
maxPayloadLength: 16 * 1024 * 1024,
idleTimeout: 60,
open: (ws, req) => {
// this handler is called when a client opens a ws connection with the server
},
message: ws => {
// called when a client sends a message
},
close: (ws, code, message) => {
// called when a ws connection is closed
}
}).listen(port, token => {
token ?
console.log(`Listening to port ${port}`) :
console.log(`Failed to listen to port ${port}`);
});
如果运行 node server.js 则可以在 ws://127.0.0.1:7777/ws 开始接收传入的 ws 连接。
4.本文总结
本文主要和大家介绍一个最近因为Bun运行时而大火的µWebSockets。相信通过本文的阅读,大家对 µWebSockets 会有一个初步的了解。
因为篇幅有限,文章并没有过多展开,如果有兴趣,可以在我的主页继续阅读,同时文末的参考资料提供了大量优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!
参考资料
https://github.com/uNetworking/uWebSockets/blob/master/misc/READMORE.md
https://github.com/uNetworking/uWebSockets.js/
https://edisonchee.com/writing/intro-to-%C2%B5websockets.js/
https://github.com/socketio/socket.io
https://github.com/uNetworking/uSockets
https://morioh.com/p/d76ceffb2a17
https://ably.com/blog/whats-new-in-socketio-4