rocketmq:4.0.0
生产者
注意:defaultTopicQueueNums参数,生产者客户端指定的主题队列数量(实际对应Broker端的WriteQueueNums),如果大于WriteQueueNums默认配置则会失效
消费者
push推消息模式,集群模式,消费类型为并发类型,消费下标类型为从上次下标开始消费兜底为最后一个下标(某些极端情况会降级为从头开始消费):CONSUME_FROM_LAST_OFFSET
服务端查询消费者消费下标逻辑:CONSUME_FROM_LAST_OFFSET类型
- 客户端从broker查询消费下标,服务端获取下标优先级由高到低:
- 服务器端按照格式:topic + TOPIC_GROUP_SEPARATOR + group 组装key从缓存中按照queueId获取消费下标
- 如果缓存下标大于等于0直接返回,否则继续
- 根据topic按照queueId获取队列中最小下标(偏移量为CQ_STORE_UNIT_SIZE=20倍数,所以获取时要除以偏移量)
- 如果下标小于等于0,并且下标0未交换至磁盘即仍然在内存中(此时消息量级很小),则返回下标0
- 否则,响应查询失败:QUERY_NOT_FOUND
- 客户端读取broker下标后处理逻辑
- 如果出现异常抛出MQBrokerException异常(含:QUERY_NOT_FOUND),返回下标-1
- 如果读取下标为-1则从broker队列的最大下标开始消费消息
因此,可能存在以下几种场景会消费历史消息(线上曾出现消费历史消息的问题,导致由于数据库历史数据已经迁移至历史表,查询历史消息的数据为空,产生大量线上异常)
- 持久化文件损坏或丢失,触发1.d步骤逻辑:${user.home}/store/consumequeue
- 新扩容的broker,消息还很少,并且新加了消费组,此时消费组获取上次消费下标返回-1,触发1.d步骤逻辑
- 新扩容的broker,消息还很少,并且新加了主题,所有消费组会受影响,因为新broker上新主题对应所有消费组没有上次的消费下标,返回-1,触发1.d步骤逻辑
一个JVM内增加消费者是否可以加速消费?
首先看下消费者实例注册逻辑:consumerTable.putIfAbsent(group, consumer),是基于group维度进行进行注册消费者,所以,如果仅仅单纯的增加消费者DefaultMQPushConsumer实例是不可行。还需要增加mQClientFactory实例才可以,增加mQClientFactory实例的方法则是需要变更实例名称或增加unitName即可
public String buildMQClientId() {
StringBuilder sb = new StringBuilder();
sb.append(this.getClientIP());
sb.append("@");
sb.append(this.getInstanceName());
if (!UtilAll.isBlank(this.unitName)) {
sb.append("@");
sb.append(this.unitName);
}
return sb.toString();
}
消费消息决策路径
- 获取所有消息队列,根据topic获取所有消息队列
- 选择broker,从NS拉取最新的topicRouteTable值本地并根据topic、group获取发生过变化(与本地broker路由数据比较,例如:重启时本地路由为空则需要刷新)的broker中随机选择一个
- 获取消费者ID列表,从broker根据消费组group获取所有消费者ID列表
- 选择消息队列,基于策略(默认为平均散列队列算法:AllocateMessageQueueAveragely),根据group、当前消费者ID(clientId)、所有消息队列、所有消费者ID列表,选择主题下一部分队列进行消费,尽量保证消费者组集群平均消费,而不是消费组中的一个热点消费者独自消费
消息队列
根据主题从NameServer获取所有消息队列