如何在NATS JetStream流上正确实现请求-应答模式
问题背景
- 此前参考JetStream Walkthrough编写了Go语言NATS基础示例,对应代码版本为nats-stream-example仓库2c834d7d967f024348fbaa478eae18e9749431ba提交版本
- 后续尝试参考Request-Reply Walkthrough编写扩展示例,目标是基于JetStream实现请求-应答能力(区别于原生无JetStream的核心NATS请求应答),对应代码为nats-stream-example仓库149243b8bd30974a592061cd9d0c3a9b7f3f30fc提交版本
- 测试时出现异常:未启动reply子命令的情况下,仅运行request子命令就收到了内容为
msg.Data={"stream":"my_stream2", "seq":112}的应答
复现步骤
- 启动开启JetStream功能的NATS服务:
$ nats-server -js
- 创建指定流与绑定主题:
$ ./nats-stream-example stream-add --stream my_stream2 --subject foo2
- 为目标流创建拉取消费者:
$ ./nats-stream-example consumer-add --consumer pull_consumer2 --stream my_stream2
- 发起请求:
$ ./nats-stream-example request --subject foo2 --count 100
异常根因
测试中收到的msg.Data={"stream":"my_stream2", "seq":112}不是应答端返回的业务响应,是JetStream服务端在消息成功持久化后返回的发布确认(PubAck)。代码未区分JetStream发布确认帧和实际业务应答,把发布确认误当成请求响应返回,才会出现在未启动reply端的情况下提前收到“应答”的问题。
正确实现方案
JetStream是NATS提供的持久化流处理能力,和核心NATS的请求-应答模式是叠加使用关系,不需要完全重写请求-应答逻辑,二者组合的正确实现规则如下:
- 流配置规则:仅为请求发送的业务主题(比如示例中的
foo2)创建JetStream流,不要将请求方动态生成的reply主题纳入流的subject匹配范围,避免应答消息被流持久化干扰正常逻辑。如果确实需要持久化应答消息,单独为reply主题创建独立流,不要和请求流混用。 - 请求侧实现要点:
- 每个请求生成全局唯一的临时reply主题,订阅该主题等待应答
- 调用JetStream Publish接口向业务主题发送请求时,传入构造好的、携带reply主题的消息
- 收到消息时先判断消息来源主题:只有从reply主题收到的消息才是业务应答,直接从Publish接口同步返回的内容是JetStream的发布确认,不属于业务应答范畴
- 应答侧实现要点:
- 创建绑定业务主题的JetStream消费者(拉取/推送模式均可)
- 消费到请求消息后,从消息元数据中取出请求方指定的reply主题
- 完成业务逻辑处理后,直接调用核心NATS的Publish接口将应答发送到该reply主题即可,不需要将应答发布到JetStream流
- 可靠性说明:如果需要保证请求消息不丢失,仅通过JetStream持久化请求侧的业务主题即可满足需求,应答侧默认走核心NATS的点对点投递即可,不需要额外配置流。
内容的提问来源于stack exchange,提问作者hnakamur
相关产品推荐
相关产品推荐

