---
title: "如何针对你的工作流对 RPC 提供方做基准测试"
description: "从负载下的延迟、区块滞后和数据一致性三个维度对 RPC 提供方做基准测试，分开衡量，测你的应用实际调用的方法，并在它运行的地方测。"
---

# 如何针对你的工作流对 RPC 提供方做基准测试

<ImageBlock
  src="https://media.alchemy.com/blog/how-to-benchmark-rpc-providers-hero-v3.jpg"
  alt="基于你的工作负载对提供方做基准测试"
  width={1024}
  height={480}
  priority
/>

选择 RPC 提供方，应当依据针对你自己工作负载的基准测试。我们一直在对各家提供方做基准测试，包括我们自己，也见过很多团队这样做。发送请求是容易的部分。难的是决定衡量什么，然后搭建一个你真正愿意据此做决定的测试。

所以我们把方法论写了下来，并构建了 Alchemy rpcbench 来执行它：一个单一的二进制文件，在你自己的机器上，针对你的应用实际调用的方法，对任意一组 RPC 提供方做基准测试。方法论本身也适用于你已有的任何工具。

值得衡量的有三件事：负载下的延迟、区块滞后和数据一致性。每一项会影响系统的不同部分，一家提供方可能在一项上很强、在另一项上落后，因此混合评分会掩盖你真正关心的那一项。衡量你的应用实际调用的内容，并在它运行的地方测。

## 对 RPC 提供方做基准测试时，应该衡量什么？

负载下的延迟、区块滞后和数据一致性，分别衡量、分别报告。

<EmbeddedTable
  table={{
    columns: [
      { key: "dimension", width: 160, title: "维度", dataType: "object" },
      { key: "what", width: 260, title: "衡量内容", dataType: "object" },
      { key: "how", width: 360, title: "衡量方法", dataType: "object" },
    ],
    data: [
      {
        dimension: { title: "负载下的延迟", tooltip: "", icon: "" },
        what: {
          title: "请求量上升时，提供方响应请求的速度",
          tooltip: "",
          icon: "",
        },
        how: {
          title:
            "逐级运行几档接近生产环境的 RPS 测试。在每一档读取 p95 和 p99，并同时查看失败率和 429 比率",
          tooltip: "",
          icon: "",
        },
        id: 0,
      },
      {
        dimension: { title: "区块滞后", tooltip: "", icon: "" },
        what: {
          title: "提供方交付新区块的速度",
          tooltip: "",
          icon: "",
        },
        how: {
          title:
            "同时在每家提供方上订阅 <code>newHeads</code>。记录每个区块由谁最先公布，以及领先多少",
          tooltip: "",
          icon: "",
        },
        id: 1,
      },
      {
        dimension: { title: "数据一致性", tooltip: "", icon: "" },
        what: {
          title: "区块被公布的那一刻，其数据是否已经可以提供",
          tooltip: "",
          icon: "",
        },
        how: {
          title:
            "在每个新区块头出现时，请求该区块、它的日志、收据和交易。检查它们是否一致",
          tooltip: "",
          icon: "",
        },
        id: 2,
      },
    ],
  }}
/>

数据一致性是大多数团队从不测试的一项。有些提供方用不同的后端服务不同的方法，而这些后端并不总是同步推进，因此一家提供方可能已经公布了新区块头，而同一区块的日志返回时却不完整。可以想象一座图书馆，在书上架之前就更新了目录：目录说新书已到，书架上却是空的。

## 应该先衡量哪一项？

从与你的应用如何使用数据相匹配的那一项开始。如果你根据新区块采取行动，那就是区块滞后。如果你在区块落地的那一刻读取它的日志或状态，那就是数据一致性。否则就是负载下的延迟。我们见过 Valantis、Aave、RedStone 和 Struct 等团队，各自运行衡量不同内容的基准测试。

