前景回顾

【mq】从零开始实现 mq-01-生产者、消费者启动

【mq】从零开始实现 mq-02-如何实现生产者调用消费者?

【mq】从零开始实现 mq-03-引入 broker 中间人

【mq】从零开始实现 mq-04-启动检测与实现优化

【mq】从零开始实现 mq-05-实现优雅停机

【mq】从零开始实现 mq-06-消费者心跳检测 heartbeat

【mq】从零开始实现 mq-07-负载均衡 load balance

【mq】从零开始实现 mq-08-配置优化 fluent

【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的更多相关文章

  1. 【mq】从零开始实现 mq-09-消费者拉取消息 pull message

    前景回顾 [mq]从零开始实现 mq-01-生产者.消费者启动 [mq]从零开始实现 mq-02-如何实现生产者调用消费者? [mq]从零开始实现 mq-03-引入 broker 中间人 [mq]从零 ...

  2. kafka 消费者拉取消息

    本文只跟踪消费者拉取消息的流程.对于 java 客户端, kafka 的生产者和消费者复用同一个网络 io 类 NetworkClient. 入口在 KafkaConsumer#pollOnce 中, ...

  3. 【mq】从零开始实现 mq-11-消费者消息回执添加分组信息 pull message ack groupName

    前景回顾 [mq]从零开始实现 mq-01-生产者.消费者启动 [mq]从零开始实现 mq-02-如何实现生产者调用消费者? [mq]从零开始实现 mq-03-引入 broker 中间人 [mq]从零 ...

  4. RocketMQ入门(3)拉取消息

    转自:http://www.changeself.net/archives/rocketmq入门(3)拉取消息.html RocketMQ入门(3)拉取消息 RocketMQ不止可以直接推送消息,在消 ...

  5. git 拉取和获取 pull 和 fetch 区别

    使用Git  直接提交的话   直接 push 获取最新版本  有两种  拉取 和 获取 pull 和 fetch git  pull     从远程拉取最新版本 到本地  自动合并 merge   ...

  6. RocketMQ 拉取消息-文件获取

    看完了上一篇的<RocketMQ 拉取消息-通信模块>,请求进入PullMessageProcessor中,接着 PullMessageProcessor.processRequest(f ...

  7. git 拉取和获取 pull 和 fetch 区别【转】

    本文转载自:http://blog.csdn.net/u010094934/article/details/52775653 使用git  直接提交的话   直接 push 获取最新版本  有两种   ...

  8. Kafka消费者拉取数据异常Unexpected error code 2 while fetching data

    Kafka消费程序间歇性报同一个错: 上网没查到相关资料,只好自己分析.通过进一步分析日志发现,只有在拉取某一个特定的topic的数据时报错,如果拉取其他topic的数据则不会报错.而从这个异常信息来 ...

  9. Linux 搭建git 自己拉取本地 git pull,其他地方的git仓库拉取代码

    Linux 下建立 Git 与 GitHub 的连接 Git 是一款开源的分布式版本控制系统,而 GitHub 是依托 Git 的代码托管平台. GitHub 利用 Git 极其强大的克隆和分支功能, ...

随机推荐

  1. 查看mysql相关信息

    查看本机mysql的相关信息,执行以下SQL即可: SHOW VARIABLES LIKE "%char%";

  2. NetCore微服务实现事务一致性masstransit之saga使用

    demo如下,一个订单处理的小例子: 首先看看结果很简单: 核心代码如下: using MassTransit; using Microsoft.Extensions.DependencyInject ...

  3. Vue报错之" [Vue warn]: Unknown custom element: <wzwzihello> - did you register the component correctly? For recursive components, make sure to provide the "name" option."

    一.报错截图 [Vue warn]: Unknown custom element: <wzwzihello> - did you register the component corre ...

  4. MySQL怎么用命令修改字段长度

    MySQL怎么用命令修改double字段长度 1 alter table 表名 modify column 列名 类型(要修改的长度) COMMENT 备注信息; 2 alter table t_ov ...

  5. 面试题目:手写一个LRU算法实现

    一.常见的内存淘汰算法 FIFO  先进先出 在这种淘汰算法中,先进⼊缓存的会先被淘汰 命中率很低 LRU Least recently used,最近最少使⽤get 根据数据的历史访问记录来进⾏淘汰 ...

  6. Flex 布局新旧混合写法详解(兼容微信)

    flex 是个非常好用的属性,如果说有什么可以完全代替 float 和 position ,那么肯定是非它莫属了(虽然现在还有很多不支持 flex 的浏览器).然而国内很多浏览器对 flex 的支持都 ...

  7. 深入理解ES6之《扩展对象》

    属性初始值的简写 当对象字面量只有一个属性的名称时,JS引擎会在可访问作用域中查找其同名变量:如果找到则该变量的值被赋给对象字面量里的同名属性 function createPerson(name, ...

  8. HTML5 Audio & Video 属性解析

    一.HTML 音频/视频 方法 play() play() 方法开始播放当前的音频或视频. var myVideo=document.getElementById("video1" ...

  9. 阿里云-部署-服务-Docker

    目录 ♫ MusicPlayer Naiveboom - 比较安全 个人阿里云部署的小服务,欢迎使用,服务器资源有限,如果遇到卡顿还请谅解~ 索引: 在线音乐播放器 阅后即焚 ♫ MusicPlaye ...

  10. Redis 缓存击穿(失效)、缓存穿透、缓存雪崩怎么解决?

    原始数据存储在 DB 中(如 MySQL.Hbase 等),但 DB 的读写性能低.延迟高. 比如 MySQL 在 4 核 8G 上的 TPS = 5000,QPS = 10000 左右,读写平均耗时 ...