一、前言
test
环境服务启动,通过代码新注册一个customer group
进行消费,服务一直报错如下:
level=error msg="fetch offset of mq from broker error"
MessageQueue="MessageQueue [topic=xxx, brokerName=broker-a, queueId=1]"consumerGroup=xxx underlayError="broker response code: 22, remarks: Not found, V3_0_6_SNAPSHOT maybe this group consumer boot first"
奇怪的点在于,dev
环境相同的代码,是可以注册成功并且消费的,而且运维也登上去通过命令行发送和消费过消息,确认开发环境是ok
的。
二、来自chatgpt的回答
Broker 返回状态码 "22",并且附加了错误信息 "Not found,V3_0_6_SNAPSHOT maybe this group consumer boot first"。
这意味着该 Consumer Group 可能是第一次启动,并且 Broker 端
还没有关于它的相关记录
也就是说,这个broker 22
错误是新注册customer group
后会经常出现的,因为新的customer group
要和broker
同步offset
,同步之后就会成功。然而实际上服务起了一夜,还是一直报这个错误。猜测是一直轮询,但是每次同步offset
就报错了。
三、go客户端为什么一直报错
1、rocketmq客户端问题
参考:
https://github.com/apache/rocketmq-client-go/pull/886
https://github.com/apache/rocketmq-client-go/issues/993
经过一顿搜索,终于在官方的issue
上发现有这个提问,具体链接如上。可以看到,go
的rocketmq-client-go
在新customer group
的情况下,同步offset
会返回error
error
会导致我们收到上面的错误打印。
off, err := r.fetchConsumeOffsetFromBroker(r.group, mq)if err != nil {rlog.Error("fetch offset of mq from broker error", map[string]interface{}{rlog.LogKeyConsumerGroup: r.group,rlog.LogKeyMessageQueue: mq.String(),rlog.LogKeyUnderlayError: err,})r.mutex.RUnlock()return -1, err}
2、rocketmq客户端社区的修复
rocketmq-client-go
社区已经修复了这个问题,在找不到consumer group
的时候,返回-1
和nil
,这样就不会报出来错误,阻塞下面的流程。
if res.Code == internal.ResQueryNotFound {return -1, nil}// 函数外处理,把offset注册到OffsetTable这个map中
// OffsetTable map[primitive.MessageQueue]int64
// key是mq的消费队列,value是offset值。把offset=-1挂到我们新group对应的mq queue实例上,
// 接下来就可以同步offset了
func (r *remoteBrokerOffsetStore) update(mq *primitive.MessageQueue, offset int64, increaseOnly bool) {r.mutex.Lock()defer r.mutex.Unlock()localOffset, exist := r.OffsetTable[*mq]if !exist {r.OffsetTable[*mq] = offsetreturn}if increaseOnly {if localOffset < offset {r.OffsetTable[*mq] = offset}} else {r.OffsetTable[*mq] = offset}
}
有兴趣的同学可以看看java
的实现,还是挺清晰的,代码地址:https://github.com/apache/rocketmq/blob/develop/client/src/main/java/org/apache/rocketmq/client/consumer/store/RemoteBrokerOffsetStore.java#L228
四、为什么dev环境是可以正常消费
百思不得其解,明明是同一套代码。。后来问了运维,原来dev
环境服务刚启动也是报错的状态,然后运维手动去发送了命令,并通过新customer group
消费了一次,后来服务就可以成功消费了。
RocketMQ 的 Consumer 连接上 Broker 之后,会自动拉取消息消费进度。Consumer 向 Broker
发送拉取消息的请求时会携带该 Consumer Group 对应的每个 Message Queue 消费进度,Broker
会根据这些消费进度返回还未被消费的消息给 Consumer,并将实际消费进度保存到 Consumer 所在的
Consumer Group 的消费进度表中。
也就是说,命令行之行的时候,自动进行了consumer
的offset
同步。。。
五、最终的解决方案
1、升级rocket-rocket-go为master版本
master版本已经修复了这个问题
go get github.com/apache/rocketmq-client-go/v2@master
// 下面是我现在的go.mod
//github.com/apache/rocketmq-client-go/v2 v2.1.2-0.20230518020902-2a8172bb9174
升级master
毕竟不是稳定版本,可能会有问题,更多是临时方案,后续还是要安装社区稳定版本的。
2、登录rocketmq实例,手动发送消息
手动发送并消费消息,相当于使用rocketmq官方的client来同步offset,这样就绕开了rocketmq-client-go的这个bug。消费者注册完成后,接下来就可以顺利的消费了。
3、提前创建好consumer group
不要通过代码创建,一了百了
end