---
title: "如何零停机迁移 RPC 提供商"
description: "迁移 RPC 提供商只是更换端点，而不是重建系统。本文介绍开箱即用兼容性包含哪些内容、迁移前应先排查什么，以及如何在不停机的情况下完成切换。"
---

# 如何零停机迁移 RPC 提供商

<ImageBlock
  src="https://media.alchemy.com/blog/migrate-between-rpc-providers-zero-downtime-16x9-20260821.png"
  alt="在 RPC 提供商之间迁移，零停机"
  width={1920}
  height={1080}
  priority
/>

团队往往会推迟切换 RPC 提供商，因为这感觉像是一次重构。但多数情况下，这只是几个小时的配置工作。每个主流提供商都遵循相同的 JSON-RPC 标准，你的应用今天发出的请求，就是新提供商明天所期望的请求。迁移 RPC 提供商是换端点，不是重写代码。

剩下的工作都围绕这次替换展开，而不在替换本身之内：找出所有隐藏端点 URL 的地方，梳理那些少数不能直接迁移的提供商专属调用，并按用户无感知的顺序切换流量。

## 什么是即插即用的端点兼容性？

即插即用的端点兼容性是指两个 RPC 提供商对标准 JSON-RPC 方法接受相同的请求并返回相同的响应。应用只需更改调用的端点 URL 即可切换提供商。方法名、参数和响应结构保持不变，因此标准接口部分完全不需要改代码。