<EmbeddedTable
  table={{
    columns: [
      {
        key: "building",
        width: 240,
        title: "如果你在构建",
        dataType: "object",
      },
      { key: "first", width: 260, title: "先衡量", dataType: "object" },
      { key: "risk", width: 280, title: "风险所在", dataType: "object" },
    ],
    data: [
      {
        building: {
          title:
            'DEX 或链上报价引擎（例如 <a href="/case-studies/how-valantis-built-a-reliable-trading-experience-on-hyperliquid">Valantis</a>）',
          tooltip: "",
          icon: "",
        },
        first: {
          title:
            "区块滞后，然后是定价逻辑所发出的 <code>eth_call</code> 的 <strong>p99</strong>",
          tooltip: "",
          icon: "",
        },
        risk: {
          title:
            "你基于一个已经过时的区块定价，或者你的报价在价差消失之后才到达",
          tooltip: "",
          icon: "",
        },
        id: 0,
      },
      {
        building: {
          title:
            '带清算功能的借贷协议（例如 <a href="/case-studies/how-aave-runs-largest-lending-protocol-alchemy">Aave</a>）',
          tooltip: "",
          icon: "",
        },
        first: {
          title:
            "区块滞后和数据一致性，然后是健康因子和价格读取的 <strong>p99</strong>",
          tooltip: "",
          icon: "",
        },
        risk: {
          title:
            "清算晚了一个区块才触发，或者在该区块数据可提供之前就读取了仓位",
          tooltip: "",
          icon: "",
        },
        id: 1,
      },
      {
        building: {
          title:
            '预言机或事件驱动的服务（例如 <a href="/case-studies/how-redstone-keeps-price-feeds-reliable-across-51-chains">RedStone</a>）',
          tooltip: "",
          icon: "",
        },
        first: {
          title:
            "区块滞后和数据一致性，然后是 <code>eth_getLogs</code> 和 <code>eth_call</code> 的 <strong>p99</strong>",
          tooltip: "",
          icon: "",
        },
        risk: {
          title: "由于日志返回不完整，你错过了触发条件",
          tooltip: "",
          icon: "",
        },
        id: 2,
      },
      {
        building: {
          title:
            '索引器或钱包后端（例如 <a href="/case-studies/how-struct-is-building-the-data-layer-for-onchain-finance">Struct</a>）',
          tooltip: "",
          icon: "",
        },
        first: {
          title:
            "峰值速率下的 <strong>p95</strong>，并同时查看失败率和 429 比率",
          tooltip: "",
          icon: "",
        },
        risk: {
          title: "回填停滞，或者在用户交易时余额变得陈旧",
          tooltip: "",
          icon: "",
        },
        id: 3,
      },
    ],
  }}
/>

然后逐个方法比较提供方，因为平均更快的提供方，在你的应用调用最多的那一个方法上仍可能更慢。这个差距就是关键所在：价差消失后才到达的报价不是慢报价，而是错失的成交。

## 如何运行一个可信的 RPC 基准测试？

按固定的时间表发送请求来衡量延迟。显而易见的设计是发送一个请求、等待响应、再发送下一个，而这恰恰会在提供方开始吃力时悄悄减少请求。你的测试在本应保持稳定的时刻放缓了，于是你想找的那次变慢永远不会出现在数据里。改为按固定时钟发送，并记录每次发送相对时钟落后了多少。当你自己的机器跟不上时，数字描述的就是你的机器。

### 准备运行

<EmbeddedTable
  table={{
    columns: [
      { key: "rule", width: 300, title: "规则", dataType: "object" },
      { key: "why", width: 480, title: "为什么重要", dataType: "object" },
    ],
    data: [
      {
        rule: {
          title: "测试你的应用实际调用的方法，并对所有提供方使用相同的负载",
          tooltip: "",
          icon: "",
        },
        why: {
          title:
            "基准测试只能比较你发送的内容。每个方法以全速率单独运行，否则开销大的方法永远积累不出稳定的尾部",
          tooltip: "",
          icon: "",
        },
        id: 0,
      },
      {
        rule: {
          title: "使用接近生产环境的 RPS 速率",
          tooltip: "",
          icon: "",
        },
        why: {
          title:
            "在 50 RPS 下看起来一样的提供方，到 300 RPS 时可能拉开差距。还要关注带宽，因为大范围的 <code>eth_getLogs</code> 会占满你的链路，看起来就像性能下降",
          tooltip: "",
          icon: "",
        },
        id: 1,
      },
      {
        rule: { title: "确定什么算成功", tooltip: "", icon: "" },
        why: {
          title:
            "延迟只统计成功的请求，因此快速失败不会得分。衡量到最后一个字节的时间。把 429 单独归为一类",
          tooltip: "",
          icon: "",
        },
        id: 2,
      },
    ],
  }}
/>

### 执行运行

