本文档介绍了 OpenZeppelin Monitor 中的 RPC 客户端实现,它具有自动端点轮换和回退功能,以确保可靠的区块链监控。重点介绍了 RPC 客户端的配置方法,包括 RPC URL 的配置,以及端点管理、轮换策略和重试策略。同时,还列出了 EVM 和 Stellar 网络中,监控器在每次 cron 迭代中发出的 RPC 调用列表,并提供了最佳实践和故障排除建议。
OpenZeppelin Monitor 包括一个强大的 RPC 客户端实现,具有自动端点轮换和回退功能。即使单个 RPC 端点出现问题,这也能确保可靠的区块链监控。
支持多个 RPC 端点,具有加权负载均衡
端点故障时自动回退
速率限制处理(429 响应)
连接健康检查
线程安全的端点轮换
RPC 端点在网络配置文件中配置,并带有负载均衡权重:
{
"rpc_urls": [\
{\
"type_": "rpc",\
"url": {"type": "plain", "value": "https://primary-endpoint.example.com"},\
"weight": 100\
},\
{\
"type_": "rpc",\
"url": {"type": "plain", "value": "https://backup-endpoint.example.com"},\
"weight": 50\
}\
]
}
对于高可用性设置,请配置至少 3 个具有适当权重的(私有)RPC 端点,以确保即使多个端点发生故障也能持续运行。 |
字段 | 类型 | 描述 |
---|---|---|
type_ |
String |
端点类型(目前仅支持 “rpc”) |
url.type |
String |
密钥类型(“Plain”,“Environment” 或 “HashicorpCloudVault”) |
url.value |
String |
RPC 端点 URL |
weight |
Number |
负载均衡权重(0-100) |
端点管理器处理
基于权重的初始端点选择
故障时自动轮换
连接健康检查
线程安全的端点更新
每个区块链网络类型都有其自己专门的传输客户端,该客户端包装了基本的 HttpTransportClient
。
传输客户端的实现方式如下:
核心 HTTP 传输:HttpTransportClient
提供核心 HTTP 功能,包括集成的可重试客户端。
特定于网络的传输:
EVMTransportClient
用于 EVM 网络
StellarTransportClient
用于 Stellar 网络
RPC 客户端包含一个自动轮换策略,用于处理特定类型的故障:
对于 429(请求过多)响应:
立即轮换到备用 URL
使用新端点重试请求
继续此过程,直到成功或所有端点都已耗尽
触发 RPC 端点轮换的错误代码可以在 src/services/blockchain/transports/mod.rs
文件中自定义。
pub const ROTATE_ON_ERROR_CODES: [u16; 1] = [429];
传输层结合使用相同端点重试和端点轮换,以处理瞬时故障并保持服务可用性。
reqwest-retry
)HttpTransportClient
(以及扩展的 EVM 和 Stellar 客户端)使用 reqwest_middleware::ClientWithMiddleware
。此客户端在初始化期间使用 utils::http::create_retryable_http_client
工具进行配置。此实用程序在共享的基础 reqwest::Client
之上分层 reqwest_retry::RetryTransientMiddleware
。
此中间件处理:
对 当前活动的 RPC URL 发出的请求自动重试瞬时 HTTP 错误(例如,5xx 服务器错误、网络超时)。
这些重试尝试之间的指数退避策略。
诸如重试次数、退避持续时间和抖动之类的参数在 HttpRetryConfig
结构中定义(请参阅配置选项)。
这种相同的端点重试机制是独立的,并且在端点轮换逻辑之抢跑。如果当前 URL 的所有相同端点重试失败,则该错误将由 EndpointManager
处理。
EndpointManager
)如果当前活动的 RPC URL 的所有相同端点重试都失败,或者如果收到某些 HTTP 状态代码(例如,429 请求过多,如 ROTATE_ON_ERROR_CODES
中所定义),则 EndpointManager
(由 HttpTransportClient
使用)将尝试轮换到健康的备用 URL。这样可以确保如果一个端点变得持续不可用,系统可以切换到替代端点。备用 URL 的健康检查也受益于相同的端点重试机制。
相同的端点重试行为通过 HttpRetryConfig
结构配置,该结构由 create_retryable_http_client
用于为 reqwest-retry
设置 ExponentialBackoff
策略。
HttpRetryConfig
的默认设置导致 ExponentialBackoff
策略大致等效于:
// 这说明了 HttpRetryConfig::default() 和
// create_retryable_http_client 创建的默认策略。
let http_retry_config = HttpRetryConfig::default();
let retry_policy = ExponentialBackoff::builder()
.base(http_retry_config.base_for_backoff)
.retry_bounds(http_retry_config.initial_backoff, http_retry_config.max_backoff)
.jitter(http_retry_config.jitter)
.build_with_max_retries(http_retry_config.max_retries);
可配置的选项在 HttpRetryConfig
结构中定义:
// 在 utils::http 中
pub struct HttpRetryConfig {
/// 瞬时错误的最大重试次数(在初始尝试之后)。
pub max_retries: u32,
/// 第一次重试之前的初始退避持续时间。
pub initial_backoff: Duration,
/// 重试的最大退避持续时间。
pub max_backoff: Duration,
/// 指数退避计算的基数(例如,2)。
pub base_for_backoff: u64,
/// 要应用于退避持续时间的抖动。
pub jitter: Jitter,
}
客户端架构确保了高效的资源使用和一致的重试行为:
单个基本 reqwest::Client
由 HttpTransportClient
创建,具有优化的连接池设置。此基本客户端是共享的。
create_retryable_http_client
工具使用此基本客户端和一个 HttpRetryConfig
来生成一个 ClientWithMiddleware
。
然后,此 ClientWithMiddleware
(“可重试客户端”)用于 HttpTransportClient
中的所有 HTTP 操作,包括初始健康检查、通过 EndpointManager
发送的请求以及轮换期间的 try_connect
调用。这样可以确保所有操作都受益于配置的重试策略和共享的连接池。
每个传输客户端都可以定义自己的重试策略:
// src/services/transports/http.rs
pub struct HttpTransportClient {
pub client: ClientWithMiddleware,
endpoint_manager: EndpointManager,
test_connection_payload: Option<String>,
}
// 具有重试机制的客户端创建示例
// 使用默认重试策略
let http_retry_config = HttpRetryConfig::default();
// 创建基本 HTTP 客户端
let base_http_client = reqwest::ClientBuilder::new()
.pool_idle_timeout(Duration::from_secs(90))
.pool_max_idle_per_host(32)
.timeout(Duration::from_secs(30))
.connect_timeout(Duration::from_secs(20))
.build()
.context("Failed to create base HTTP client")?;
// 使用基本客户端和重试策略创建一个可重试的 HTTP 客户端
let retryable_client = create_retryable_http_client(
&http_retry_config,
base_http_client,
Some(TransientErrorRetryStrategy), // 使用自定义或默认的重试策略
);
EndpointManager
使用 HttpTransportClient
提供的启用重试的 ClientWithMiddleware
来尝试连接到主 URL。如果这些尝试(包括内部 reqwest-retry
重试)最终失败,并出现需要轮换的错误(例如,429 状态代码或持久性网络错误),则 EndpointManager
将启动 URL 轮换序列。
Fallback RPCPrimary RPCClientWithMiddleware (reqwest-retry)EndpointManagerHttpTransportClientUser/ApplicationFallback RPCPrimary RPCClientWithMiddleware (reqwest-retry)EndpointManagerHttpTransportClientUser/ApplicationRetryClient 在内部处理相同端点的重试(例如,对于 5xx)alt[备用健康检查成功][备用健康检查失败]alt[RPC_Primary 上的重试成功][RPC_Primary 上的所有重试失败(例如,网络错误或 429)]send_raw_request()send_raw_request(self, ...)POST to RPC_Primary成功成功成功响应最终错误(例如 429 或网络错误)来自 RPC_Primary 的最终错误决定轮换(基于错误类型)try_connect(Fallback_URL) (HTC 为此使用其 RetryClient)POST to RPC_Fallback (健康检查)成功 (健康检查)成功 (健康检查)成功 (健康检查)将活动 URL 更新为 RPC_FallbackPOST to RPC_Fallback (实际请求)成功成功成功响应Error (健康检查)Error (健康检查)Error (健康检查)最终错误(所有 URL 失败)错误响应
以下是 monitor 为每种网络类型对 cron 计划的每次迭代进行的 RPC 调用列表。 随着正在处理的区块数量的增加,RPC 调用的数量也会增加,如果管理不当,可能会导致速率限制问题或成本增加。
Stellar 网络调用
EVM 网络调用
常见操作
net_version
eth_blockNumber
eth_getBlockByNumber
eth_getLogs
仅在需要时
eth_getTransactionReceipt
getNetwork
getLatestLedger
getLedgers
对于每个没有 ABI 的受监控合约
getLedgerEntries
getLedgerEntries
分批处理,每次 200 个
getTransactions
getEvents
网络初始化
分批处理,每次 200 个区块
处理区块
获取合约规范
获取 WASM 哈希
获取 WASM 代码
获取区块数据
获取交易
获取事件
完成
网络初始化
对于范围内的每个区块
处理区块
获取区块日志
获取交易回执
完成
处理新区块
主
EVM
RPC 客户端初始化(每个活动网络):net_version
获取最新的区块号(每次 cron 迭代):eth_blockNumber
获取区块数据(每个区块):eth_getBlockByNumber
获取区块日志(每个区块):eth_getLogs
获取交易回执(仅在需要时):
当 monitor 条件需要特定于回执的字段(例如,gas_used
)时
监控交易状态且不存在用于验证状态的日志时
Stellar
RPC 客户端初始化(每个活动网络):getNetwork
获取最新的账本(每次 cron 迭代):getLatestLedger
获取账本数据(在单个请求中最多批处理 200 个):getLedgers
在区块过滤期间,对于配置中没有 ABI 的每个受监控合约:
获取合约实例数据:getLedgerEntries
获取合约 WASM 代码:getLedgerEntries
获取交易(在单个请求中最多批处理 200 个):getTransactions
获取事件(在单个请求中最多批处理 200 个):getEvents
配置多个具有适当权重的私有端点
尽可能使用地理位置分散的端点
监控端点健康状况并根据需要调整权重
根据网络特性设置适当的重试策略
429 请求过多:增加备用 URL 的数量、调整权重或降低监控频率
连接超时:检查端点健康状况和网络连接
无效响应:验证端点与你的网络类型是否兼容
启用调试日志以获取详细的传输信息:
RUST_LOG=debug
这将显示:
端点轮换
连接尝试
请求/响应详细信息
- 原文链接: docs.openzeppelin.com/mo...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!