---
title: "我们如何为 RPC 性能设定基准"
description: "基准数据容易被引用，也容易被误读。以下是 Alchemy 的公开 EVM 读取基准在受控条件下测量延迟、成功率、失败和超时的具体方法。"
---

# 我们如何为 RPC 性能设定基准

<ImageBlock
  src="https://media.alchemy.com/blog/how-we-benchmark-rpc-performance-hero-v2.png"
  alt="基准测试方法论首图"
  width={1024}
  height={480}
  priority
/>

[基准测试数据](https://www.alchemy.com/benchmarks)容易被引用，也容易被误读。[RPC 性能](https://www.alchemy.com/overviews/rpc-node)尤其如此。一张延迟图表可能掩盖了几个不同的问题：请求从哪里发出、运行的是哪个方法、发送了什么样的负载、什么算作成功、失败是如何处理的，以及测试衡量的是单次 RPC 调用还是完整的应用工作流。

我们构建了[公开的 EVM 读取基准测试](https://www.alchemy.com/benchmarks)，目的是让这些细节变得可见。其目标是在一套受控且可重复的测试环境下，展示各服务商之间的对比情况。

我们的基准测试回答的问题是：在选定的链、地区和时间窗口内，各服务商在常见 EVM JSON-RPC 读取方法上的表现如何对比。

这些是方法级别的基础设施基准测试：针对每个方法、链、服务商和地区，分别运行一套受控的测试方法。它们衡量的是单次 RPC 请求，而不是完整的应用工作流，例如加载钱包资产组合、准备一笔兑换交易，或跨多次调用轮询交易状态。这一区别很重要：方法级别的结果有助于开发者比较服务商行为，但并不能完整代表任何生产环境应用。

## Alchemy 的公开 EVM 读取基准测试是什么？

Alchemy 的[公开 EVM 读取基准测试](https://www.alchemy.com/benchmarks)是一项受控测试，用于衡量 RPC 服务商对配置好的 EVM JSON-RPC 读取请求的响应情况。它记录成功响应的延迟（平均值、p50 和 p95）、成功率和失败请求数，并按服务商、链、方法、地区和时间窗口进行分组。

我们只根据成功的响应计算延迟。失败的尝试和超时会单独记录，通过成功率和失败请求数来体现。这样可以让基准测试同时关注速度和可靠性，而不是孤立地看待其中一个指标。

## 定义与测量规则

以下定义、设置和规则是本文其余部分的基础。下面各小节会对每一行进行展开说明。

<EmbeddedTable
  table={{
    columns: [
      {
        key: "concept",
        width: 260,
        title: "Concept or rule",
        dataType: "object",
      },
      {
        key: "meaning",
        width: 680,
        title: "What it means",
        dataType: "object",
      },
    ],
    data: [
      {
        concept: { title: "What the benchmark measures", tooltip: "", icon: "" },
        meaning: {
          title:
            "Successful-response latency (average, p50, p95), success rate, and failed-request counts for configured EVM JSON-RPC read requests. Results are grouped by provider, chain, method, region, and time window.",
          tooltip: "",
          icon: "",
        },
        id: 0,
      },
      {
        concept: { title: "JSON-RPC", tooltip: "", icon: "" },
        meaning: {
          title:
            'The standard message format apps use to ask blockchain nodes for data. In plain English, the app sends a structured request like "get this wallet balance" or "return this transaction receipt," and the node sends back a structured answer.',
          tooltip: "",
          icon: "",
        },
        id: 1,
      },
      {
        concept: { title: "Method-level benchmark", tooltip: "", icon: "" },
        meaning: {
          title:
            "A test of one configured RPC request with one payload, such as eth_getBalance, eth_getBlockByNumber, eth_getLogs, or eth_getTransactionReceipt.",
          tooltip: "",
          icon: "",
        },
        id: 2,
      },
      {
        concept: { title: "What is not measured", tooltip: "", icon: "" },
        meaning: {
          title:
            "Full app workflows, write transactions, customer-specific traffic mixes, first-request connection setup, block freshness, every chain, every provider, and every possible payload shape.",
          tooltip: "",
          icon: "",
        },
        id: 3,
      },
      {
        concept: { title: "Request origin", tooltip: "", icon: "" },
        meaning: {
          title:
            "The benchmark runs from controlled AWS ECS regions: US East, US West, EU Central, and AP Southeast. This describes benchmark traffic only, not all Alchemy production traffic.",
          tooltip: "",
          icon: "",
        },
        id: 4,
      },
      {
        concept: { title: "Test frequency", tooltip: "", icon: "" },
        meaning: {
          title: "Common public EVM read methods run approximately every 10 seconds.",
          tooltip: "",
          icon: "",
        },
        id: 5,
      },
      {
        concept: { title: "Success criteria", tooltip: "", icon: "" },
        meaning: {
          title:
            "A request succeeds only if it returns before the 8-second timeout, returns HTTP 2xx, parses as JSON, and does not contain a JSON-RPC error object.",
          tooltip: "",
          icon: "",
        },
        id: 6,
      },
      {
        concept: { title: "Latency calculation", tooltip: "", icon: "" },
        meaning: {
          title:
            "Latency is measured only for successful responses. It is runner-side wall-clock time for the measured JSON-RPC POST request and response over a warmed, reused HTTP connection.",
          tooltip: "",
          icon: "",
        },
        id: 7,
      },
      {
        concept: { title: "Timeout handling", tooltip: "", icon: "" },
        meaning: {
          title:
            "Timeouts count as failures. They are not inserted into latency calculations as 8,000 ms responses.",
          tooltip: "",
          icon: "",
        },
        id: 8,
      },
      {
        concept: { title: "Retries", tooltip: "", icon: "" },
        meaning: {
          title:
            "None. The benchmark makes one attempt. If that attempt fails, the failure is counted.",
          tooltip: "",
          icon: "",
        },
        id: 9,
      },
      {
        concept: { title: "Latency with success rate", tooltip: "", icon: "" },
        meaning: {
          title:
            "Latency shows how quickly valid responses return. Success rate shows how often they return successfully at all.",
          tooltip: "",
          icon: "",
        },
        id: 10,
      },
    ],
  }}
/>

## 基准测试覆盖了哪些内容？

基准测试覆盖 Ethereum、Optimism、Arbitrum、Base 和 World Chain 主网，公开的服务商集合包括 Alchemy、dRPC、Infura 和 QuickNode。我们选择这些是为了覆盖被广泛使用的 EVM 主网，以及开发者最常评估的 RPC 服务商，并保留了随时间扩展这一集合的空间。对于给定的方法，每个服务商收到的都是同样配置好的 JSON-RPC 请求。

我们在受控的 AWS ECS 地区运行每项测试：US East、US West、EU Central 和 AP Southeast。在同一地区内，每个服务商都从同一运行位置进行测试，因为网络距离会影响延迟。如果我们从弗吉尼亚测试一个服务商，从新加坡测试另一个，结果反映的更多是地理因素而非服务商本身的性能。

每个结果按以下维度分组：

- 服务商
- 链
- 方法
- 地区
- 滚动时间窗口

## 基准测试向每个服务商发送什么内容？

方法是被调用的操作，例如 `eth_getBalance` 或 `eth_getLogs`。负载是我们发送以触发该方法的完整请求，包括其参数。这一区别很重要，因为性能取决于两者：一次开销很小的查询和一次大范围扫描给服务商带来的负载截然不同，即使同一个方法，也会随着输入变化而变化。

对于给定的方法，每个服务商在标准付费账户下收到的都是同样配置好的 JSON-RPC 负载。这就是公平性规则：相同的方法、相同的参数、相同的链、相同的地区、相同的超时时间、相同的成功判定标准，并且对任何服务商都不给予特殊对待。

目前，这意味着在大多数链上有七项配置好的测试，在 Ethereum 上有八项：

- `eth_getBalance`：一次账户余额查询
- 在 `earliest` 区块的 `eth_getBlockByNumber`：链起始处的区块头读取
- 在 `latest` 区块的 `eth_getBlockByNumber`：链头部的区块头读取
- `eth_getLogs`，覆盖 1 个区块的范围
- `eth_getLogs`，覆盖 10 个区块的范围
- `eth_getLogs`，覆盖 100 个区块的范围
- `eth_getLogs`，覆盖 1,000 个区块的范围（仅限 Ethereum）
- `eth_getTransactionReceipt`：一次单笔交易回执查询

一些负载会随链变化。例如使用 `latest` 区块标签的调用，会随着链的推进而变化。另一些则使用固定的历史输入：Ethereum 的 `eth_getLogs` 测试使用了针对 LINK 代币合约配置好的历史区块范围。

这种组合是有意为之：

- 固定输入让测试的部分内容可重复。
- 变动输入让测试的部分内容反映实时链状态。
- 固定的历史输入可能更容易被服务商缓存。

RPC 性能会因方法、参数、区块范围、响应大小、缓存行为和链状态而有很大差异。在解读基准测试结果时，请关注与你自身工作负载最接近的方法和参数。

## 性能是如何衡量的？

我们通过两个信号来衡量性能，每个信号回答不同的问题：延迟，即成功响应返回的速度（以平均值、p50 和 p95 报告）；成功率，即请求成功的频率（背后有失败请求数支撑）。

两者相关但不同：一个快速成功的响应，并不等同于一个可靠的响应。我们将两者并列呈现，以免一方掩盖另一方。

## 延迟是如何衡量的？

延迟是运行端对一次成功的 JSON-RPC HTTP POST 请求所测得的请求与响应时间。计时器在运行端发送请求时启动，在收到响应时停止。如果一个服务商在 120 毫秒内返回了有效响应，那么这次请求的延迟就是 120 毫秒。

我们使用预热且复用的 HTTP 连接。简单来说，我们不会计入建立新连接所需的初始步骤，例如 DNS 查找、TCP 连接建立或 TLS 握手。运行端会先预热连接，然后在一个池化的保持活动连接上测量 POST 请求本身。

这使得延迟数据更接近重复的应用流量场景，因为客户端通常会复用连接。这也意味着不应将这些数字理解为冷启动首次请求的延迟。

只有成功的响应才会计入延迟分布。如果请求超时、返回 HTTP 错误、返回 JSON-RPC 错误对象、解析失败或遇到网络错误，我们会将其计为一次失败的尝试。

超时不会被当作耗时 8,000 毫秒的响应处理。我们将超时排除在延迟之外，计入成功率，这样可以让延迟专注于响应速度，成功率专注于可靠性。

## 什么是成功率？

成功率是指在基准测试规则下成功返回的请求所占的比例：成功请求数除以尝试请求总数。

只有满足以下所有条件，一次请求才算成功：

1. 在基准测试超时时间内完成。
2. 返回 HTTP 2xx 状态。
3. 响应体可以解析为 JSON。
4. JSON-RPC 响应中不包含非空的 `error` 对象。

我们不会通过重试来掩盖失败的尝试。每次尝试的请求只有一次机会。如果失败，就计为一次失败。

## 基准测试如何处理超时？

基准测试运行端最多等待 8 秒，以获得每个服务商返回的有效响应。如果在这段时间内没有返回有效响应，我们会将这次尝试记录为超时失败。

8 秒的截止时间是我们测试设置的一部分。它并非由链本身设定，也不是服务商内部可能使用的任何超时时间。

目前，8 秒的截止时间适用于所有公开的 EVM 读取方法。某个特定方法本可以配置不同的超时时间，但目前没有方法这样做。

## 什么算作失败请求？

我们将失败的尝试与延迟分开跟踪。如果一次请求出现以下情况，就计为失败：

- 返回非 2xx 的 HTTP 状态，例如 403、429、500、502、503 或 504
- 返回 JSON-RPC 错误对象
- 在 8 秒后超时
- 在网络层面失败，或
- 无法解析为有效的 JSON

限流响应计为失败，我们也绝不会通过重试把失败的尝试变成成功的尝试。每一次失败都会降低该服务商的成功率，并计入该方法、链、地区和时间窗口下的失败请求数。

## 这些指标是如何定义的？

<EmbeddedTable
  table={{
    columns: [
      { key: "metric", width: 200, title: "Metric", dataType: "object" },
      {
        key: "technicalDefinition",
        width: 320,
        title: "Technical definition",
        dataType: "object",
      },
      {
        key: "plainEnglishMeaning",
        width: 280,
        title: "Plain-English meaning",
        dataType: "object",
      },
      {
        key: "importantCaveat",
        width: 280,
        title: "Important caveat",
        dataType: "object",
      },
    ],
    data: [
      {
        metric: { title: "Average latency", tooltip: "", icon: "" },
        technicalDefinition: {
          title:
            "Sum of successful response times divided by count of successful responses.",
          tooltip: "",
          icon: "",
        },
        plainEnglishMeaning: {
          title: "On successful calls, how long did the provider take on average?",
          tooltip: "",
          icon: "",
        },
        importantCaveat: {
          title: "Excludes failures, so it must be read with success rate.",
          tooltip: "",
          icon: "",
        },
        id: 0,
      },
      {
        metric: { title: "p50 latency", tooltip: "", icon: "" },
        technicalDefinition: {
          title:
            "Median latency across successful responses in the same provider, chain, method, region, and window group.",
          tooltip: "",
          icon: "",
        },
        plainEnglishMeaning: {
          title: "The typical successful response time.",
          tooltip: "",
          icon: "",
        },
        importantCaveat: {
          title: "Can hide slower tail behavior.",
          tooltip: "",
          icon: "",
        },
        id: 1,
      },
      {
        metric: { title: "p95 latency", tooltip: "", icon: "" },
        technicalDefinition: {
          title:
            "The latency point where 95% of successful responses were faster and 5% were slower.",
          tooltip: "",
          icon: "",
        },
        plainEnglishMeaning: {
          title: "How slow successful requests get outside the normal case.",
          tooltip: "",
          icon: "",
        },
        importantCaveat: {
          title:
            "Timeouts are not included as 8-second samples; they count as failures.",
          tooltip: "",
          icon: "",
        },
        id: 2,
      },
      {
        metric: { title: "Success rate", tooltip: "", icon: "" },
        technicalDefinition: {
          title: "Successful requests divided by total attempted requests.",
          tooltip: "",
          icon: "",
        },
        plainEnglishMeaning: {
          title: "How often the provider returned successfully under the test rules.",
          tooltip: "",
          icon: "",
        },
        importantCaveat: {
          title: "The reliability companion to latency.",
          tooltip: "",
          icon: "",
        },
        id: 3,
      },
      {
        metric: { title: "Failed requests", tooltip: "", icon: "" },
        technicalDefinition: {
          title: "Count of attempts that did not meet the success criteria.",
          tooltip: "",
          icon: "",
        },
        plainEnglishMeaning: {
          title: "How many requests failed.",
          tooltip: "",
          icon: "",
        },
        importantCaveat: {
          title: "Shown as counts per method; the flip side of success rate.",
          tooltip: "",
          icon: "",
        },
        id: 4,
      },
    ],
  }}
/>

## 为什么要将延迟和成功率放在一起报告？

只有当响应真正返回时，快速的响应才有意义。一个服务商可能在成功的请求上显示较低的延迟，同时更频繁地超时或失败，因此我们将延迟与成功率一起报告：延迟展示有效响应返回的速度，成功率展示响应是否返回成功的频率。

举例来说，如果我们发送 1,000 次尝试，其中 990 次在 8 秒超时之前成功返回，成功率就是 99%。失败的 10 次尝试不会计入 p50、p95 或平均延迟，而是作为可靠性方面的失败体现出来。

这可以避免两种误读：不能仅凭速度就判断一个延迟低但失败更多的服务商，也不能仅凭可靠性就判断一个成功率高但尾部延迟较慢的服务商。

## 基准测试为什么使用滚动时间窗口？

基准测试持续运行。常见的公开 EVM 读取方法大约每 10 秒运行一次，我们每 5 分钟从过去 24 小时的滚动窗口中刷新一次[公开基准测试页面](https://www.alchemy.com/benchmarks)。

在完全正常运行的情况下，一个每 10 秒运行一次的方法，每天会为每个服务商、方法、网络和地区产生约 8,640 次尝试。滚动的 24 小时窗口为每个公开数字提供了足够的样本量，可以平滑掉偶发的波动，同时保持页面数据的新鲜度。

因此，请将公开页面上的数字理解为滚动的每日汇总，而不是单次请求、一次性测试或某个瞬间的快照。

## 基准测试没有衡量什么？

目前，基准测试衡量的是受控条件下、经过预热的单方法读取性能。它没有衡量：

- 多次调用组成的用户流程
- 写入交易或交易提交
- WebSocket 或订阅行为
- 客户特定的请求组合
- 冷启动的首次请求连接建立
- 每一个服务商、链、地区、方法或负载形式
- 你自己架构、流量水平或部署地理位置的具体表现

正是这一边界让基准测试变得有用：它告诉你这些数字适用于哪些场景，以及在哪些情况下你应该运行自己的、针对具体工作负载的测试。

速度和可靠性是相关但不同的问题：

- 速度关注的是成功的请求返回得有多快。
- 可靠性关注的是请求成功返回的频率有多高。

我们设计这个基准测试正是为了体现这一区别，而不是把它简化成一个“最快服务商”的笼统结论。

## 开发者应该如何使用这些结果？

将[公开基准测试](https://www.alchemy.com/benchmarks)作为服务商评估的起点。一种实用的解读方式：

1. 选择与你的工作负载最接近的链和方法。
2. 对比与你的用户或基础设施所在位置最匹配的地区。
3. 关注 p95，而不只是平均值或 p50。
4. 将成功率与延迟一起查看。
5. 如果你的工作负载使用不同的负载形式、更高的并发量、写入操作、WebSocket 或多次调用流程，在做决定之前请运行针对该工作负载的专门基准测试。

透明的基准测试能帮助你提出更好的问题。它让你更容易看清哪些内容被测试了、哪些被计入了结果、哪些被排除在外，以及结果中哪些部分对实际应用最为重要。

## 常见问题

### Alchemy 的公开 EVM 读取基准测试是什么？

这是一项受控测试，用于衡量 RPC 服务商处理常见 EVM JSON-RPC 读取请求的方式。它记录了在选定的服务商、链、方法和地区范围内成功响应的延迟（平均值、p50、p95）、成功率和失败请求数，让开发者能够在完全相同、可重复的条件下比较各服务商，而不是依赖营销说辞。

### RPC 延迟是如何衡量的？

延迟是运行端对一次 JSON-RPC POST 请求及其响应、在预热且复用的连接上测得的实际耗时。计时器在请求发出时启动，在收到有效响应时停止。只有成功的响应才会被计入，因此延迟反映的是响应速度，而不是连接建立过程或失败情况。

### 什么算作一次成功的请求？

只有在 8 秒超时时间内返回、返回 HTTP 2xx 状态、可解析为 JSON，且不包含 JSON-RPC 错误对象的情况下，一次请求才算成功。其他情况，包括超时、HTTP 错误和限流响应，都会被计为一次失败的尝试，并被排除在延迟统计之外。

### 基准测试如何处理超时？

运行端最多等待 8 秒以获得有效响应。如果没有响应到达，这次尝试会被记录为超时失败，而不是被当作一次耗时 8,000 毫秒的慢响应。超时会降低成功率，但绝不会拉高延迟数字，这使得速度和可靠性始终作为独立的指标存在。

### 基准测试是否使用重试？

不使用。每次测量都是单次尝试。如果这次尝试失败，就会被计为失败，而不会通过重试将其掩盖。这样可以让成功率保持真实，因为重试会掩盖真实应用实际会遇到的间歇性失败。

### 基准测试涵盖哪些链和服务商？

公开基准测试涵盖 Ethereum、Optimism、Arbitrum、Base 和 World Chain 主网。服务商集合包括 Alchemy、dRPC、Infura 和 QuickNode。对于给定的方法，每个服务商在同一地区、相同的超时和成功判定规则下，收到的都是同样配置好的请求。

### 基准测试测试哪些 RPC 方法？

基准测试运行七项配置好的读取测试，基于四个 EVM 方法构建：`eth_getBalance`；在最早和最新区块上的 `eth_getBlockByNumber`；覆盖 1、10 和 100 个区块范围的 `eth_getLogs`；以及 `eth_getTransactionReceipt`。Ethereum 额外增加了一项覆盖 1,000 个区块的 `eth_getLogs` 测试，共计八项。每项测试都使用固定的负载，以保持结果在各服务商之间的可比性。

### 基准测试没有衡量什么？

它没有衡量完整的应用工作流、写入交易、WebSocket 订阅、客户特定的流量组合，或冷启动连接建立过程。它也没有覆盖每一条链、每一个服务商、每一个地区、每一个方法或每一种负载形式。对于这些情况，请运行与你自身流量相匹配的、针对具体工作负载的基准测试。

### 我应该如何解读 p95 延迟数字？

将 p95 理解为：对于这个方法、链、地区和时间窗口，95% 的成功响应至少达到了这个速度。它揭示了平均值所掩盖的尾部表现。请始终将其与成功率一起解读，因为只有当响应真正返回时，快速的响应才有意义。
