在https://www.cnblogs.com/lccsblog/p/12249265.html中,PullMessageService负责对消息队列进行消息拉取,从远端服务器拉取消息后将消息存入ProcessQueue消息队列处理队列中,然后调用ConsumeMessageService#submitConsumeRequest方法进行消息消费,使用线程池来消费消息,确保了消息拉取于消息消费的解偶。

public interface ConsumeMessageService {
void start(); void shutdown(); void updateCorePoolSize(int corePoolSize); void incCorePoolSize(); void decCorePoolSize(); int getCorePoolSize(); ConsumeMessageDirectlyResult consumeMessageDirectly(final MessageExt msg, final String brokerName);//直接消费消息,主要用于通过管理命令收到消费消息

  //提交消息消费
void submitConsumeRequest(
final List<MessageExt> msgs,
final ProcessQueue processQueue,
final MessageQueue messageQueue,
final boolean dispathToConsume);
}
public class ConsumeMessageConcurrentlyService implements ConsumeMessageService {
private static final InternalLogger log = ClientLogger.getLog();
private final DefaultMQPushConsumerImpl defaultMQPushConsumerImpl;//消息推模式实现类
private final DefaultMQPushConsumer defaultMQPushConsumer;//消费者对象
private final MessageListenerConcurrently messageListener;//并发消息业务事件类
private final BlockingQueue<Runnable> consumeRequestQueue;//消息消费任务队列
private final ThreadPoolExecutor consumeExecutor;//消息消费线程池
private final String consumerGroup;//消费组 private final ScheduledExecutorService scheduledExecutorService;//添加消费任务到consumeExecutor延迟调度器
private final ScheduledExecutorService cleanExpireMsgExecutors;//定时删除过期消息线程池

ConsumeMessageConcurrentlyService#submitConsumeRequest

拉取的消息条数大于consumeMessageBatchMaxSize,则分页,ConsumeRequest的run方法封装了具体消息消费逻辑:

检查队列是否丢弃,执行消息消费钩子函数ConsumeMessage,executeHookBefore将consumeMessageContext放到一个ArrayBlockingQueue中,有个线程在消费:

看一下DefaultMQPushConsumerImpl#resetRetryAndNamespace

恢复重试消息主题名?这是由消息重试机制决定的,mq江消息存入commitlog文件时,如果发现消息的延时级别delayTimeLevel大于0,会首先将重试主题存入在消息的属性中,然后设置主题名称为SCHEDULE_TOPIC,以便时间到后重新参与消息消费。

ConsumeMessageConcurrentlyService.ConsumeRequest#run

具体的消息消费,调用应用程序消息监听器的consumeMessage方法

执行钩子函数

在处理结果前再次验证一下ProcessQueue的isDroped状态值,如果设置为true,将不对结果进行处理。

ConsumeMessageConcurrentlyService#processConsumeResult:

根据消息监听器返回的结果,计算ackIndex,如果返回CONSUME_SUCCESS,ackIndex设置为msg.szie()-1,如果返回RECONSUME_LATER,ackIndex=-1,为下文发送msgbak消息做准备。

参考上面ackIndex的值,广播模式下,如果消费失败,则只打印日志,如果成功则不进入循环。

集群模式下,如果消费成功,不会进行sendMessageBack,如果失败,该批消息都需要重新发回broker,如果消息发送失败,则直接将本批ack消息发送失败的消息再次封装为ConsumeRequest,然后延迟5s后重新消费。如果ack消息发送成功,该消息会“延迟消费”?。

从ProcessQueue中移除这批消息,返回的偏移量是移除该批消息后最小的偏移量,保证了消息不会丢失但可能会重复消费

这一点不是很好理解我们仔细看下代码:

其实是只有当整批消息全部remove掉之后,才会返回这批消息的最大偏移量,这就出现当出现consumerlater并且sendMessageback失败时,整个批次的消息消费进度都不会往前推,并且如果某个消息出现了死循环,整批消息消费进度都会卡住。但是不影响外层pullRequest回到池子里,后面会发生什么??

然后用该偏移量更新消息消费进度,以便在消费者重启后能从上一次的消费进度开始消费,避免重复消息。

当消息监听器返回RECONSUME_LATER,消息消费进度也会向前推进,这是因为当返回Reconsume_LATER,RocketMQ会创建一条与原先消息属性相同的消息,拥有一个唯一的新msgId,并存储原消息ID,改消息会存入到commitlog文件中,与原先的消息没有任何关联,那该消息当然也会进入到ConsumemeQueue队列中,将拥有一个全新的队列偏移量。

另外这种批量拉取也带来一个问题,消息的消费进度也是批量更新的,并且是从内存异步提交到broker的若是消费者意外下线,没有提交的一批消息就会被新分配的consumer全部被重复消费。为此在流控那里做了maxspan的限制,来减小这种影响,还有其他措施,但是不能杜绝重复消费,因此幂等是必要的 更详细的部分参考:

https://blog.csdn.net/guolong1983811/article/details/78821913

【mq读书笔记】消息消费过程(钩子 失败重试 消费偏移记录)的更多相关文章

  1. 【mq读书笔记】消息确认(失败消息,定时队列重新消费)

