1. 概念

  Producer端重试:

  生产者端的消息失败,也就是Producer往MQ上发消息没有发送成功,比如网络抖动导致生产者发送消息到MQ失败。 
这种消息失败重试我们可以手动设置发送失败重试的次数。

  Consumer端重试:

  Consumer消费消息失败后,要提供一种重试机制,令消息再消费一次,Consumer消费消息失败通常可以认为有以下几种情况

  1.
由于消息本身的原因,例如反序列化失败,消息数据本身无法处理(例如话费充值,当前消息的手机被注销,无法充值)等。这种错误通常需要跳过这条消息,再消费其他消息,而且这条失败消息即使立刻重试消费,99%也不成功,所以最后提供一种定时的重试机制,即过10s再重试。

  2. 由于依赖下游应用服务不可用,例如db连接不可用,外系统网络不可达等。

  遇到这种错误,即使跳过当前失败的消息,消费其他消息也会报错,这种情况下建议应用sleep 30s,再消费下一条消息,这样可以减轻Broker重试消息的压力。

2. Broker消息重试策略

  查看broker.log文件,可以看到启动有很多的启动参数,其中有一条如下:

  这里就表示的是消息重试的时间,1s,5s....的间隔时间后再进行消息的重试,这里是消息消费的消息重试。

3. Producer端消息重试实现

package com.wangx.rocketmq.quickstart;

import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException; public class Producer { public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
DefaultMQProducer producer = new DefaultMQProducer("myGroup"); producer.setNamesrvAddr("localhost:9876"); producer.start();
     //重试三秒
producer.setRetryTimesWhenSendFailed(3);
for (int i = 0; i < 10; i++) {
Message message = new Message("MyTopic", "tabA", ("Hello World" + i).getBytes());
       //超时时间
SendResult result = producer.send(message,100);
System.out.println(result); }
producer.shutdown();
}
}

如果消息在100ms之内发送失败,就重试三次

4. Consumer使用方式

  在Consumer中,当消费消息的处理过程中,出现异常时,我们通常返回的是RECONSUME_LATER,表示一会儿之后再重试,当返回了这个状态之后,broker就会按照2中的时间间隔来重试消息。当然,最大也只能重试到2h.

  在实际的运用场景中,我们并不想要消息无止境的一直重试下去,可能我们回想要消息重试几次之后,还是不能成功的情况下就将这条消息存储到db或log文件中,所以此时我们可以这样实现:

package com.wangx.rocketmq.quickstart;

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt; import java.util.List;
public class Consumer { public static void main(String[] args) throws MQClientException {
final DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("MyConsumerGroup"); consumer.setNamesrvAddr("47.105.145.123:9876;47.105.149.61:9876");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
consumer.subscribe("MyTopic", "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
try {
MessageExt ext = msgs.get(0);
int x = 0;
String topic = ext.getTopic();
String body = new String(ext.getBody(),"utf-8");
if (Integer.parseInt(body) % 2 == 0) {
//产生异常
x = Integer.parseInt(body) / 0;
}
System.out.println("收到来自topic: " + topic + ",的消息:" + body);
} catch (Exception e) {
try {
MessageExt ext = msgs.get(0);
String topic = ext.getTopic();
String body = new String(ext.getBody(),"utf-8");
if (ext.getReconsumeTimes() == 3) { //模拟将消息保存到db或日志文件中,返回成功状态,使消息不再重试
System.out.println("保存成功消息:" + body + "成功!!!");
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
System.err.println("err:收到来自topic: " + topic + ",的消息:" + body);
} catch (Exception e1) {
e1.printStackTrace();
}
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
} });
consumer.start();
}
}

  控制台打印结果如下:

收到来自topic: MyTopic,的消息:1
err: 收到来自topic: MyTopic,的消息:2
err: 收到来自topic: MyTopic,的消息:6
err: 收到来自topic: MyTopic,的消息:2
err: 收到来自topic: MyTopic,的消息:6
收到来自topic: MyTopic,的消息:7
err: 收到来自topic: MyTopic,的消息:0
err: 收到来自topic: MyTopic,的消息:8
err: 收到来自topic: MyTopic,的消息:4
收到来自topic: MyTopic,的消息:3
收到来自topic: MyTopic,的消息:5
收到来自topic: MyTopic,的消息:9
err: 收到来自topic: MyTopic,的消息:0
err: 收到来自topic: MyTopic,的消息:8
err: 收到来自topic: MyTopic,的消息:4
err: 收到来自topic: MyTopic,的消息:2
err: 收到来自topic: MyTopic,的消息:6
err: 收到来自topic: MyTopic,的消息:0
err: 收到来自topic: MyTopic,的消息:8
err: 收到来自topic: MyTopic,的消息:4
保存成功消息:2成功!!!
保存成功消息:6成功!!!
保存成功消息:0成功!!!
保存成功消息:8成功!!!
保存成功消息:4成功!!

  可以看到奇数的时候,正常消费,当消息为偶数时,会抛出异常,此时返回的是RECONSUME_LATER,所以消息将会重试消费,在MessageExt中保存了一个属性叫reconsumeTimes,表示消息重试次数,我们这里使用当消息重试三次之后,模拟将消息保存到db或日志文件中的操作,然后返回CONSUME_SUCCESS,结束消息的重试。这样就可以保证消息出现异常时我们可以做适当的操作避免消息一直重试或对于消息无法消费情况做一些补偿操作。

