【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 极其强大的克隆和分支功能, ...
随机推荐
- 学习openldap02
III (二十二)OpenLDAP 目录服务: 目录是一类为了浏览和搜索数据而设计的特殊的数据库,目录服务是按照树状形式存储信息,目录包含基于属性的描述性信息,并且支持高级的过滤功能,如microso ...
- 算法导论 - 基础知识 - 算法基础(插入排序&归并排序)
在<算法导论>一书中,插入排序作为一个例子是第一个出现在该书中的算法. 插入排序: 对于少量元素的排序,它是一个有效的算法. 插入排序的工作方式像许多人排序一手扑克牌.开始时,我们手中牌为 ...
- python学习笔记(十)——进程间通信
python 在进程间通信时有很多方式,比如使用Queue的消息队列,使用 pip的管道通信,share memory 共享内存或 semaphore 信号量等通信方式. 这里我们演示一下通过消息队列 ...
- transformjs 污染了 DOM?是你不了解它的强大
原文链接:https://github.com/AlloyTeam/AlloyTouch/wiki/Powerful-transformjs 写在前面 上星期在React微信群里,有小伙伴觉得tran ...
- 解决HDFS无法启动namenode,报错Premature EOF from inputStream;Failed to load FSImage file, see error(s) above for more info
一.情况描述 启动hadoop后发现无法打开hdfs web界面,50070打不开,于是jps发现少了一个namenode: 查看日志信息,发现如下报错: 2022-01-03 23:54:10,99 ...
- 【Android开发】LogcatView,手机中查看logcat神器
先上图 集成: 1, allprojects { repositories { ... maven { url 'https://www.jitpack.io' } } } 2, dependenci ...
- java中请给一个Abstract类实现接口的实例!
2.Abstract类实现接口 马克-to-win:如果实现某接口的类是abstract类,则它可以不实现该接口所有的方法.但其非abstract的子类中必须拥有所有抽象方法的实在的方法体:(当然它a ...
- 安卓性能测试之 adb shell 常用命令
pm list packages 列出包名adb shell pm list packages:列出所有的包名.adb shell dumpsys package:列出所有的安装应用的信息adb sh ...
- jsp笔记---标签
<meta>标签 <meta> 标签提供了 HTML 文档的元数据.元数据不会显示在客户端,但是会被浏览器解析. META元素通常用于指定网页的描述,关键词,文件的最后修改时间 ...
- vue实现省市区三级联动
npm 安装 npm install v-distpicker --save Vue全局引入组件 import Distpicker from 'v-distpicker' Vue.component ...