    接上文的集群模式,监听器返回RECONSUME_LATER,需要将将这些消息发送给Broker延迟消息.如果发送ack消息失败,将延迟5s后提交线程池进行消费. 入口:ConsumeMessageCo ...

  2. 【mq读书笔记】mq消息消费

    消息消费以组的的模式开展: 一个消费组内可以包含多个消费者,每一个消费组可订阅多个主题: 消费组之间有集群模式与广播模式两种消费模式:集群模式-主题下的同一条消息只允许被其中一个消费者消费.广播模式- ...

  3. 【mq读书笔记】顺序消息

    注意异常情况导致整个消费无限重试 阻塞消费 mq支持局部消息顺序消费,可以确保同一个消息消费队列中的消息被顺序消费.看下针对顺序消息在整个消费过程中做的调整: 队列负载: DefaultMQPushC ...

  4. 【mq读书笔记】mq事务消息

    关于mq食物以什么样的方式解决了什么样的问题可以参考这里: https://www.jianshu.com/p/cc5c10221aa1 上文中示例基于mq版本较低较新的版本中TransactionL ...

  5. 【mq读书笔记】消费进度管理

    从前2节可以看到,一次消费后消息会从ProcessQueue处理队列中移除该批消息,返回ProcessQueue最小偏移量,并存入消息进度表中.那消息进度文件存储在哪合适呢? 广播模式:同一个消费组的 ...

  6. 【mq读书笔记】mq消息存储

    comitlog文件 ConsumerQueue文件 IndexFile文件 RocketMQ将所有主题的消息存储在同一个文件中,确保消息发送时顺序写文件. 为了提高消息消费的效率RocketMQ引入 ...

  7. 【mq读书笔记】消息消费队列和索引文件的更新

    ConsumeQueue,IndexFile需要及时更新,否则无法及时被消费,根据消息属性查找消息也会出现较大延迟. mq通过开启一个线程ReputMessageService来准时转发commitL ...

  8. 【mq读书笔记】消息队列负载与重新分配(分配 新队列pullRequest入队)

    回顾PullMessageService#run: 如果队列总没有PullRequest对象,线程将阻塞. 围绕PullRequest有2个问题: 1.PullRequest对象在什么时候创建并加入p ...

  9. 【mq读书笔记】消息拉取

    疑问:PullRequest何时添加? PullMessageService提供延迟添加与立即添加2种方式 疑问:PullRequest是在什么时候创建的呢? 1.上上图中 PullRequest p ...

随机推荐

  1. CSS动画之转换模块

    2D转换模块:注意点:1.可以类似于过渡模块一样简写,但是这里不是用逗号隔开而是用空格 2.2D的转换模块会修改元素的坐标系,所以旋转之后的平移就不是水平平移 格式:旋转:transform: rot ...

  2. 手写Express.js源码

    上一篇文章我们讲了怎么用Node.js原生API来写一个web服务器,虽然代码比较丑,但是基本功能还是有的.但是一般我们不会直接用原生API来写,而是借助框架来做,比如本文要讲的Express.通过上 ...

  3. 撸个反向代理,激活JRebel~

    持续原创输出,点击上方蓝字关注我 目录 前言 本地反向代理 服务器反向代理[个人推荐] IDEA安装JRebel并激活 服务器安装JRebel并激活 总结 前言 热部署相信大家都听说过,比如Sprin ...

  4. 【Luogu】P1436 棋盘分割 题解

    嗯,点开题目,哇!是一道闪亮亮的蓝题! 不要被吓到了,其实,这道题就是一个简单的DP啦! 我们设 \(f[x1][y1][x2][y2][c]\) 为以 \((x1,y1)\) 为左上角,以 \((x ...

  5. C#3新增语法特性

    C#3,.Net Framework 3.5 ,Visual Studio 2008, CLR 3.0 C#3.0新引进的语法基于.Net Framework 3.5.主要引进的语法:Linq,隐式类 ...

  6. linux-挂载NFS网络文件系统教程

    目录 前言 链接 参考 笔录草稿 NFS环境搭建 前言 本文实现需要联网 链接 野火NFS介绍 NFS详细介绍 NFS简要介绍 参考 上面链接 笔录草稿 NFS环境搭建 一些目标配置 服务主机共享目录 ...

  7. odbc。INI配置

    [ODBC Data Sources] ST = OSCAR ODBC DRIVER [ST] Driver = /opt/ShenTong/odbc/lib/liboscarodbcw.so Ser ...

  8. python的各版本的不同

    Python的版本主要分为 2.× . 3.× 两个系列. Python3计划每年发布一个新的子版本,一次只增加一两种新语法. 使用时当然选择越新的Python版本越好,版本越老的代码越难维护. 维护 ...

  9. CSharpFlink分布式实时计算,OutOfMemoryException异常,你意想不到的原因。

    目录 一.测试过程及问题 二.问题排查及分析过程 三.问题分析及解决过程 四.问题解决初步结果 一.测试过程及问题 从昨天15点左右开始测试,1个主节点,10个计算节点,1000个数据点,每个数据点3 ...

  10. 手把手教你使用rpm部署ceph集群

    环境准备 1.在运行 Ceph 守护进程的节点上创建一个普通用户,ceph-deploy 会在节点安装软件包,所以你创建的用户需要无密码 sudo 权限.如果使用root可以忽略. 为赋予用户所有权限 ...