web3-providers-ws 4.x 演进全解:从 changelog 看 WebSocket 提供者的架构重构、构建体系与稳定化之路
2026/9/21 17:53:55 网站建设 项目流程

web3-providers-ws 4.x 演进全解:从 changelog 看 WebSocket 提供者的架构重构、构建体系与稳定化之路

【免费下载链接】web3.jsCollection of comprehensive TypeScript libraries for Interaction with the Ethereum JSON RPC API and utility functions.项目地址: https://gitcode.com/gh_mirrors/we/web3.js

web3-providers-ws是 web3.js 4.x 生态中专用于 WebSocket 协议的 provider 子包,负责以ws:///wss://长连接方式与 Ethereum 节点通信,支撑实时事件订阅等场景。本文以该包自 4.0.1-alpha 至 4.0.8 的 CHANGELOG.md 为骨架,逐条解读每个版本的关键变更,并结合仓库源码深入剖析SocketProvider抽象、重连机制、ESM/CJS 混合构建与消息分块解析等底层实现,帮助读者既看懂版本演进脉络,也掌握WebSocketProvider的实际用法与原理。

版本脉络总览:一条从 alpha 走向稳定的演进路线

回顾整个 changelog,web3-providers-ws的 4.x 早期版本大体可分为三个阶段:

阶段版本区间主题
依赖同步期4.0.1-alpha.2 / alpha.3 / alpha.5跟随 web3.js 主仓更新依赖
架构重构期4.0.1-alpha.4迁移到公共SocketProvider抽象类,废弃close事件
发布体系成型期4.0.1-rc.0 ~ 4.0.1命名导出、SocketConnectiongetter、源文件发布、ESM/CJS 混合构建
稳定性治理期4.0.2 ~ 4.0.8修复@types/ws问题、固定类型依赖版本、修复分块处理 bug

其中4.0.1是首个稳定(非预发布)版本,其 Release Notes 明确说明详细的变更日志均列于此前各 alpha 与 RC 版本中,因此 4.0.x 全系的核心能力在 4.0.1 已定型,后续版本以修复与依赖治理为主。