<EmbeddedTable
  table={{
    columns: [
      { key: "rule", width: 300, title: "规则", dataType: "object" },
      { key: "why", width: 480, title: "为什么重要", dataType: "object" },
    ],
    data: [
      {
        rule: {
          title: "以相同方式预热每家提供方",
          tooltip: "",
          icon: "",
        },
        why: {
          title:
            "在每个方法开始前，对每家提供方以全速率发送十秒请求并丢弃结果。按时间计，绝不按请求数计：500 RPS 下 10 个请求只有 20ms 的预热",
          tooltip: "",
          icon: "",
        },
        id: 0,
      },
      {
        rule: {
          title: "保持时间表，关注你自己的机器",
          tooltip: "",
          icon: "",
        },
        why: {
          title:
            "如果在 1,000 个样本上发送偏差的 p99 超过 5ms，就标记这次运行。到那时你衡量的是你的机器",
          tooltip: "",
          icon: "",
        },
        id: 1,
      },
      {
        rule: {
          title: "一次测试一家提供方",
          tooltip: "",
          icon: "",
        },
        why: {
          title:
            "同时运行会让它们争用你的带宽，于是谁赢了争用谁就显得更快。让每家轮流单独测试，并在每次运行之间轮换顺序",
          tooltip: "",
          icon: "",
        },
        id: 2,
      },
    ],
  }}
/>

区块滞后是例外。“谁最先公布了这个区块”只有在同一时刻观察所有提供方时才有意义，而且每个区块只是一条很小的消息，因此不会有谁争用你的带宽。

### 解读结果

<EmbeddedTable
  table={{
    columns: [
      { key: "rule", width: 300, title: "规则", dataType: "object" },
      { key: "why", width: 480, title: "为什么重要", dataType: "object" },
    ],
    data: [
      {
        rule: {
          title:
            "衡量两家提供方在相同请求上的差距。如果这个差距有可能为零，就判为平局",
          tooltip: "",
          icon: "",
        },
        why: {
          title:
            "落在彼此置信区间内的提供方，你的数据并不能把它们区分开。成对样本少于 100 个时，根本不要下结论",
          tooltip: "",
          icon: "",
        },
        id: 0,
      },
      {
        rule: {
          title: "运行两次，在你的系统运行的地方运行",
          tooltip: "",
          icon: "",
        },
        why: {
          title:
            "延迟取决于你与提供方之间的网络路径。运行两次可以显示性能在不同网络条件下的表现",
          tooltip: "",
          icon: "",
        },
        id: 1,
      },
    ],
  }}
/>

## 什么是 Alchemy rpcbench？

Alchemy rpcbench 是一个单一的二进制文件，在你的机器上、使用你自己的密钥，运行上述完整的方法论。它只向你配置中的端点发送请求，生成的 HTML 报告直接交给你。

我们构建它，是因为把方法论写下来只是容易的那一半。上面的每条规则都需要好几天才能正确实现，而且大多数一旦做错是看不出来的，这对测量工具来说是最糟的组合。

默认配置只是一个起点。`workload:` 部分可以换成你自己的参数和方法：

<CodeSnippet
  language="yaml"
  code={`workload:
  - method: eth_call
    params:
      - to: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"  # a contract on your quote path
        data: "0x18160ddd"                                 # and your calldata
      - "$anchor"                                          # the pinned block every provider is asked about`}
/>

速率档位、持续时间、超时和区块滞后窗口同样可以配置，而且在任何负载离开你的机器之前，预检步骤会按请求数、时间和带宽估算这次运行的开销。这个估算以 Alchemy 计算单元（compute units）计，所以把它当作规模检查来看。

每次运行都会写出一个 JSON 文件、CSV 汇总和一个独立的 HTML 仪表盘。它不会给出它无法担保的数字。样本不足、提供方被限流，或者机器跟不上，都会在顶部被标记出来。

通过检查的运行会按以下顺序给你四样东西：

<EmbeddedTable
  table={{
    columns: [
      { key: "get", width: 240, title: "你得到的内容", dataType: "object" },
      { key: "tells", width: 540, title: "它告诉你什么", dataType: "object" },
    ],
    data: [
      {
        get: { title: "有效性结论", tooltip: "", icon: "" },
        tells: {
          title: "这次运行是否值得引用。先读这一项，再看其他内容",
          tooltip: "",
          icon: "",
        },
        id: 0,
      },
      {
        get: { title: "比较结果", tooltip: "", icon: "" },
        tells: {
          title: "只有大到足以超出噪声的差距。区间重叠的会判为平局",
          tooltip: "",
          icon: "",
        },
        id: 1,
      },
      {
        get: { title: "按方法的延迟", tooltip: "", icon: "" },
        tells: {
          title:
            "每个速率档位下的 p50、p90、p95 和 p99，并同时列出成功率、失败率和 429 比率",
          tooltip: "",
          icon: "",
        },
        id: 2,
      },
      {
        get: { title: "区块滞后与一致性", tooltip: "", icon: "" },
        tells: {
          title:
            "每个区块由谁最先公布、其他提供方落后多少、遗漏的区块，以及每个区块头的数据是否可以提供",
          tooltip: "",
          icon: "",
        },
        id: 3,
      },
    ],
  }}
