一、前言

      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上发现有这个提问,具体链接如上。可以看到,gorocketmq-client-go在新customer group的情况下,同步offset会返回error

rocketmq消费 python rocketmq消费者注册不上_开发语言


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的时候,返回-1nil,这样就不会报出来错误,阻塞下面的流程。

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] = offset
		return
	}
	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 的消费进度表中。

      也就是说,命令行之行的时候,自动进行了consumeroffset同步。。。

五、最终的解决方案

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