【mq】从零开始实现 mq-10-消费者拉取消息回执 pull message ack
前景回顾
【mq】从零开始实现 mq-02-如何实现生产者调用消费者?
【mq】从零开始实现 mq-03-引入 broker 中间人
【mq】从零开始实现 mq-06-消费者心跳检测 heartbeat
【mq】从零开始实现 mq-07-负载均衡 load balance
【mq】从零开始实现 mq-09-消费者拉取消息 pull message
【mq】从零开始实现 mq-10-消费者拉取消息回执 pull message ack
状态回执
大家好,我是老马。
上一节我们只实现了拉取消息的实现,但是缺少了消费状态回执。
这一节我们一起来学习下如何实现状态回执。

代码实现
回执状态的设计
我们规定如下几种回执状态:
package com.github.houbb.mq.common.constant;
/**
 * @author binbin.hou
 * @since 0.0.3
 */
public final class MessageStatusConst {
    private MessageStatusConst(){}
    /**
     * 待消费
     * ps: 生产者推送到 broker 的初始化状态
     */
    public static final String WAIT_CONSUMER = "W";
    /**
     * 推送给消费端处理中
     * ps: broker 准备推送时,首先将状态更新为 P,等待推送结果
     * @since 0.1.0
     */
    public static final String TO_CONSUMER_PROCESS = "TCP";
    /**
     * 推送给消费端成功
     * @since 0.1.0
     */
    public static final String TO_CONSUMER_SUCCESS = "TCS";
    /**
     * 推送给消费端失败
     * @since 0.1.0
     */
    public static final String TO_CONSUMER_FAILED = "TCF";
    /**
     * 消费完成
     */
    public static final String CONSUMER_SUCCESS = "CS";
    /**
     * 消费失败
     */
    public static final String CONSUMER_FAILED = "CF";
    /**
     * 稍后消费
     * @since 0.1.0
     */
    public static final String CONSUMER_LATER = "CL";
}
消费者状态回执
我们在消费之后,添加状态回执:
for(MqMessage mqMessage : mqMessageList) {
    IMqConsumerListenerContext context = new MqConsumerListenerContext();
    final String messageId = mqMessage.getTraceId();
    ConsumerStatus consumerStatus = mqListenerService.consumer(mqMessage, context);
    log.info("消息:{} 消费结果 {}", messageId, consumerStatus);
    // 状态同步更新
    MqCommonResp ackResp = consumerBrokerService.consumerStatusAck(messageId, consumerStatus);
    log.info("消息:{} 状态回执结果 {}", messageId, JSON.toJSON(ackResp));
}
回执实现,根据 messageId 更新对应的消息消费状态。
public MqCommonResp consumerStatusAck(String messageId, ConsumerStatus consumerStatus) {
    final MqConsumerUpdateStatusReq req = new MqConsumerUpdateStatusReq();
    req.setMessageId(messageId);
    req.setMessageStatus(consumerStatus.getCode());
    final String traceId = IdHelper.uuid32();
    req.setTraceId(traceId);
    req.setMethodType(MethodType.C_CONSUMER_STATUS);
    // 重试
    return Retryer.<MqCommonResp>newInstance()
            .maxAttempt(consumerStatusMaxAttempt)
            .callable(new Callable<MqCommonResp>() {
                @Override
                public MqCommonResp call() throws Exception {
                    Channel channel = getChannel(null);
                    MqCommonResp resp = callServer(channel, req, MqCommonResp.class);
                    if(!MqCommonRespCode.SUCCESS.getCode().equals(resp.getRespCode())) {
                        throw new MqException(ConsumerRespCode.CONSUMER_STATUS_ACK_FAILED);
                    }
                    return resp;
                }
            }).retryCall();
}
Broker 回执处理
消息分发
// 消费者消费状态 ACK
if(MethodType.C_CONSUMER_STATUS.equals(methodType)) {
    MqConsumerUpdateStatusReq req = JSON.parseObject(json, MqConsumerUpdateStatusReq.class);
    final String messageId = req.getMessageId();
    final String messageStatus = req.getMessageStatus();
    return mqBrokerPersist.updateStatus(messageId, messageStatus);
}
简单实现
这里是基于本地 map 更新状态的,性能比较差。
后续会以 mysql 实现。
public MqCommonResp updateStatus(String messageId, String status) {
    // 这里性能比较差,所以不可以用于生产。仅作为测试验证
    for(List<MqMessagePersistPut> list : map.values()) {
        for(MqMessagePersistPut put : list) {
            MqMessage mqMessage = put.getMqMessage();
            if(mqMessage.getTraceId().equals(messageId)) {
                put.setMessageStatus(status);
                break;
            }
        }
    }
    MqCommonResp commonResp = new MqCommonResp();
    commonResp.setRespCode(MqCommonRespCode.SUCCESS.getCode());
    commonResp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg());
    return commonResp;
}
小结
对于消息状态的细化,更加便于我们后续的管理,和问题的定位。
希望本文对你有所帮助,如果喜欢,欢迎点赞收藏转发一波。
我是老马,期待与你的下次重逢。
开源地址
The message queue in java.(java 简易版本 mq 实现) https://github.com/houbb/mq
拓展阅读
rpc-从零开始实现 rpc https://github.com/houbb/rpc
【mq】从零开始实现 mq-10-消费者拉取消息回执 pull message ack的更多相关文章
- 【mq】从零开始实现 mq-09-消费者拉取消息 pull message
		前景回顾 [mq]从零开始实现 mq-01-生产者.消费者启动 [mq]从零开始实现 mq-02-如何实现生产者调用消费者? [mq]从零开始实现 mq-03-引入 broker 中间人 [mq]从零 ... 
