本指南解释 Polkadot 的 WebSocket JSON-RPC 接口,涵盖 wss 与 ws 的区别、默认端口 9944、@polkadot/api 的 WsProvider 如何管理订阅和重连,以及如何使用 nginx 保护 WSS。包含一个可运行的订阅脚本和常见问题(如超时和“fetch failed”)的故障排除清单。
直接回答:关于 Polkadot WebSocket RPC 你需要了解什么
Polkadot 节点暴露一个 WebSocket JSON-RPC 端点(默认端口 9944),允许客户端查询链上数据,并且关键的是,订阅实时更新。安全变体是 wss://(WebSocket Secure),它使用 TLS 加密流量。对于生产环境应用,你应该始终使用 wss:// 端点,例如 OnFinality 的 Polkadot 网络页面 提供的端点。@polkadot/api 库通过其 WsProvider 抽象了 WebSocket 连接,处理订阅和重连逻辑。本指南解释其机制,提供可运行的示例,并为常见连接问题提供故障排除步骤。
如果你正在构建 dApp 或索引器,你可能会使用 @polkadot/api 订阅新区块、最终化头或存储更改。理解 WebSocket 连接在底层如何工作有助于你诊断超时或意外断开等问题。让我们深入架构。
Polkadot WebSocket RPC 如何工作:wss 与 ws 及端口 9944
Polkadot 基于 Substrate,使用 WebSocket 上的 JSON-RPC 进行所有实时交互。默认 WebSocket 端口是 9944,而 HTTP RPC 端口是 9933。ws:// 方案未加密,而 wss:// 使用 TLS 加密。对于任何生产用途,你必须使用 wss:// 以防止窃听和中间人攻击。Polkadot 开发者安全 WebSocket 指南 解释了如何设置安全的 WebSocket 代理。
当你通过 WebSocket 连接到 Polkadot 节点时,客户端发送 JSON-RPC 请求和订阅。订阅是长期存在的请求,服务器推送通知。例如,chain_subscribeNewHeads 在每次导入新区块时发送通知。WebSocket 协议包含 ping/pong 保活机制以检测死连接。如果客户端在特定超时时间内未收到 pong,它可以认为连接已死并尝试重连。
- 默认 WebSocket 端口:9944
- 安全 WebSocket:wss://(TLS 加密)
- 订阅:chain_subscribeNewHeads、chain_subscribeFinalizedHeads、state_subscribeStorage
- 保活:ping/pong 帧以维持连接
@polkadot/api WsProvider 如何管理连接和订阅
@polkadot/api 库使用 WsProvider 管理 WebSocket 连接。它处理底层 WebSocket 协议,包括重连逻辑和订阅管理。当你使用 WSS 端点创建 API 实例时,WsProvider 建立连接并在连接断开时自动重连。它使用指数退避策略,从短延迟开始,逐渐增加到最大值,以避免压垮服务器。
订阅通过订阅 ID 管理。当你调用 api.rpc.chain.subscribeNewHeads() 时,提供者发送 chain_subscribeNewHeads 请求。服务器响应一个订阅 ID,提供者将该 ID 映射到回调。如果连接断开,提供者重连并自动重新订阅所有活动订阅,使用相同的订阅 ID。这确保你的应用无需手动干预即可继续接收更新。
然而,存在已知问题。例如,一个常见问题是 WsProvider 超时未被 try-catch 捕获,如 Substrate Stack Exchange 所讨论的。这可能导致未处理的 promise 拒绝。此外,'fetch failed' 错误通常发生在 WebSocket 连接未正确建立时,通常是由于网络问题或端点 URL 错误。
可运行示例:使用 @polkadot/api 订阅新区块头
下面是一个完整的、可运行的脚本,它连接到 Polkadot WSS 端点,订阅新区块头,并记录区块号和哈希。它还演示了如何处理断开和重连。要运行它,你需要 Node.js 和 @polkadot/api 包(npm install @polkadot/api)。
该脚本使用 OnFinality 的公共 WSS 端点(如果需要,可以替换为你自己的端点)。它设置订阅并记录每个新区块头。它还监听 'connected' 和 'disconnected' 事件以显示重连行为。
// polkadot-subscribe.js
const { ApiPromise, WsProvider } = require('@polkadot/api');
const WS_URL = 'wss://polkadot.api.onfinality.io/public-ws';
async function main() {
const provider = new WsProvider(WS_URL);
const api = await ApiPromise.create({ provider });
// Log connection events
provider.on('connected', () => console.log('Connected to', WS_URL));
provider.on('disconnected', () => console.log('Disconnected from', WS_URL));
provider.on('error', (err) => console.error('Provider error:', err));
// Subscribe to new heads
const unsub = await api.rpc.chain.subscribeNewHeads((head) => {
console.log(`New block #${head.number} hash: ${head.hash}`);
});
// Keep the process alive
process.on('SIGINT', async () => {
await unsub();
await api.disconnect();
process.exit(0);
});
}
main().catch(console.error);预期输出及如何验证
当你运行脚本时,你应该看到类似以下的输出(实际区块号和哈希会有所不同):
Connected to wss://polkadot.api.onfinality.io/public-ws
New block #12345678 hash: 0x1234...abcd
New block #12345679 hash: 0x5678...ef01
...
要验证订阅是否正常工作,你可以将区块号与区块浏览器(如 Polkadot Subscan)上的最新区块进行比较。区块号应每 6 秒增加 1(Polkadot 的平均出块时间)。如果你没有看到新区块,请检查你的网络连接和端点 URL。另外,确保你的防火墙允许端口 443 上的 WebSocket 连接(对于 wss://)。
常见故障及修复:WsProvider 超时和 'fetch failed'
开发者面临的两个常见问题是 WsProvider 超时和 'fetch failed' 错误。超时发生在 WebSocket 连接已建立但服务器在特定时间内未响应时。这可能发生在节点过载或网络缓慢时。WsProvider 对连接建立有一个内置超时(默认 60 秒)。如果超过超时,它会抛出一个错误,该错误可能不会被 ApiPromise.create() 调用周围的 try-catch 捕获,如 Substrate Stack Exchange 所述。要处理此问题,你可以监听提供者的 'error' 事件。
'fetch failed' 错误通常发生在 WebSocket 握手失败时,通常是由于 URL 错误、防火墙阻止连接或 DNS 问题。此错误由 WebSocket 实现使用的底层 fetch API 抛出。要修复它,请验证端点 URL,检查服务器是否可达(例如,使用 curl 或 WebSocket 客户端),并确保你的网络允许出站 WebSocket 连接。
有关 WebSocket RPC 连接为何断开以及如何修复的深入探讨,请参阅我们的指南 为什么 WebSocket RPC 连接会断开。
- 检查端点 URL 和网络连接
- 监听提供者的 'error' 事件以捕获超时
- 使用可靠的 WSS 提供商,如 OnFinality 的 RPC Assistant
- 如果需要,实现自定义重连逻辑
使用 nginx 和 TLS 保护 WSS
如果你运行自己的 Polkadot 节点,你应该通过安全的 WebSocket 端点暴露它。Polkadot 开发者安全 WebSocket 指南 建议使用 nginx 作为反向代理并终止 TLS。这允许你将节点的原生 WebSocket 端口(9944)绑定到 localhost,并在端口 443 上暴露 wss:// 端点。
以下是一个最小的 nginx 配置,它将 WebSocket 连接代理到本地 Polkadot 节点。你需要有 TLS 证书(例如,来自 Let's Encrypt),并配置 proxy_pass 到 http://127.0.0.1:9944,并带有适当的 WebSocket 头。
server {
listen 443 ssl;
server_name rpc.example.com;
ssl_certificate /etc/letsencrypt/live/rpc.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rpc.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:9944;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
}WebSocket RPC 的权衡与限制
虽然 WebSocket RPC 很强大,但它也有局限性。订阅会消耗节点上的大量资源,尤其是当许多客户端订阅存储更改时。公共端点通常会施加速率限制以保护节点。对于高吞吐量的应用,考虑使用专用端点或像 OnFinality 的 API 服务 这样的服务,它提供可扩展的基础设施。
另一个限制是 WebSocket 连接是有状态的,这在负载均衡环境中可能有问题。如果客户端连接到一台服务器,然后负载均衡器将后续请求路由到另一台,订阅可能会中断。解决方案包括粘性会话或使用集中式 RPC 提供商,后者透明地处理此问题。
最后,重连逻辑可能导致重复通知,如果客户端重连并重新订阅。@polkadot/api 库通过使用订阅 ID 来处理此问题,但你应该注意应用程序逻辑中潜在的重复事件。
后续步骤和更多资源
既然你了解了 Polkadot WebSocket RPC,你可以自信地构建实时应用程序。要开始,请探索 OnFinality 学习中心 获取更多指南,或查看我们的 Polkadot 网络页面 获取可用端点。如果你需要可靠的 RPC 服务,请考虑我们的 定价 和 API 服务。
有关 Polkadot RPC 端点的完整列表,请使用我们的 RPC Assistant。如果你遇到连接问题,请参阅我们的指南 为什么 WebSocket RPC 连接会断开。祝你编码愉快!