1. 从一条命令行说起substrate 到底在解决什么问题第一次接触 substrate 这个词是在一个做跨链数据同步的项目里。当时团队需要把一条业务链上的状态变更实时同步到另外几条异构链上同时还要保证每条链上的数据最终一致。最初的想法很朴素——写个中间件轮询各链的区块解析事件日志再逐条转发。结果上线不到一周就崩了区块重组导致重复消费、事件顺序错乱、某条链短暂不可用就造成数据断层。后来一位做底层协议的朋友丢过来一句“你这种场景本质上是在做状态订阅与最终一致性substrate 那套东西就是干这个的。”这里的 substrate指的是一类面向区块链状态订阅与跨链消息传递的底层框架它的核心能力是让开发者不用从零去处理区块重组、事件去重、确认深度、断点续传这些脏活而是通过一套抽象好的接口直接订阅“链上发生了什么”并把消息可靠地投递到目标端。它解决的不是“怎么发交易”而是“怎么可靠地知道链上发生了什么并且保证这件事被正确处理一次”。适合谁来参考这篇内容如果你正在做以下任意一件事这篇都值得往下看跨链桥的消息层、链上事件驱动的后端服务、多链数据聚合、DeFi 清算机器人、NFT 铸造监听、以及任何需要“链上状态变化触发链下动作”的场景。哪怕你只是刚接触区块链后端开发只要理解 HTTP 和消息队列的基本概念这篇里的思路和踩坑记录都能直接拿去用。我下面会从整体设计思路、核心机制拆解、实操落地、问题排查四个维度把 substrate 这类框架的里里外外讲透。内容基于我实际参与过的两个跨链同步项目以及和几位做基础设施的朋友反复讨论后的经验总结不是文档翻译是实打实的工程视角。2. 整体设计与思路拆解为什么不能自己轮询了事2.1 自己轮询区块的三种死法很多人第一反应是不就是监听链上事件吗我写个定时任务每隔几秒拉一次最新区块解析日志不就行了。这个方案在测试网跑得挺欢一上主网就出问题。我总结下来有三种典型死法。第一种是区块重组导致的重复与丢失。区块链的“最新区块”并不是最终确定的尤其在 PoW 或者某些 PoS 的短确认场景下你刚处理完高度 1000 的区块下一秒它可能被回滚高度 1000 换成了另一个区块。如果你的服务已经把旧区块里的事件发到了下游就产生了脏数据如果你只记录“处理到 1000”重组后新 1000 的事件就被跳过了。第二种是事件顺序与并发问题。同一个区块里可能有多笔交易触发同一个合约的同一个事件它们之间有严格的顺序依赖。你用多线程去解析和转发顺序一乱下游的状态机就崩了。更麻烦的是跨合约调用产生的事件它们的因果顺序在日志里是隐含的不仔细处理就会错。第三种是节点不可用与断点续传。你依赖的 RPC 节点可能重启、限流、或者临时返回错误。如果没有一套可靠的游标机制服务重启后要么从头扫浪费资源要么从内存里的位置继续数据丢失。我见过最惨的一个案例服务重启后游标归零把整条链的历史事件重新发了一遍下游直接被打爆。2.2 substrate 的核心抽象订阅、确认、投递三段式substrate 这类框架的设计哲学是把上面这些脏活收敛成三个清晰的阶段订阅Subscribe、确认Confirm、投递Deliver。每个阶段都有明确的职责和状态管理开发者只需要关心“订阅什么”和“投递到哪”中间的可靠性由框架保证。订阅阶段负责和链节点交互拿到区块和事件。这里的关键设计是确认深度Confirmation Depth和重组检测Reorg Detection。框架不会一拿到最新区块就往下发而是等这个区块被后续 N 个区块确认后才认为它“安全”。N 的取值取决于链的出块时间和业务对一致性的要求比如以太坊主网常用 12 到 15一些快速链可能只要 3 到 5。同时框架会持续比对已确认区块的哈希一旦发现某个高度上的哈希变了就触发回滚逻辑把该高度之后已投递的消息标记为无效或重新投递。确认阶段是状态管理的核心。框架会维护一个游标Cursor记录“已经安全处理到哪个高度、哪个事件索引”。这个游标必须持久化通常落在数据库里并且和消息投递在同一个事务里更新保证“投递成功”和“游标前进”是原子的。这一步是很多自研方案最容易忽略的地方也是数据不一致的根源。投递阶段负责把消息送到目标端可能是另一个链的合约、一个消息队列、或者一个 HTTP 接口。投递需要支持至少一次At-least-once语义因为网络抖动、目标端超时都可能导致重试。这就要求下游具备幂等处理能力或者框架本身提供去重机制。substrate 通常会在消息里带上唯一的标识比如链 ID 区块哈希 交易哈希 日志索引下游用这个标识做去重。2.3 为什么选框架而不是自研有人会问这些逻辑我自己也能写为什么要用 substrate 这类框架。我的回答是你能写但你不一定能写对更不一定能维护好。重组检测、确认深度动态调整、游标事务、重试退避、多链适配这些细节每一个都有坑而且坑和坑之间会相互影响。框架的价值在于它把这些边界情况都考虑过了并且经过了多个项目的验证。另一个重要原因是多链适配。不同的链在区块结构、事件日志格式、确认机制、RPC 接口上都有差异。substrate 这类框架通常会抽象出一层适配器把不同链的差异屏蔽掉上层逻辑只需要处理统一的事件模型。这对于需要同时对接多条链的项目来说节省的工作量是巨大的。当然选框架也有代价。你需要理解它的抽象模型需要按照它的约定去组织代码遇到问题时排查链路会更长。但从我两个项目的经验看这个投入是值得的尤其是当你的业务对数据一致性有要求的时候。3. 核心细节解析与实操要点把可靠性拆到每一层3.1 确认深度怎么定一个可计算的方法确认深度不是拍脑袋定的它和链的出块时间、网络状况、以及你能承受的失败概率有关。一个粗略的估算方法是假设攻击者掌握总算力的 q 比例诚实节点掌握 1-q那么一个交易被回滚的概率随确认数指数下降。对于 q0.1 的情况6 个确认后回滚概率已经低于 0.1%12 个确认后基本可以忽略。但在实际工程中我们更常用的是观察法在目标链上跑一段时间统计历史区块的重组深度分布取一个覆盖 99.9% 情况的值。比如某条链过去半年的最大重组深度是 5那确认深度设 8 到 10 就比较稳妥。对于出块时间很短的链确认深度可以相应降低但不要低于 3否则重组检测会频繁触发影响吞吐。还有一个容易被忽略的点确认深度应该是可配置的并且支持按合约或按事件类型差异化设置。比如同一条链上一个高频低价值的游戏事件可以用 3 个确认而一个涉及大额资金的桥接事件可能需要 20 个确认。substrate 的配置通常支持这种粒度用的时候别一刀切。3.2 游标持久化事务边界是生命线游标持久化看起来简单实际上是最容易出错的地方。核心原则只有一条游标更新和消息投递必须在同一个原子操作里或者至少保证游标不会领先于实际投递进度。我见过两种错误做法。一种是先更新游标再投递结果投递失败游标已经前进消息永久丢失。另一种是先投递再更新游标投递成功但更新游标时服务崩溃重启后重复投递。前者丢数据后者重复数据。对于大多数业务重复数据比丢数据好处理所以通常选择“先投递、后更新游标”并配合下游幂等。更稳妥的做法是把游标和消息放在同一个数据库事务里。比如用 PostgreSQL建一张消息表投递时先插入消息记录状态为 pending然后更新游标提交事务。后台再有一个 worker 去实际发送消息发送成功后把状态改为 sent。这样即使发送失败消息记录还在可以重试游标也不会因为发送失败而回退。这个模式叫事务性发件箱Transactional Outbox在 substrate 类框架里很常见。注意游标的粒度要足够细。只记录区块高度是不够的因为一个区块里可能有多个事件处理到一半崩溃就会重复处理整个区块。建议游标精确到“区块高度 交易索引 日志索引”这样重试时可以从断点精确恢复。3.3 重组处理回滚不是删除是标记重组发生时框架需要把已经投递但后来被回滚的消息处理掉。这里的关键认知是你不能真的去下游删除消息因为下游可能已经基于这条消息做了不可逆的操作。正确的做法是投递一条“补偿消息”或者“回滚消息”让下游自己决定怎么处理。substrate 通常会在检测到重组后生成一个回滚事件包含被回滚的区块高度和哈希以及受影响的消息 ID 列表。下游收到回滚事件后可以根据自己的业务逻辑决定是撤销操作、还是标记为待人工处理。对于金融类应用通常需要人工介入对于游戏类应用可能直接回滚状态即可。重组检测的实现方式一般是框架维护一个最近 N 个区块的哈希列表每次拿到新区块时检查它指向的父区块哈希是否和列表里记录的一致。如果不一致说明发生了重组需要往回找到分叉点然后把分叉点之后的所有区块标记为无效。这个过程需要递归或迭代处理因为重组可能跨多个区块。3.4 消息去重唯一标识的设计至少一次投递意味着下游必须能去重。唯一标识的设计要满足两个条件全局唯一和可重现。全局唯一保证不同消息不会碰撞可重现保证同一条消息在重试时生成相同的标识。常用的标识组合是chain_id block_hash tx_hash log_index。这四个字段组合起来在一条链上是唯一的而且只要区块内容不变重新解析同一条日志会得到相同的标识。注意不要用block_number因为重组后同一高度可能是不同区块也不要用自增 ID因为重试时无法重现。下游去重的实现方式有两种。一种是维护一张已处理消息 ID 的表处理前先查表处理完插入。这种方式简单但表会越来越大需要定期清理。另一种是利用业务本身的幂等性比如转账操作检查“这笔交易是否已经处理过”用业务主键做去重。后者更优雅但要求业务逻辑本身支持幂等。实操心得在消息体里除了唯一标识还建议带上原始事件的完整内容、区块时间戳、以及一个“重试次数”字段。排查问题时这些信息能帮你快速定位是哪条链、哪个区块、哪笔交易出的问题。我吃过亏早期消息体只带了一个 ID出问题时得回去翻链上数据效率极低。4. 实操过程与核心环节实现从零搭一条订阅管道4.1 环境准备与依赖选型假设我们要用 substrate 类框架搭一条从链 A 到消息队列的订阅管道。先明确技术栈链 A 是以太坊兼容链消息队列用 Kafka状态存储用 PostgreSQL。框架本身通常提供多种语言的 SDK我选 Go 版本因为并发模型适合这种 IO 密集型场景。依赖清单如下框架 SDK、链的 RPC 客户端、PostgreSQL 驱动、Kafka 客户端、以及一个配置管理库。版本上要注意框架 SDK 和链 RPC 的兼容性比如某些框架对以太坊的eth_subscribe和eth_getLogs支持程度不同选之前先看文档里的兼容性矩阵。配置项需要提前规划好链的 RPC 端点建议配多个做故障转移、确认深度、起始区块从哪个高度开始订阅、游标存储的表名、Kafka 的 topic 和 broker 列表、以及重试策略最大重试次数、退避算法。这些配置建议用环境变量或配置中心管理不要硬编码。4.2 订阅器的初始化与启动流程订阅器的启动流程分几步。第一步是连接 RPC 节点做一次健康检查确认能拿到最新区块高度。第二步是读取游标如果游标不存在首次启动就从配置的起始区块开始如果存在就从游标的下一个位置开始。第三步是启动重组检测模块加载最近 N 个区块的哈希列表。第四步是启动投递 worker连接 Kafka 和 PostgreSQL。第五步是进入主循环不断拉取新区块、确认、解析事件、投递。主循环的核心逻辑是一个状态机拉取区块 - 等待确认 - 检测重组 - 解析事件 - 投递消息 - 更新游标。每个步骤都可能失败需要有不同的重试策略。比如拉取区块失败可以立即重试投递消息失败需要指数退避更新游标失败需要回滚整个批次。这里有个细节拉取区块和解析事件可以流水线化。不要等一个区块完全处理完再拉下一个而是预取几个区块放在缓冲区里这样能提高吞吐。但预取的数量要控制太多会占用内存太少又起不到缓冲作用。我一般设 10 到 20 个区块的缓冲。4.3 事件解析与消息构造事件解析是把链上的原始日志转换成业务可用的消息。这一步需要合约的 ABI用来解码日志里的 data 和 topics。ABI 可以从编译产物里拿也可以从链上浏览器下载。建议把 ABI 缓存起来不要每次解析都去读文件。解析时要注意几个坑。第一匿名事件没有 topic0解析方式不同需要特殊处理。第二索引参数和非索引参数在日志里的位置不同索引参数在 topics 里非索引参数在 data 里解码时要对应好。第三动态类型如 string、bytes的解码比较复杂建议直接用成熟的 ABI 解码库不要自己手写。消息构造时除了事件本身的内容还要带上元数据链 ID、区块高度、区块哈希、交易哈希、日志索引、时间戳、以及前面说的唯一标识。消息格式建议用 JSON 或 ProtobufJSON 可读性好Protobuf 体积小、解析快。如果吞吐量不大JSON 就够了如果每秒要处理几千条消息Protobuf 更合适。4.4 投递与确认的完整代码路径投递的代码路径大概是这样的从解析模块拿到消息 - 开启数据库事务 - 插入消息记录状态 pending- 更新游标 - 提交事务 - 发送到 Kafka - 发送成功则更新消息状态为 sent失败则进入重试队列。这里的关键是事务边界。插入消息和更新游标必须在同一个事务里否则会出现游标领先或落后的问题。发送到 Kafka 是在事务提交之后因为 Kafka 发送不是事务性的除非用 Kafka 的事务 API但那样复杂度高。发送失败时消息记录还在数据库里状态是 pending后台的重试 worker 会定期扫描 pending 消息并重新发送。重试策略我一般用指数退避第一次失败等 1 秒第二次等 2 秒第三次等 4 秒最多重试 10 次之后进入死信队列人工处理。退避的上限不要太大否则消息积压会越来越严重。同时要监控 pending 消息的数量如果持续增长说明下游有问题需要告警。注意Kafka 的发送是异步的要正确处理回调。发送成功和发送失败的回调里都要更新消息状态并且要考虑回调执行时数据库连接是否还可用。我遇到过回调里数据库连接池耗尽导致状态更新失败的情况后来把状态更新放到了一个独立的 goroutine 池里避免阻塞 Kafka 的回调线程。4.5 监控与告警的埋点位置监控是生产环境的眼睛。substrate 类管道需要监控的指标包括当前处理到的区块高度、最新区块高度、两者之间的差值lag、每秒处理的消息数、投递成功率、重试队列长度、重组次数、以及各阶段的耗时分布。lag 是最重要的指标。如果 lag 持续增长说明处理速度跟不上出块速度需要扩容或优化。lag 突然归零或变成负数可能是重组或者游标异常。投递成功率低于 99% 就要告警低于 95% 就要紧急处理。重组次数突然增多可能是链本身出了问题需要关注。埋点建议用 Prometheus 的客户端库暴露一个/metrics接口然后用 Grafana 做面板。告警规则可以配在 Alertmanager 里按严重程度分级别。我一般会配三条规则lag 超过 100 个区块告警、投递成功率低于 99% 告警、重组次数 10 分钟内超过 3 次告警。5. 常见问题与排查技巧实录那些文档里不会写的坑5.1 消息重复的三种来源与定位方法消息重复是最常见的问题来源主要有三种。第一种是投递成功但游标更新失败重启后从旧游标重新处理导致重复。第二种是Kafka 的 at-least-once 语义发送成功但 ack 丢失客户端重发。第三种是重组后的重新投递框架把回滚区块里的事件重新处理了一遍。定位重复问题首先要看消息的唯一标识是否相同。如果相同说明是同一条消息被重复投递去查游标更新和 Kafka ack 的日志。如果不同说明是不同的事件被解析成了相同的业务内容去查事件解析逻辑。我一般会在消息体里加一个produced_at时间戳重复消息的时间戳会不同能快速区分是重试还是重新解析。解决重复的根本方法是下游幂等。如果下游实在做不到幂等可以在框架层加一个去重窗口比如维护最近 10000 条消息的 ID 集合投递前先查集合。但这种方式有内存限制而且重启后会丢失只能作为辅助手段。5.2 游标卡住不动的排查路径游标卡住表现为 lag 持续增长但消息处理数不增加。排查路径从下往上先看投递 worker 是否在运行有没有 panic 或死锁再看数据库连接是否正常有没有慢查询然后看 Kafka 是否可达topic 是否存在最后看 RPC 节点是否正常有没有被限流。我遇到过一次游标卡住查了半天发现是 PostgreSQL 的连接池被一个长事务占满了。那个长事务是一个批量更新游标的操作因为数据量太大执行了几分钟还没提交导致其他操作都在等连接。后来把批量更新拆成了小批次每次更新 100 条问题就解决了。还有一个隐蔽的坑是时区问题。游标表里如果用了timestamp类型而应用和数据库的时区不一致比较时间时会出现偏差导致游标看起来没动。建议统一用 UTC 时间或者直接用整数存区块高度避免时区问题。5.3 重组导致的连锁反应与应对重组不仅影响当前区块还会影响后续所有依赖它的区块。如果重组深度是 5那么最近 5 个区块里的事件都可能需要回滚。框架需要能够快速找到分叉点并把分叉点之后的所有消息标记为无效。应对重组的关键是快速检测和快速响应。检测靠的是父哈希比对响应靠的是回滚消息的投递。回滚消息要包含足够的信息让下游能定位到需要撤销的操作。我一般会在回滚消息里带上原始消息的完整内容这样下游不用再去查数据库。重组频繁发生时要考虑是不是确认深度设得太低了。适当提高确认深度可以减少重组的影响但会增加延迟。这是一个权衡需要根据业务对延迟的敏感度来定。对于延迟不敏感的场景确认深度可以设大一些比如 30 甚至 50。5.4 常见问题速查表问题现象可能原因排查方法解决方案消息重复游标更新失败、Kafka ack 丢失、重组重投比对消息唯一标识和时间戳下游幂等、去重窗口、修复游标事务游标卡住投递 worker 挂掉、数据库连接池耗尽、Kafka 不可达检查 worker 状态、数据库连接、Kafka 连通性重启 worker、扩容连接池、修复 Kafkalag 持续增长处理速度跟不上、RPC 限流、解析耗时过长看各阶段耗时分布、RPC 错误率扩容、优化解析、增加 RPC 节点重组频繁确认深度太低、链本身不稳定统计重组深度分布提高确认深度、关注链的健康状况消息丢失游标领先于投递、事务边界错误检查游标和消息记录的一致性修复事务逻辑、用事务性发件箱模式解析失败ABI 不匹配、日志格式异常、匿名事件看解析错误日志、比对 ABI更新 ABI、特殊处理匿名事件实操心得这张表里的问题我几乎都遇到过最耗时的不是解决问题本身而是定位问题。建议在框架初始化时就打开详细日志把每个阶段的输入输出都记下来出问题时能快速回溯。日志量大的话可以分级正常情况记 INFO出问题时临时调到 DEBUG。6. 多链场景下的适配与扩展思路6.1 不同链的确认机制差异多链场景下每条链的确认机制都不一样。以太坊用 PoW 或 PoS确认深度和算力分布有关一些基于 BFT 的链确认是即时的但需要关注验证者集合的变化还有一些链用 DAG 结构确认的概念和链式结构完全不同。substrate 类框架通常通过适配器模式来处理这些差异。适配器负责把不同链的区块和事件转换成统一的内部模型上层逻辑不感知链的差异。写适配器时要注意确认深度的语义可能不同有的链是“区块数”有的是“时间”有的是“验证者签名数”需要统一成一种可比较的度量。我做过一个同时对接三条链的项目一条是以太坊兼容链一条是 Cosmos 系一条是自定义的联盟链。三条链的确认机制完全不同适配器写了三套但上层的投递和游标逻辑是共用的。这个架构的好处是新增一条链只需要写一个适配器不用动核心逻辑。6.2 跨链消息的顺序保证跨链场景下消息的顺序保证比单链复杂得多。如果两条链之间有因果关系比如链 A 上的事件触发了链 B 上的操作那么链 B 的消息必须在链 A 的消息之后处理。但两条链的出块时间不同消息到达的顺序可能和因果顺序不一致。解决这个问题有两种思路。一种是在消息里带上因果依赖下游根据依赖关系做排序。另一种是在框架层做全局排序用一个统一的序列号来标记消息的顺序。前者灵活但下游复杂后者简单但需要框架支持全局时钟。我倾向于第一种思路因为跨链的因果关系通常只在特定业务场景下存在全局排序会引入不必要的开销。具体做法是在消息体里加一个depends_on字段列出这条消息依赖的其他消息 ID下游收到消息后先检查依赖是否都已处理没有就暂存等待。6.3 扩展新链的检查清单新增一条链时按这个清单逐项检查链的 RPC 接口是否支持批量拉取区块和日志确认深度的定义和推荐值重组检测的方式父哈希比对是否适用事件日志的格式和 ABI 获取方式是否有特殊的限流或认证机制出块时间和历史重组深度分布以及链的最终确定性机制。这个清单能覆盖 90% 的适配工作。剩下的 10% 是链特有的坑比如某些链的 RPC 返回的区块里不包含完整的交易日志需要额外调用接口获取某些链的事件日志里没有 log index需要用其他字段组合成唯一标识。这些坑只能在实际对接时发现建议先在测试网跑一段时间再上主网。7. 性能调优与资源规划的实际经验7.1 批量拉取与并发解析的参数选择性能调优的第一步是批量拉取。不要一个区块一个区块地拉而是一次拉一批比如 50 到 100 个区块。批量拉取能显著减少 RPC 调用次数提高吞吐。但批量大小要控制太大可能导致单次响应超时或内存占用过高。我一般从 50 开始试根据 RPC 的响应时间和内存占用调整。并发解析是第二步。解析事件是 CPU 密集型操作可以用多个 goroutine 并行处理。但并发度不是越高越好太高会导致上下文切换开销增大反而降低吞吐。经验值是 CPU 核数的 2 到 4 倍。同时要注意解析后的消息投递是有顺序要求的并发解析后需要按原始顺序重新排序再投递。数据库写入是第三步。游标更新和消息插入都是写操作批量写入比单条写入快得多。可以用COPY或批量INSERT来提升性能。但批量写入的事务要控制大小太大容易锁表太小又起不到批量效果。我一般每批 500 到 1000 条。7.2 内存与连接池的容量规划内存占用主要来自三个地方区块缓冲区、消息队列、以及解析时的临时对象。区块缓冲区的大小是批量大小乘以单个区块的平均大小。消息队列的大小是并发度乘以单条消息的平均大小。临时对象的大小取决于事件的复杂度动态类型多的事件占用更大。连接池的规划要看并发量。数据库连接池的大小一般是并发 worker 数的 1.5 到 2 倍留一些余量给监控和后台任务。RPC 连接池的大小取决于 RPC 提供商的限制一般不要超过提供商允许的最大并发数。Kafka 的生产者连接池通常一个实例一个连接就够了Kafka 客户端本身会做批量和压缩。我做过一个每秒处理 5000 条消息的管道内存占用大概 2GB数据库连接池 50RPC 连接池 20Kafka 生产者 1 个。这个配置在 4 核 8G 的机器上跑得很稳。当然具体数字要看消息的大小和复杂度建议先压测再定容量。7.3 压测方法与瓶颈定位压测的方法有两种一种是回放历史区块把过去的区块重新拉一遍看处理速度另一种是模拟生成区块用测试链或本地链产生大量事件。前者更真实但受限于历史数据的量后者可控但可能和真实场景有差异。瓶颈定位用 profiling 工具。Go 语言可以用 pprof看 CPU 和内存的热点。常见的瓶颈有ABI 解码耗时过长、数据库写入慢、Kafka 发送阻塞、以及 RPC 响应慢。针对不同的瓶颈有不同的优化手段解码慢可以缓存 ABI 或换更快的库数据库慢可以加索引或换批量写入Kafka 阻塞可以增加分区或调整批量参数RPC 慢可以换节点或增加并发。压测时要注意不要压垮生产链的 RPC 节点。用独立的测试节点或者用归档节点做回放。我见过有人直接压生产节点结果把节点打挂了影响了其他服务。这个坑一定要避免。8. 写在最后一些零散但有用的经验substrate 这类框架的核心价值是把跨链状态订阅的复杂性封装起来让开发者能专注于业务逻辑。但封装不等于透明理解它内部的确认、重组、游标、投递机制是用好它的前提。我见过太多人把框架当黑盒用出了问题完全不知道从哪查起。如果你刚开始接触建议先在测试网跑一个小项目把确认深度设小一点故意制造一些重组和失败观察框架的行为。这种“破坏性测试”能帮你快速建立对框架的直觉。等熟悉了再上生产心里就有底了。另外监控和告警一定要在项目初期就做好不要等到出问题才补。lag、投递成功率、重组次数这三个指标是最基本的缺一不可。日志要详细但要有分级正常运行时不要刷屏出问题时能快速定位。最后分享一个我常用的调试技巧在本地起一个 mock 的链节点返回预设的区块和事件用来复现特定的问题场景。比如想复现重组就让 mock 节点在某个高度返回不同的哈希想复现投递失败就让 mock 的 Kafka 返回错误。这种方式比在真实链上复现快得多也更可控。
阅读完成 · 觉得有帮助?