架构重构:统一抽象类 SocketProvider(4.0.1-alpha.4, #5683)

changelog 在4.0.1-alpha.4中记录了两项关键变更:

  • mainfiles字段由dist/改为lib/目录(#5739)
  • 重构为使用公共SocketProvider类(#5683)
  • 旧事件close被废弃,由disconnect取代(#5683)

其中第二条是架构层面的核心动作。重构后,WebSocketProvider不再自行管理底层连接细节,而是继承位于 socket_provider.ts 的抽象类SocketProvider。从源码可见该抽象类的职责划分:

  • 连接生命周期:connect()disconnect()safeDisconnect()reset()
  • 请求队列管理:_pendingRequestsQueue_sentRequestsQueue两张Map,分别存放连接建立前与已发送的请求
  • 事件体系:on/once/removeListener/removeAllListeners
  • 重连逻辑:_reconnect()ReconnectOptions
  • 消息解析:通过ChunkResponseParser处理分块响应

WebSocketProvider只需实现抽象方法即可完成一个完整 provider,见 src/index.ts:

protected _openSocketConnection() { this._socketConnection = new WebSocket( this._socketPath, undefined, this._socketOptions && Object.keys(this._socketOptions).length === 0 ? undefined : this._socketOptions, ); } protected _sendToSocket<Method extends Web3APIMethod<API>>(payload): void { if (this.getStatus() === 'disconnected') { throw new ConnectionNotOpenError(); } this._socketConnection?.send(JSON.stringify(payload)); } protected _parseResponses(event: WebSocket.MessageEvent) { return this.chunkResponseParser.parseResponse(event.data as string); }

这一抽象的价值在于:IpcProviderweb3-providers-ipc)与WebSocketProvider共享了全部队列、重连、事件逻辑,两个包的行为保持一致,也方便未来接入其他 socket 协议。

close → disconnect 事件迁移

同一次重构中,旧的事件名close被废弃,统一为disconnect。新的事件体系由SocketProvider提供:connectdisconnectmessagechainChangedaccountsChanged等。例如监听断开事件:

provider.on('disconnect', error => { console.log('连接已断开', error); });

集成测试 reconnection.test.ts 中即通过waitForEvent(web3Provider, 'connect')等待连接建立,验证了这套新事件体系的可用性。

重连机制与 ReconnectOptions:默认值与自定义策略

SocketProvider在构造函数中将用户传入的reconnectOptions与默认值合并:

export type ReconnectOptions = { autoReconnect: boolean; delay: number; maxAttempts: number; }; const DEFAULT_RECONNECTION_OPTIONS = { autoReconnect: true, delay: 5000, maxAttempts: 5, };

也就是说,默认开启自动重连、间隔 5 秒、最多尝试 5 次。集成测试明确断言了这三个默认值,见 reconnection.test.ts:

expect(web3Provider._reconnectOptions).toEqual({ autoReconnect: true, delay: 5000, maxAttempts: 5, });

自定义重连策略时,只需传入第三个构造参数:

const provider = new WebSocketProvider( 'wss://mainnet.infura.io/ws/v3/YOUR_INFURA_ID', {}, { delay: 500, autoReconnect: true, maxAttempts: 10, }, );

_reconnect()的实现(socket_provider.ts)可以看到几个关键细节:

  • 重连时会对_sentRequestsQueue中所有请求以PendingRequestsOnReconnectingError拒绝,避免请求悬挂;
  • 若重连次数未达maxAttempts,则延迟delay毫秒后重连;超过则清空队列并以MaxAttemptsReachedOnReconnectingError触发error事件;
  • _onCloseEvent(src/index.ts)中,仅当autoReconnect开启且关闭码不在[1000, 1001](正常关闭码)或!event.wasClean时才触发重连,避免对正常关闭反复重连。

disconnect()默认使用关闭码1000NORMAL_CLOSE_CODE),safeDisconnect()则先等待待处理与已发送队列清空再断开,适合需要优雅收尾的场景。

构建与发布体系演进:dist → lib 与 ESM/CJS 混合构建

changelog 中关于构建发布的变更链条非常清晰:

  • 4.0.1-alpha.4(#5739):main/filesdist/改为lib/
  • 4.0.1-rc.1(#5904):新增 ESM 与 CJS 的混合构建
  • 4.0.1-rc.1(#5956):发布内容中加入源文件

当前 package.json 印证了这套体系:

{ "name": "web3-providers-ws", "version": "4.0.8", "main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", "exports": { ".": { "types": "./lib/types/index.d.ts", "import": "./lib/esm/index.js", "require": "./lib/commonjs/index.js" } }, "files": ["lib/**/*", "src/**/*"], "engines": { "node": ">=14", "npm": ">=6.12.0" } }

exports字段按import/require分别指向 ESM 与 CJS 产物,types指向类型声明,同时保留main/module兼容旧工具链。构建脚本则通过tsc --build分别产出三种目标:tsconfig.cjs.jsontsconfig.esm.jsontsconfig.types.json,并在各自目录写入{"type": "commonjs"}/{"type": "module"}package.json以明确模块类型。

依赖方面,运行时依赖收敛为isomorphic-ws(跨 Node/浏览器环境的 WebSocket 封装)与ws,类型依赖固定为@types/ws@8.5.3,其余为 web3.js 内部包(web3-errorsweb3-typesweb3-utils)。engines声明支持 Node.js >= 14,ES 目标为 2020。

API 表面演化:命名导出与 SocketConnection getter

命名导出 WebSocketProvider(4.0.1-rc.0, #5771)

WebSocketProvider既以export default提供默认导出,也通过export { WebSocketProvider }提供命名导出(见 src/index.ts 末尾)。同时该包还转发了ClientRequestArgs(来自http)与ClientOptions(来自isomorphic-ws)两个类型,方便使用者构造 socket 选项时获得类型提示。

web3主包也对外统一导出了该 provider(packages/web3/src/index.ts),因此可以直接:

import { Web3, WebSocketProvider } from 'web3'; const web3 = new Web3(new WebSocketProvider('wss://mainnet.infura.io/ws/v3/YOUR_INFURA_ID'));

SocketConnection getter 返回 isomorphic WebSocket(4.0.1-rc.0, #5891)

SocketProvider提供了SocketConnectiongetter(socket_provider.ts),WebSocketProvider中其返回类型为 isomorphic 的WebSocket。这意味着你可以绕过 provider 抽象,直接访问底层 socket 的特殊属性,或注册自定义的服务器事件监听。单元测试也验证了这一点:

expect(wsProvider.SocketConnection).toBeInstanceOf(WebSocket);

稳定性治理:类型依赖修复与分块解析 bug

进入 4.0.2 之后,changelog 的主题转向修复与依赖治理:

  • 4.0.2(#6205):修复#6162@types/ws问题
  • 4.0.4(#6309):将@types/ws固定为8.5.3,杜绝类型包版本漂移引发的编译问题
  • 4.0.7(#6496):修复 chunks 处理逻辑中的 bug
  • 其余版本以依赖更新为主

其中 #6496 对应的正是ChunkResponseParser(chunk_response_parser.ts)的分块去重逻辑。WebSocket 消息可能被 TCP 层拆分成多个 chunk,也可能多个 JSON-RPC 响应粘合在一条消息中,解析器通过正则将相邻的 JSON 边界切分(}|--|{}]|--|[{等模式),再对每个片段尝试JSON.parse:解析失败则缓存为lastChunk,等待下一个消息拼接后继续解析,同时设置 15 秒超时,超时后若未开启自动重连则清空队列并抛出InvalidResponseError

从 changelog 到实践:WebSocketProvider 完整使用指南

安装

npm install web3-providers-ws # 或 yarn add web3-providers-ws

初始化与验证

WebSocketProvider的构造签名(src/index.ts)为:

constructor( socketPath: string, socketOptions?: ClientOptions | ClientRequestArgs, reconnectOptions?: Partial<ReconnectOptions>, )
  • socketPath:必须为ws://wss://开头的字符串,否则构造时抛出InvalidClientError。合法/非法地址的样例可见 test_data.ts(validConnectionStrings/invalidConnectionStrings)。
  • socketOptions:透传给 isomorphic-ws 的ClientOptions或 Node 的ClientRequestArgs,例如设置headers携带 API key、handshakeTimeoutperMessageDeflate等。注意源码中若传入空对象{}会被视为未传而忽略。
  • reconnectOptions:如上文所述的重连参数,可选。

官方 provider 指南 02_web3_providers_guide/index.md 给出了两种典型写法:

// 方式一:同时配置 socket 选项与重连选项 const provider = new WebSocketProvider( 'ws://localhost:8545', { headers: { // 节点服务要求 API key 放在请求头中时 'x-api-key': '<API key>', }, }, { delay: 500, autoReconnect: true, maxAttempts: 10, }, ); // 方式二:仅配置重连选项 const provider = new WebSocketProvider( 'ws://localhost:8545', {}, { delay: 500, autoReconnect: true, maxAttempts: 10, }, );

发起请求与订阅

provider 通过 EIP-1193 风格的request()发送 JSON-RPC 请求(socket_provider.ts):连接中时请求进入_sentRequestsQueue并立即发送;连接尚未建立时进入_pendingRequestsQueue,待open事件触发后统一补发(_sendPendingRequests)。同时supportsSubscriptions()恒返回true,配合 web3.js 的事件订阅体系可实现eth_subscribe等实时订阅。

单元测试 web_socket_provider.test.ts 演示了最小请求流程:

const wsProvider = new WebSocketProvider('ws://localhost:8545'); const jsonRpcPayload = { jsonrpc: '2.0', id: 42, method: 'eth_getBalance', params: ['0x407d73d8a49eeb85d32cf465507dd71d507100c1', 'latest'], }; const result = await wsProvider.request(jsonRpcPayload);

断开连接与程序退出

WebSocket 是常驻长连接,进程不会自动退出。需要显式断开:

// 直接断开(默认关闭码 1000) web3.currentProvider?.disconnect(); // 或等待请求队列清空后优雅断开 await provider.safeDisconnect();

测试与质量保障

web3-providers-ws的测试分为单元与集成两层(见 test 目录):

  • 单元测试web_socket_provider.test.ts:mockisomorphic-ws,覆盖构造合法性校验(合法/非法 URL 断言)、provider 方法集(requestgetStatussupportsSubscriptionsonconnectdisconnect等)、选项传入与request成功路径;
  • 集成测试reconnection.test.ts:针对 Node 环境,验证默认/自定义重连选项、connect/disconnect事件发射、_reconnectOptions的合并结果;geth_fault_tolerance.test.ts 则模拟节点故障场景验证容错能力。

运行方式(见 package.json scripts):

npm run test:unit npm run test:integration

结语:从 changelog 反推 4.x 的设计取舍

透过这份 changelog 可以清晰看到web3-providers-ws的演进思路:先用SocketProvider抽象统一 socket 类 provider 的公共能力并重塑事件体系,再完善发布形态(lib/目录、ESM/CJS/types 三产物 + 源文件),随后通过固定@types/ws版本、修复分块解析 bug 等举措走向稳定。对于使用者而言,理解这条脉络意味着:升级 4.x 时优先关注closedisconnect的事件迁移与构造参数行为,遇到类型问题检查@types/ws是否被固定为8.5.3,而在生产环境中应显式配置ReconnectOptions以匹配业务对连接可用性的要求。

【免费下载链接】web3.jsCollection of comprehensive TypeScript libraries for Interaction with the Ethereum JSON RPC API and utility functions.项目地址: https://gitcode.com/gh_mirrors/we/web3.js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询