原文 RocketMQ学习笔记(13)----RocketMQ的Consumer消息重试

RocketMQ学习笔记(13)----RocketMQ的Consumer消息重试的更多相关文章

  1. 【rocketmq学习笔记】rocketmq入门学习

    基本介绍 rocketmq是阿里巴巴团队使用java语言开发的一款基于发布订阅模型的分布式消息队列中间件,是一款低延迟,高可用,拥有海量消息堆积能力和灵活拓展性的消息队列. 特点 可以实现集群无单点故 ...

  2. RocketMQ学习笔记(15)----RocketMQ的消息模式

    在前面学习ActiveMQ时,看到ActiveMQ可以是队列消息模式,也可以是订阅发布模式. 同样,在RocketMQ中,也存在两种消息模式,即是集群消费模式和广播消费模式. 1. 集群消费模式 跟A ...

  3. RocketMQ 源码学习笔记————Producer 是怎么将消息发送至 Broker 的?

    目录 RocketMQ 源码学习笔记----Producer 是怎么将消息发送至 Broker 的? 前言 项目结构 rocketmq-client 模块 DefaultMQProducerTest ...

  4. RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的?

    目录 RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 前言 项目结构 rocketmq-client 模块 DefaultMQProducerTest Roc ...

  5. RocketMQ学习笔记(16)----RocketMQ搭建双主双从(异步复制)集群

    1. 修改RocketMQ默认启动端口 由于只有两台机器,部署双主双从需要四个节点,所以只能修改rocketmq的默认启动端口,从官网下载rocketmq的source文件,解压后使用idea打开,全 ...

  6. RocketMQ学习笔记(14)----RocketMQ的去重策略

    1. Exactly Only Once (1). 发送消息阶段,不允许发送重复的消息 (2). 消费消息阶段,不允许消费重复的消息. 只有以上两个条件都满足情况下,才能认为消息是“Exactly O ...

  7. RocketMQ学习笔记(4)----RocketMQ搭建双Master集群

    前面已经学习了RockeMQ的四种集群方式,接下来就来搭建一个双Master(2m)的集群环境. 1. 双Master服务器环境 序号 ip 用户名 密码 角色 模式 (1) 47.105.145.1 ...

  8. Ext.Net学习笔记13:Ext.Net GridPanel Sorter用法

    Ext.Net学习笔记13:Ext.Net GridPanel Sorter用法 这篇笔记将介绍如何使用Ext.Net GridPanel 中使用Sorter. 默认情况下,Ext.Net GridP ...

  9. 【Visual C++】游戏编程学习笔记之八:鼠标输入消息(小demo)

     本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder  微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.c ...

随机推荐

  1. 智能社区--HI3516C可视门禁研发出来咯

    铝壳.非常大气的外壳. 200W像素,HI3516C,携带server.创新的产品.欢迎交流:QQ237753582 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5u ...

  2. openstack (1)----- NTP 时间同步服务

    一.标准时间 1.地球分为东西十二个区域,共计24个时区 2.格林威治作为全球标准时间即(GMT时间),东时区以格林威治时区进行加,而西时区则进行减 3.地球的轨道并非正圆,在加上自传速度逐年递减,因 ...

  3. mysql查看所有存储过程,函数,视图,触发器,表

    查询数据库中的存储过程和函数 方法一: select `name` from mysql.proc where db = 'your_db_name' and `type` = 'PROCEDURE' ...

  4. EJB之JPA

    在前一篇文章中大概了解了EJB是什么?那么接下来就进一步介绍一下它与JPA有什么样的关系?及什么是JPA?JPA怎样用? 一.是什么? 第一次听说JPA是在EJB视屏中,所以一直感觉他们有不解的渊源. ...

  5. MySQL系列:innodb源代码分析之内存管理

    在innodb中实现了自己的内存池系统和内存堆分配系统,在innodb的内存管理系统中,大致分为三个部分:基础的内存块分配管理.内存伙伴分配器和内存堆分配器.innodb定义和实现内存池的主要目的是提 ...

  6. 经典面试题回答——学习Java基础的目的

    本系列知识解释:相信每个学习Java的人都是从JavaSE開始的,也就是Java基础開始. 可是却并不清楚学习Java基础究竟有什么用?        首先我来回答这个问题,学习Java基础是有两个目 ...

  7. msyql索引详解

    一.mysql查询表索引命令两种形式 1.mysql>SHOW INDEX FROM 'biaoming' 2.mysql>SHOW keys FROM 'biaoming' 运行结果如下 ...

  8. 【bzoj4602】[Sdoi2016]齿轮

    dfs,连边,边权为比值,赋值搜索,遇到矛盾时退出 #include<algorithm> #include<iostream> #include<cstdlib> ...

  9. YTU 2541: 汽水瓶

    2541: 汽水瓶 时间限制: 1 Sec  内存限制: 128 MB 提交: 40  解决: 27 题目描述 有这样一道智力题:"某商店规定:三个空汽水瓶可以换一瓶汽水.小张手上有十个空汽 ...

  10. 【Unity3D】Unity3D SkinnedMeshRenderer换装系统

    转载请注明出处:http://www.cnblogs.com/shamoyuu/p/6505561.html 一.换装原理 游戏角色换装分为以下几步: 1.替换蒙皮网格 2.刷新骨骼 3.替换材质 上 ...