- kafka 消费者拉取消息
		本文只跟踪消费者拉取消息的流程.对于 java 客户端, kafka 的生产者和消费者复用同一个网络 io 类 NetworkClient. 入口在 KafkaConsumer#pollOnce 中, ... 
- 【mq】从零开始实现 mq-11-消费者消息回执添加分组信息 pull message ack groupName
		前景回顾 [mq]从零开始实现 mq-01-生产者.消费者启动 [mq]从零开始实现 mq-02-如何实现生产者调用消费者? [mq]从零开始实现 mq-03-引入 broker 中间人 [mq]从零 ... 
- RocketMQ入门(3)拉取消息
		转自:http://www.changeself.net/archives/rocketmq入门(3)拉取消息.html RocketMQ入门(3)拉取消息 RocketMQ不止可以直接推送消息,在消 ... 
- git 拉取和获取 pull 和 fetch 区别
		使用Git 直接提交的话 直接 push 获取最新版本 有两种 拉取 和 获取 pull 和 fetch git pull 从远程拉取最新版本 到本地 自动合并 merge ... 
- RocketMQ 拉取消息-文件获取
		看完了上一篇的<RocketMQ 拉取消息-通信模块>,请求进入PullMessageProcessor中,接着 PullMessageProcessor.processRequest(f ... 
- git 拉取和获取 pull 和 fetch 区别【转】
		本文转载自:http://blog.csdn.net/u010094934/article/details/52775653 使用git 直接提交的话 直接 push 获取最新版本 有两种 ... 
- Kafka消费者拉取数据异常Unexpected error code 2 while fetching data
		Kafka消费程序间歇性报同一个错: 上网没查到相关资料,只好自己分析.通过进一步分析日志发现,只有在拉取某一个特定的topic的数据时报错,如果拉取其他topic的数据则不会报错.而从这个异常信息来 ... 
- Linux 搭建git 自己拉取本地 git pull,其他地方的git仓库拉取代码
		Linux 下建立 Git 与 GitHub 的连接 Git 是一款开源的分布式版本控制系统,而 GitHub 是依托 Git 的代码托管平台. GitHub 利用 Git 极其强大的克隆和分支功能, ... 
随机推荐
- 错误问题之“Apache  Log4j 漏洞,在版本为包含2.14以内!”
			漏洞概述 Apache Log4j是一个用于Java的日志记录库,其支持启动远程日志服务器. Log4j 1.2 中包含一个 SocketServer 类,该类容易受到不可信数据反序列化的影响,当侦听 ... 
- 使用滑模控制对sin(t)曲线追踪
			结合:[Matlab]简单的滑模控制程序及Simulink仿真本片文章观看,此篇文章是在这篇文章的基础上进行修改的 输出u的推导过程 如果不明白控制量输出u的推到过成请看:[控制理论]滑模控制最强解析 ... 
- 【转】ng-class的用法
			原文出处:https://segmentfault.com/a/11... 在开发中我们通常会遇到一种需求:一个元素在不同的状态需要展现不同的样子. 而在这所谓的样子当然就是改变其css的属性,而实现 ... 
- java中异常到底有什么用?举例
			异常的意义:马克-to-win:通过上面的例子,我们看出通过引入异常这种技术,即使出现不测(用户把0赋给除数),也可以让程序不崩溃,还能继续优雅 的运行.那,这种技术有用,值得学.马克-to-win: ... 
- Python使用递归绘制谢尔宾斯基三角形
			谢尔宾斯基三角形使用了三路递归算法,从一个大三角形开始,通过连接每一个边的中点,将大三角型分为四个三角形,然后忽略中间的三角形,依次对其余三个三角形执行上述操作. 运行效果: 源代码: 1 impor ... 
- Blazor组件自做七 : 使用JS隔离制作定位/持续定位组件
			1. 运行截图 演示地址 2. 在文件夹wwwroot/lib,添加geolocation子文件夹,添加geolocation.js文件 本组件主要是调用浏览器两个API实现基于浏览器的定位功能,现代 ... 
- Vue入坑日记: day - 01
			前言 最近做了一些小项目,小组里写前端的确实有点拉胯,于是自己动手写前端,因为大一学过web前端基础,所以对HTML,CSS还有印象,就直接对JS下手了,学了两天把JS大致搞明白了,顺便对JQuery ... 
- kafka快速入门到精通
			目录 1. 消息队列两种模式 1.1 消息队列作用 1.2 点对点模式(一对一,消费者主动拉取数据,消息收到后消息删除) 1.3 发布/订阅模式(一对多,消费数据之后不会删除消息) 1.4 kafka ... 
- Caused by: com.sonatype.nexus.staging.client.StagingRuleFailuresException: Staging rules failure! 已解决!
			问题分析 由于项目中修改了一些代码,然后没有修改版本号,直接deploy代码到仓库,最终导致错误! 根据 https://central.sonatype.org/faq/can-i-change-a ... 
- JQuery学习基础
			## 今日内容 1. JQuery 基础: 1. 概念 2. 快速入门 3. JQuery对象和JS对象区别与转换 4. 选择器 ... 
