换 Solana RPC 服务商就是换一个 URL。代码上确实只要这么多 —— 而这正是大多数迁移做得不好的原因。
★换是简单的。知道它有没有帮上忙,才是真正的工作。★
确实只要一行的那部分
const connection = new Connection(NEW_ENDPOINT, "confirmed");
Solana 的 JSON-RPC 接口是标准化的,所以同一份客户端代码对任何合规端点都能用。没有 SDK 要换,也没有数据要迁。
★而这也正是陷阱。★ 因为它太容易了,团队根据厂商的宣传就切了过去,看不出明显差别,而且没有任何办法判断自己是改善了还是弄糟了。
在切换之前测,不是之后
没有基线就无法评估一次迁移,而基线必须在切换之前采集:
async function baseline(connection, samples = 200) {
const results = [];
for (let i = 0; i < samples; i++) {
const submitSlot = await connection.getSlot();
const sig = await sendRepresentativeTransaction(connection);
const landed = await waitForLanding(sig);
results.push(landed.slot - submitSlot);
await sleep(1000);
}
results.sort((a, b) => a - b);
return {
p50: results[Math.floor(results.length * 0.5)],
p90: results[Math.floor(results.length * 0.9)],
p99: results[Math.floor(results.length * 0.99)],
};
}
★有两条规则让这次测量保持诚实。★
测 slot 距离,不要测毫秒。 一次更快的往返,如果没有改变你落在哪个区块,就没有改变任何要紧的事。
在高负载下测,不要在清闲时段测。 ★网络平静时看起来一模一样的端点,在拥堵下会分化★ —— 而拥堵正是你的结果被决定的时候。
如果你测完发现差距在提交而不在读取,免费领个 BoltTx key 改一行就能拿来对比。
服务商之间实际差在哪
JSON-RPC 接口是标准的;围绕它的行为不是。
| 方面 | 差异所在 |
|---|---|
| 限流 | ★按请求数 vs 按积分加权★ |
getProgramAccounts |
★常被限制或禁用★ |
| 历史保留 | 近期 slot vs 数月 |
| WebSocket 限制 | 每连接的订阅数 |
| 节点池同步 | ★节点之间的 slot 偏差★ |
| 优先费数据 | 采样窗口与准确度 |
★带星的那两行造成了最多的迁移意外。★
按积分加权计费意味着,一个在某家限流内的请求量,可能在另一家直接爆掉 —— 因为 getProgramAccounts 的代价可能是 getBalance 的很多倍。
slot 偏差表现为余额看起来在倒退。不同服务商的节点池行为不同,在一个端点上从不需要 minContextSlot 的代码,换一家可能就需要了。
先并行跑两家
安全的迁移不是切换,是一段影子期:
// ★旧端点仍然是权威。新端点只做测量。★
const sig = await oldConnection.sendRawTransaction(raw, opts);
void newConnection
.sendRawTransaction(raw, opts) // ★相同字节 —— 安全★
.then((s) => shadowMetrics.record(s))
.catch((e) => shadowMetrics.recordError(e));
★把相同的已签名字节发给两个端点,不可能重复执行。★ 签名是同一个,而一个签名最多被收录一次 —— 所以第二次发送要么被网络丢弃,要么是通往同一次收录的更快路径。
这是唯一一种没有风险的并行提交,也是影子测试之所以可行的原因。为第二个端点重建交易就不安全了。
渐进切换
const useNew = hash(requestId) % 100 < rolloutPercent;
const conn = useNew ? newConnection : oldConnection;
分阶段推进 —— 先一小部分,再更多 —— 并且在每一步都比较两个样本群,而不是盯着看板凭感觉判断。
★切换完成之后,把旧端点继续配着。★ 两个都好的时候,按健康状况做故障切换不花任何代价,而它就是"某家服务商劣化"变成一次不便、还是变成一次故障的分界。
const endpoints = [primary, secondary];
for (const ep of endpoints) {
if (!health.isHealthy(ep)) continue;
try { return await ep.sendRawTransaction(raw, opts); }
catch (e) { health.recordFailure(ep); }
}
把读取和发送分开
迁移是个好时机,可以不再把一个端点当成一件事。
★读取和发送的要求是相反的。★ 读取很重、有突发性、能容忍一个 slot 的陈旧;发送很小、对延迟敏感、绝不能被别的东西饿死。
共用一个端点意味着一次 getProgramAccounts 回填能耗尽你发送所需的配额 —— 而且恰好在你的机器人最忙的时候。把它们分开,消除掉的是一整类"在它发生之前完全不可见"的故障。
不该轻信什么
厂商的延迟数字。 通常是在有利条件下、对一个廉价调用测出的往返时间。★往返时间不是 slot 距离。★
清闲时段的对比。 唯一有意思的问题是拥堵下的表现。
你的第一个小时的数据。 缓存预热、连接池、DNS 都需要时间稳定下来。
平均值。 ★两个平均延迟完全一样的端点,尾部可能天差地别,而亏损住在尾部。★ 比 p90 和 p99。
上链应该是什么水平
我们投递节点上的真实交易:中位确认 336 毫秒 —— 不到一个 slot。
★这是你该拿自己的测量结果去对照的基线。★ 如果在调完手续费和重试之后,你的分布仍然明显更宽,那剩下的差距是路由,而不是客户端配置。
BoltTx 在这里的位置
我们是一条发送路径,不是一个通用 RPC。所以我们是加在你现有配置之上,不是来替换它的。
读取、订阅、历史继续用你现在的服务商。只把 sendRawTransaction 指向我们,这是一行,而且可逆。提交走我们自建的四区域投递节点,带 SWQoS 路由,不暴露公共 mempool —— 交易在落块之前、传输途中观察不到。
你在本地签名。我们不托管资金、不代签、不修改交易内容 —— 所以拿相同字节和你当前的端点并行做影子测试是安全的。tip 带在交易里、从你自己的钱包链上支付,交易失败时跟着一起 revert —— 这是 Solana 原子交易的机制决定的。只有到达链上的交易才计费。
免费领 API key,没有月费:
// 读取那边不用动。
const sender = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");
常见问题
换 Solana RPC 服务商有多难? 代码改动只有一行,因为 JSON-RPC 接口是标准化的。真正的工作是衡量这次改动有没有帮上忙,而这需要一个在切换之前采集的基线。
对比 RPC 服务商时该测什么? "提交到落块"的 slot 距离,按 p50/p90/p99 看分布。 往返延迟说明不了你有没有赶上下一个区块,而那才是决定竞速胜负的东西。
能不切换就测试一家新服务商吗? 能。把相同的已签名字节发给两个端点,记录新端点的结果但不据此行动。相同字节产出同一个签名,所以这不可能重复执行。
把同一笔交易发给两家服务商安全吗? 安全,前提是字节完全相同。一个签名最多被收录一次,所以第二次提交要么被丢弃,要么是通往同一次收录的更快路径。
接口既然是标准的,服务商之间还差什么?
限流模型、getProgramAccounts 是否可用、历史保留时长、WebSocket 限制、节点池同步,以及优先费数据质量。接口一样,行为不一样。
迁移之后请求量为什么突然超限了? 很多服务商按加权积分而不是按请求数计费,所以重调用的代价远高于轻调用。同样的流量能装进一家的限额、却超出另一家的。
在新服务商上余额为什么看起来不一致?
节点池行为不同,连续请求可能打到处于不同 slot 的节点。把你见过的最高 slot 作为 minContextSlot 传进去,拒绝陈旧响应。
迁移完成后该保留旧服务商吗? 该,作为故障切换。两个都好的时候,按健康状况在两个已配置端点之间选择不花任何代价,它把一次服务商劣化从故障变成不便。
该多渐进地切换? 分阶段,每一步都比较两个样本群。按请求标识做百分比切分,能让你比较同类,而不是从看板上凭感觉判断。
读取和发送该用同一个端点吗? 最好不要。读取很重且有突发性,而发送对延迟敏感,所以共用端点会让一次回填在最糟的时刻耗尽发送所需的配额。
能相信服务商公布的延迟数字吗? 把它当作一个起点。它们通常报告的是有利条件下、对轻量调用的往返时间,而那和拥堵下的 slot 距离不是一回事。
对比该跑多久? 长到能覆盖拥堵时段,不只是一个清闲窗口。缓存、连接池和 DNS 也需要时间稳定,所以第一个小时不具代表性。
两个端点测试时一样、生产里为什么不一样? 因为测试通常发生在低负载下。端点在拥堵下才分化,而拥堵恰恰是你的成交率被决定的时候。
换服务商除了 URL 还要改代码吗?
发送路径通常不用。你可能需要为一致性加上 minContextSlot,或者在限流模型不同时调整并发,但客户端代码本身不变。
如果新服务商读取更快、发送不更快呢? 这很常见,也正是把两条路径分开有用的原因。你可以留下更快的读取、把发送路由到别处,而不用让一个决定牵制另一个。
怎么知道这次迁移真的有帮助? 在可比条件下,对比迁移前后的 slot 距离分布。 如果 p90 没变,那改善的东西并没有传导到你真正在乎的结果上。