/>

## 这套方法论不能告诉你什么

- 一个区域的结果不代表另一个区域。如果你的系统运行在别处，就从那里运行。
- 区块滞后是相对于你测试过的最快提供方而言的，而不是相对于真正的链顶端。
- 方法是逐个运行的，因此一次运行永远不是你线上流量组合的重放。
- 一次运行只是一个数据点。我们强烈建议多次运行，以获得关于性能的可靠信号，因为许多因素都可能影响读数，包括全链范围的问题。
- Alchemy rpcbench 工具仅适用于 EVM 网络，每次运行支持一个网络。

## 如何获取 Alchemy rpcbench？

Alchemy rpcbench 目前处于私有 alpha 阶段。[填写此表单](/rpcbench-access)申请访问！

如果你更愿意用已有的工具自己搭建，上面的方法论就是全部内容。两种方式我们都乐见，只要你最终据以行动的数字是你自己得出的。

## 常见问题

### 我应该如何比较 RPC 提供方？

在你的系统运行的区域，针对它实际调用的方法，对每家提供方运行相同的工作负载。按固定时间表发送，一次测试一家提供方，并固定一个区块，让每家回答同一个问题。逐个方法解读结果，每个延迟数字旁都要列出失败和限流计数，然后在不同时间重复。如果你不想自己搭建，可以[联系我们](/rpcbench-access)获取 Alchemy rpcbench 的访问权限。

### RPC 延迟、区块滞后和数据一致性有什么区别？

延迟是提供方响应你发送的请求所需的时间。区块滞后是它告诉你有新区块所需的时间。数据一致性是在它这样告诉你的那一刻，该区块的日志、收据和交易是否已经可以提供。一家提供方可能三项都强，也可能一项强、另一项落后，这就是它们应该分列的原因。

### 由提供方构建的基准测试工具，结果可信吗？

可信，而且你不必只听我们的说法。Alchemy rpcbench 中没有任何代码路径会根据所连接的提供方走不同分支，因此针对特定提供方的超时或重试无处隐藏。用 README 中的审计提示词，让任意编程代理检查你克隆的代码，它会替你核查。该工具运行在你的机器上、使用你的密钥，结果永远不会离开你的输出文件夹。

### 我应该根据单一的综合性能评分来选择 RPC 提供方吗？

不应该。综合评分替你选定了权重，而正确的权重取决于你的应用。回填历史数据的索引器和抢区块的套利机器人，对提供方的需求几乎没有共同之处。读取与你所构建内容相匹配的那个维度，然后逐个方法地读。

### 应该从哪里运行 RPC 基准测试？

从你的应用运行的区域。RPC 延迟在很大程度上取决于客户端与提供方之间的网络路径，因此从笔记本电脑或从你不部署的云区域运行的基准测试，衡量的是一条你的生产流量永远不会经过的路径。

### 运行一次 RPC 基准测试需要多长时间？

运行时间取决于你如何配置测试，但默认配置大约需要两个小时。你可以在 10 分钟内完成配置并开始运行测试。Alchemy rpcbench 自带的配置在 Ethereum 上对每家提供方发送 280,000 个请求，覆盖七个方法和三个速率档位，大约需要 48 分钟，区块滞后窗口再加一个小时。提供方轮流测试，因此延迟阶段会随测试的提供方数量增加而变长。预留一个下午，因为置信度需要样本。

### 对 RPC 提供方做基准测试需要 WebSocket 端点吗？

测延迟不需要。测区块滞后和数据一致性需要，因为两者都依赖 `eth_subscribe` 和对 `newHeads` 的订阅。测区块滞后还至少需要两家提供方，因为它衡量的是谁最先公布每个区块。

### 我可以测试哪些 RPC 提供方？

任何在你所测试的链上提供 EVM JSON-RPC 端点的提供方。URL 和密钥由你提供，方法和工具中没有任何部分取决于提供方是谁。

### Alchemy 会发布自己的 RPC 基准测试吗？

会。我们的[基准测试页面](/benchmarks)持续发布提供方对比，现在就可以阅读。Alchemy rpcbench 则用于在你的应用所在的区域、针对你的应用工作负载，得出你自己的结果。