这种兼容性来自协议本身，而非提供商之间的约定。`eth_call`、`eth_getLogs`、`eth_sendRawTransaction` 以及 `eth_*`、`net_*`、`web3_*` 系列方法都由 [Ethereum JSON-RPC 规范](https://ethereum.org/en/developers/docs/apis/json-rpc/)定义，任何靠谱的提供商都会按规范实现它们。流式接口同理。基于 WebSocket 的 `eth_subscribe` 在[我们的订阅端点](https://www.alchemy.com/docs/reference/subscription-api)上携带的 newHeads、logs 和 newPendingTransactions 流，与在其他主流提供商上一样。

下面是我们的端点格式与多数其他提供商所用模式的对比：

<CodeSnippet
  language="text"
  code={`Alchemy:         https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY
Other providers: https://your-endpoint-name.provider.example/YOUR_AUTH_TOKEN/`}
/>

两者都是路径中带密钥的 URL。如果你的端点存放在环境变量中，迁移标准流量就只是改这个变量的值。这也是为什么保留[备用 RPC 端点](https://www.alchemy.com/overviews/alternative-rpc-endpoint)这一常规可靠性建议，同时也充当了迁移保险。你为可用性添加的故障转移机制，正是切换时所用的同一套机制。

标准 JSON-RPC 调用在新提供商上可以直接运行。真正隐藏的工作在于你的应用依赖了哪些提供商专属的东西，而找出这些地方正是审计要做的事。

## 迁移前应该审计哪些内容？

提供商迁移中的大多数意外都是在切换之后才被发现的，而其中每一个原本都可以提前发现。对于一个典型的代码库，审计通常只需一两个小时。

**找出所有端点引用。** 端点 URL 会以超出大多数人记忆的方式散落在各处。搜索环境文件、Hardhat 和 Foundry 配置、Docker 镜像、CI 流水线以及前端构建中，是否存在当前提供商的域名或任何硬编码的 URL。最容易漏掉的引用，通常藏在某个几个月没人打开过的 cron 任务或 CI 流水线里。

**梳理带提供商前缀的方法。** 大多数提供商都会提供带厂商前缀的便捷方法（我们的前缀是 `alchemy_`），这些方法无法跨平台迁移。代币查询对应到 [Data API](https://www.alchemy.com/docs/data) 的等效方法，例如 [alchemy_getTokenBalances](https://www.alchemy.com/docs/data/token-api/token-api-endpoints/alchemy-get-token-balances)，转账记录则对应 [alchemy_getAssetTransfers](https://www.alchemy.com/docs/data/transfers-api/transfers-endpoints/alchemy-get-asset-transfers)。等效方法是存在的，但响应结构有所不同，因此应把每一个都当作小规模代码改动，而不是简单的查找替换。

**检查你的鉴权方式。** URL 路径中的密钥可以直接迁移。但如果你使用 `x-token` 请求头或 JWT 鉴权，这些控制需要在新提供商上重新配置。就我们而言，域名和 IP 白名单设置在 [Alchemy 控制台](https://dashboard.alchemy.com)中，几分钟即可完成。

**按新的计量模型重新估算流量成本。** 这是审计项中唯一会影响账单而非代码的一项。许多提供商按固定的每秒请求数配额计量。我们按[计算单元](https://www.alchemy.com/docs/reference/compute-unit-costs)计量，像 `eth_blockNumber` 这样的轻量调用消耗的计算单元比 `eth_getLogs` 这样的重量调用少，而[吞吐量](https://www.alchemy.com/docs/reference/throughput)是滚动窗口内的计算单元速率，而不是固定的请求数量。这种影响是双向的：日志密集型的索引负载消耗的配额，往往比其请求数所暗示的要快；而轻量轮询负载消耗的则更少。多数团队会在切换前，用[我们的定价](https://www.alchemy.com/pricing)对最繁忙一小时的真实流量进行估算，而不是靠猜测月度总量。

**检查 trace 和 debug 方法的套餐限制。** 多数提供商对 `debug_` 和 `trace_` 方法按套餐分级限制。在 Alchemy，这些方法在[按量付费和企业版套餐](https://www.alchemy.com/pricing)中可用，但免费套餐不支持，所以如果你在免费端点上跑概念验证，即使其他一切都正常，trace 调用也会失败，这一点要提前预料到。

以上任何一项都不会阻碍迁移，但合在一起就界定了迁移的范围。对多数应用来说，审计结束时得到的往往是一份简短的环境变量清单、一两个方法映射，以及一份定价估算。

## 如何做到零停机切换？

零停机的做法与团队用于数据库迁移的模式相同：新旧并行运行，分阶段迁移流量，并在新路径赢得信任之前，让旧路径保持可用。

客户端库让并行阶段几乎不费力气。viem 的[fallback transport](https://viem.sh/docs/clients/transports/fallback)接受一个有序的端点列表，当某个端点失败时会转向列表中的下一个：

<CodeSnippet
  language="typescript"
  code={`import { createPublicClient, fallback, http } from "viem";
import { mainnet } from "viem/chains";
const client = createPublicClient({
  chain: mainnet,
  transport: fallback([
    http("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"),
    http("https://your-endpoint-name.provider.example/YOUR_AUTH_TOKEN/"),
  ]),
});`}
/>

把新提供商放在第一位、旧提供商放在第二位，每个请求都会先尝试新端点，失败时回退到原本就正常工作的端点。切换期间最坏的情况，也就是你今天已有的配置。ethers.js 在 [FallbackProvider](https://docs.ethers.org/v6/api/providers/fallback-provider/) 中提供了同样的模式，还带有 quorum 选项，可以查询多个提供商，并在它们达成一致后才接受结果，这让你在客户端内部就能做到跨提供商的一致性校验。

迁移流量的顺序比迁移速度更重要，原则是先读后写。读操作是无状态的，也容易验证，所以应尽早将读流量指向新端点，并对返回结果做差异比对。在你合约最繁忙的区块范围上对两个提供商都运行最重的查询，比如针对合约区块范围的 `eth_getLogs` 和针对核心合约的 `eth_call`，并比较响应结果。交易提交放到最后迁移，等读操作验证无误之后再进行，因为读失败只是重试一次，而写失败则会变成一张工单。

WebSocket 订阅需要多加一步。订阅绑定在创建它的连接上，因此不会自动故障转移，它会直接断开，需要你重新创建。计划在新端点上重新订阅，并用一个覆盖重连期间所经历区块范围的 `eth_getLogs` 查询回填这段缺口，确保不丢失任何事件。我们的[WebSocket 最佳实践](https://www.alchemy.com/docs/reference/best-practices-for-using-websockets-in-web3)也建议保持订阅范围尽量窄，这样重新订阅和回填的成本都会更低。

并行运行期间，有两项指标值得放到仪表盘上跟踪。第一是区块高度滞后。两个健康的提供商在任一时刻都可能处于略有不同的链头位置，所以应对新端点持续落后于旧端点的情况报警，而不是对瞬时差异报警。第二是错误形态。限流响应在不同提供商之间有所不同。以 HTTP 为例，我们返回带有 [Retry-After 请求头](https://www.alchemy.com/docs/reference/throughput)的 429；而在 WebSocket 上，同样的限制则表现为不带请求头的 JSON-RPC 429 错误。在新端点承载生产流量之前，先针对你使用的两种传输方式测试一下重试逻辑。

多数团队会让并行阶段承载一两天的生产流量，然后将新端点设为默认。旧端点作为回退配置保留，直到其计费周期结束。在整个过程中，回滚始终只是一次配置改动之遥，这也正是零停机在实践中的含义。问题仍然可能发生，只是永远不必触及用户。

## 如何判断新提供商是否表现良好？

你在并行阶段所做的比对，会成为基线。切换之后，继续观察切换前比对过的那些数字：成功率、p95 延迟和区块新鲜度。如果这次迁移值得做，其中至少有一项应该朝有利方向变化。

延迟方面的说法你也不必只凭信任。我们的[实时 RPC 基准测试](https://www.alchemy.com/benchmarks)在不同链和地区上将我们与其他主流提供商做了对比，其[背后的方法论](https://www.alchemy.com/blog/how-we-benchmark-rpc-performance)是公开的，你可以在承诺之前，从自己所在地区复现这些数据。想深入了解这些数据为何能在负载下保持稳定，可以看看我们的[边缘代理重构](https://www.alchemy.com/blog/alchemy-edge-proxy)，其中详细介绍了每个请求首先经过的路由层。

如果你还在权衡提供商，而不是在验证某一个，我们的[选择节点提供商指南](https://www.alchemy.com/overviews/how-to-choose-a-blockchain-node-provider)和[企业级 RPC 评估指南](https://www.alchemy.com/blog/blockchain-rpc-infrastructure-evaluation-guide-for-enterprises)会涵盖在迁移问题出现之前就值得思考的那些问题。

迁移完成之后，也要保留并行运行期间的检查机制。用于验证切换的响应比对和延迟仪表盘，几个月后依然能发现提供商的性能退化和数据陈旧问题，而且它们已经搭建好了，无需重建。

## 准备好切换 RPC 提供商了吗？

无论你正在离开的是哪家提供商，以上内容都原样适用。

在 [Alchemy 控制台](https://dashboard.alchemy.com)中创建一个免费端点，今天就把你的预发布环境指向它。不需要签合同，不需要销售电话，也没有最低消费。免费套餐包含[每月 3000 万计算单元](https://www.alchemy.com/pricing)，足以在真实生产流量上完成整个并行阶段的对比，并且同样的端点替换方式，在我们支持的[100 多条链](https://www.alchemy.com/rpc)上都适用。
