1、Producer使用指南--发送消息注意事项

1、正常情况下一个业务系统尽可能用一个Topic,消息子类型用tags来标识,tags可以由业务系统自由设置。只有发送消息设置了tags,消费方在订阅消息时,才可以利用tags在broker做消息过滤。

MQCPMessage msg = new MQCPMessage(); // 初始化消息对象

message.setTags("TagA"); // 设置消息TAG

2、每个消息在业务层面的唯一标识码,要设置到keys字段,方便将来定位消息丢失问题。服务器会为每个消息创建索引(哈希索引),应用可以通过topic,key来查询这条消息内容,以及消息被谁消费。由于是哈希索引,请务必保证key尽可能唯一,这样可以避免潜在的哈希冲突。

MQCPMessage msg = new MQCPMessage(); // 初始化消息对象

message.setTags("TagA"); // 设置消息TAG

String orderId = "20034568923546"; // 订单Id

message.setKeys(orderId);

3、消息发送成功或者失败,要打印消息日志,务必要打印sendresult和key字段。

4、send消息方法,只要不抛异常,就代表发送成功。但是发送成功会有多个状态,在sendResult里定义

5、对于消息不可丢失应用,务必要有消息重发机制。例如如果消息发送失败,存储到数据库,能有定时程序尝试重发,或者人工触发重发。

2、Consumer使用指南--消费端去重

RocketMQ无法避免消息重复,所以如果业务对消费重复非常敏感,务必要在业务层面去重,有以下几种去重方式:

1. 将消息的唯一键,可以是msgID,也可以是消息内容中的唯一标识字段,例如订单Id等,消费之前判断是否在Db或Tair(全局KV存储)中存在,如果不存在则插入,并消费,否则跳过。(实际过程要考虑原子性问题,判断是否存在可以尝试插入,如果报主键冲突,则插入失败,直接跳过)msgId一定是全局唯一标识符,但是可能会存在同样的消息有两个不同msgId的情况(有多种原因),这种情况可能会使业务上重复消费,建议最好使用消息内容中的唯一标识字段去重。

2. 使用业务层面的状态机去重

5、如何判断发送消息是否成功?

客户端Producer调用send消息方法,只要不抛异常,就代表发送成功。但是发送成功会有多个状态,在sendResult里定义。

返回状态

状态释义

SEND_OK

消息发送成功

FLUSH_DISK_TIMEOUT

消息发送成功,但是服务器刷盘超时,消息已经进入服务器队列,只有此时MASTER服务器宕机,消息才会丢失

FLUSH_SLAVE_TIMEOUT

消息发送成功,但是服务器同步到Slave时超时,消息已经进入服务器队列,只有此时SLAVE服务器宕机,消息才会丢失

SLAVE_NOT_AVAILABLE

消息发送成功,但是此时SLAVE不可用,消息已经进入服务器队列,只有此时SLAVE服务器宕机,消息才会丢失

目前MQCP测试和生产环境集群都采用两主两从共4台Broker机器,针对大部分业务系统来讲,只要MQCP没有抛出异常,可以默认消息已成功发送。建议业务系统针对发送消息后所有非SEND_OK状态的消息,打印Warning日志,并在运营端设置对应的监控规则,及时发邮件提醒。

6、如何判断消费消息是否成功?

客户端Consumer在MQCPMessageListener中实现pushMessage(),遍历并处理消息后会返回给MQCP端消费的状态,状态只有消费成功或者消费失败两种状态。

消费状态

状态释义

CONSUME_OK

消费成功

CONSUME_FAIL

消费失败

7、消费端如何实现定时消费?

在某些业务场景下,消费端希望在业务低峰(例如半夜12点后)时开始从MQCP拉取消息,在业务高峰期前关闭掉消费功能,以此来降低系统负载。这种类似场景涉及到如何在不停业务服务的场景下,多次的开启和关闭MQCP消费服务。

MQCP的消费者本身是可以多实例初始化的,每个实例的消费者服务开启和关闭也是独立的,所以可以很良好的支持定时消费的场景。

如果业务系统有类似的需求,我们建议:

1. 业务系统本身需要添加功能开关,支持配置化的方式来开启或关闭消费服务。其实现本身比较简单,就是调用MQCP消费者的start || shutdown方法。必要时需要对方法添加上层逻辑封装,来实现定制化的需求。

2. 调用完consumer对象的shutdown方法后,不要立即初始化下一个consumer对象并启用服务,建议至少延迟几秒种,等相关的资源回收完毕。

3. 完善业务端开启或关闭消息服务的日志,方便后续运维处理问题。

4. 具体实现方式,可以参考下面TestTimedConsumeImpl类。

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties; import com.paic.mqcp.client.MQCPFactory;
import com.paic.mqcp.client.common.MQCPException;
import com.paic.mqcp.client.common.MQCPMessageListener;
import com.paic.mqcp.client.consumer.MQCPConsumer;
import com.paic.mqcp.client.dto.MQCPMessage;
import com.paic.mqcp.client.dto.MQCPMessageFilter;
import com.paic.mqcp.client.util.MQCPConstant;
import com.paic.mqcp.client.util.MQCPConsumeStatus;
import com.paic.mqcp.common.consumer.ConsumeFromWhere; /**
* 模拟业务系统需要在不停应用的场景下,开启、关闭接受消息服务
*
* @author WUJING754
*
*/
public class TestTimedConsumeImpl { /** 属性对象 */
private static Properties p = null; /**
* 获取属性值对象
*
* @return Properties
*/
public static Properties initialProperties() { p = new Properties();
// 业务系统可以从配置文件中取出该属性,demo中写死的
p.setProperty(MQCPConstant.NAME_SERVER_ADDRESS,
"10.20.22.148:9876;10.20.22.149:9876");
// 消费者ID,业务系统需要在MQCP-ADMIN中注册,否则无法正常发送消息
// demo中已经初始化好了的,后续请联系MQCP开发注册系统
p.setProperty(MQCPConstant.CONSUMER_ID, "CID_PARP_TEST_DEFAULT");
// INSTANCE_NAME属性建议业务系统不设置,用默认的即可
//p.setProperty(MQCPConstant.INSTANCE_NAME,"_PACP"); return p;
} public static MQCPConsumer initialConsumer(){ MQCPConsumer pushConsumer = null; try {
// 初始化过滤对象
MQCPMessageFilter mqcpFilter = new MQCPMessageFilter();
List<String> list = new ArrayList<String>();
// 设置tag
// tag的作用是过滤消息,如果设置改值,MQCP只会取出发送消息时设置了该tag值的消息
// 需要注意的是同一CID下的多个应用实例需要设置为同样的tag列表来过滤消息,以保证消息不会被过滤取走但未被业务系统处理
list.add("testByWUJING754");
mqcpFilter.setTags(list);
// 初始化MQCPPushConsumer并指定为集群消费(消息只会被消费一次,无论应用的实例有多少个)
// PUSH消费模式下,客户端包会启动后台线程不断的从MQCP中拉去消息(准实时方式,毫秒级延时)
// 业务系统收取到消息后,需要实现MQCPMessageListener,来处理消息
pushConsumer = MQCPFactory
.createConsumer(initialProperties());
// 设置第一次CID消费消息的时间点,这里设置从最后的消息开始获取
// 如果不设置该值,默认是队列的最开始出消费消息
pushConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
// 订阅 T_PAFA5_LOG_P 主题的消息(创建topic需要在MQCP-ADMIN中,如需创建请联系MQCP开发)
pushConsumer.subscribe("T_PARP_TEST_DEMO", mqcpFilter,
new MQCPMessageListener() {
@Override
public MQCPConsumeStatus pushMessage(
List<MQCPMessage> messageList) {
// 监听到有消息抵达后,业务系统需要遍历messageList对象,来获取消息
// messageList的默认大小为1,即消息是一条一条的推送到客户端的
for (MQCPMessage msg : messageList) {
// 消息明细 MQCPMessage对象
// 建议业务系统将从消息平台拉取消息和处理消息的逻辑解耦,
// 在consumer监听器中只监听到消息,建议简单的将获取的消息解析存储,然后返回 MQCPConsumeStatus.CONSUME_OK,
// 后端可以异步来处理接收到的消息
try {
System.out.println("###########receive message\n[msgTopic:"
+ msg.getTopic()
+ "\nmsgId:"
+ msg.getMsgId()
+ "\nmsgContent:"
+ new String(msg.getConent(),"UTF-8")
+ "\nmsgKey:"
+ msg.getKey() + "]");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} return MQCPConsumeStatus.CONSUME_OK;
}
}); } catch (Exception ex) {
ex.printStackTrace();
}
return pushConsumer; } /**
* 入口
* @param args
*/
public static void main(String[] args) { // 初始化一个pushConsumer对象
MQCPConsumer pushConsumer = initialConsumer(); // 启动该consumer对象,开始消费消息
startConsumingMsg(pushConsumer); // 业务系统不断收取消息,处理消息过程中... // 根据业务需要,可以调用stopConsumingMsg()方法来停止收取消息
stopConsumingMsg(pushConsumer); // 主线程休眠30秒
try {
Thread.sleep(30000L);
} catch (InterruptedException e) { e.printStackTrace();
}
// 模拟业务系统再次初始化一个consumer对象
pushConsumer = initialConsumer();
// 启动consumer,开始收取消息
startConsumingMsg(pushConsumer); // 业务系统不断收取消息,处理消息过程中... // 业务系统根据需要,停止取消息服务
stopConsumingMsg(pushConsumer); }
/*
* 启动传入的消费者对象
*/
public static boolean startConsumingMsg(final MQCPConsumer pushConsumer){ boolean flag = true;
try {
pushConsumer.start();
System.out.println("-->start consumer success.");
} catch (MQCPException e) { System.out.println("-->start consumer fail due to " + e.getMessage());
e.printStackTrace();
flag = false;
}
return flag;
} /*
* 关闭传入的消费者对象
*/
public static void stopConsumingMsg(final MQCPConsumer pushConsumer){ pushConsumer.shutdown();
System.out.println("-->shutdown consumer success.");
} }

9、客户端生产者是否有消息重发机制?

(摘至RocketMq官方文档)
RocketMq消息发送失败如何处理,Producer的send方法本身支持内部重试,重试逻辑如下:
  1. 至多重试5次。
  2. 如果发送失败,则轮转到下一个Broker。
  3. 这个方法的总耗时时间不超过sendMsgTimeout设置的值,默认5s。
所以,如果本身向broker发送消息产生超时异常,就不会再做重试。

以上策略仍然不能保证消息一定发送成功,为保证消息一定成功,建议应用这样做:如果调用send同步方法发送失败,则尝试将消息存储到db,由后台线程定时重试,保证消息一定到达Broker。

上述DB重试方式为什么没有集成到MQ客户端内部做,而是要求应用自己去完成,我们基于以下几点考虑 
    1. MQ的客户端设计为无状态模式,方便任意的水平扩展,且对机器资源的消耗仅仅是cpu、内存、网络。
    2. 如果MQ客户端内部集成一个KV存储模块,那么数据只有同步落盘才能较可靠,而同步落盘本身性能开销较大,所以通常会采用           异步落盘,又由于应用关闭过程不受MQ运维人员控制,可能经常会发生kill -9这样暴力方式关闭,造成数据没有及时落盘而丢           失。
    3. Producer所在机器的可靠性较低,一般为虚拟机,不适合存储重要数据。
      综上,建议重试过程交由应用来控制。

10、客户端消费者是否有消息重发机制?

(摘至RocketMq官方文档)
消息重试机制如下:

注意:重试的消息,MsgKey不变,MsgId会变。

11、业务消费端没有取到消息,如何去定位问题?

正常情况下生产者发送消息到MQCP,消息被投递到消费端的延时应该在毫秒级。如果消费端迟迟没有收到消息,建议采用下面的步骤来排查问题:

1、 获得消息的消息ID或者KEY,去MQCP-ADMIN的消息查询模块,根据自己的消费者ID找到其对应的消费状态。

2、 常见的投递状态有:

SUBSCRIBED_AND_CONSUMED

订阅了,而且消费了(Offset越过了)

SUBSCRIBED_BUT_FILTERD

订阅了,但是被过滤掉了

the consumer group[***] not online

订阅了,但是消费者未启动

SUBSCRIBED_AND_NOT_CONSUME_YET

订阅了,但是没有消费(Offset小)

UNKNOW_EXCEPTION

未知异常

注: SUBSCRIBED_AND_CONSUMED状态,表示消息已被正常消费掉,如果此时有异常,需要业务系统检查日志,分析看看是否因为解析消息时有异常,导致消息未被正确处理。
     SUBSCRIBED_BUT_FILTERD状态,需要业务系统检查初始化Consumer对象时传入的TagList是否和生产者定义的tag匹配。
     SUBSCRIBED_AND_NOT_CONSUME_YET状态,可能的原因是由于消息有积压,消息还未被取走,可以稍等几十秒再去查询一下状态。
     the consumer group[***] not online状态,表示对应CID的消费者还未正确启动。业务系统需要检查消费者是否已启动,如果已启动请检查是否启动时有报错。有可能相关的配置项配置错误,导致consumer启动时校验失败。
     UNKNOW_EXCEPTION表示消息平台有异常,请联系MQCP开发同事。

13、如何避免接收到的消息是乱码?

对于生产者来说,建议将消息body转为byte数组时显示指定为UTF-8编码。对于消费者来说,建议在接收到消息后将byte数组转为String时指定UTF-8编码。这样可以避免因为消息body中有中文或者特殊字符,消费端解析时乱码,进而造成消息解析失败。

Example of producer

MQCPMessage msg = new MQCPMessage();

msg.setConent("test msg body".getBytes("UTF-8")); // 生产者组装消息body时指定urf-8编码

Example of consumer

14、MQCP中的消息标签(tag)如何使用?

在消息中间件实际的使用场景中,消费者只需要消息队列中的部分消息,其余消息希望默认不被接收,直接丢弃掉。针对类似这种场景 ,MQCP提供通过合理使用消息标签(Tag)的方式来实现消费端灵活过滤队列中消息的功能。  
实现方式如下:
        1、 生产者和消费者双方约定消息标签具体的设置值及其代表的含义。
        2、 生产者在发送消息时,组装消息对象的时候,需要给对应消息设置正确的消息标签。

MQCPMessage msg = new MQCPMessage();

//设置过滤标签---大小写敏感

msg.setTag("SystemTag");

3、 消费者在组装消费者对象时,需要正确设置消息过滤的过滤器

MQCPMessageFilter mqcpFilter = new MQCPMessageFilter();

List<String> list = new ArrayList<String>();

// 设置tag

// tag的作用是过滤消息,如果设置改值,MQCP只会取出发送消息时设置了该tag值的消息

// 需要注意的是同一CID下的多个应用实例需要设置为同样的tag列表来过滤消息,以保证消息不会被过滤取走但未被业务系统处理list.add("SystemTag");

mqcpFilter.setTags(list);

上述生产端、消费端的实现代码具体可查阅MQCP官网提供的Demo。

16、The consumer’s subscription not exist报错?

现象:业务系统开发环境报异常:The consumer’s subscription not exist,检查消费者状态,多台实例在线,检查消息消费情况,消息一直不被消费,状态为:SUBSCRIBED_AND_NOT_CONSUME_YET(订阅未消费)。

原因:业务系统之前已经接入过MQCP,且开发环境有多个开发人员在同时进行开发,多个主机同时连接MQCP,本次该consumer新增了1个订阅关系(subscription-A),但业务系统只有1个同事(小Y)的代码才新增了订阅关系,其他几个开发人员的代码没有配置,一旦他们的consumer启动后,给broker保持通信时发送的订阅关系中,并没有subscription-A,broker会remove该订阅关系,因此小Y的consumer启动后,与broker通信时发现没有subscription-A,就会报:The consumer’s subscription not exist。

解决方案:1、不同的cid,订阅不同的topic,避免同一个cid订阅多个topic

2、业务系统开发环境,只开发相关功能的主机才启动consumer

17、业务系统消费端两种常见的错误实现?

18、申请生产环境mq消息查询权限

请使用IE浏览器,在itsm系统申请对应的权限,通道如下:

IT权限管理-申请->平安科技_消息协作平台监控应用(MQCP_ADMIN)=>帐户管理组

19、测试环境admin平台查询消息已消费,但消费端未查询到消息

同一个消费者是集群消费模式,在测试环境中,只想在某一个环境测试MQ功能,需要每个环境配置不同的CID。比如某系统测试环境有多套环境:STG1和STG2环境,测试人员正在STG1测试,如果两个环境的CID相同,则消息有可能就被STG2取走,测试人员在STG1上查不到该消息。

解决方法:

1、若为刚新增的发布订阅关系,请联系MQCP同事申请给每一个环境配置独立的CID,并创建发布订阅关系

2、若每个环境都申请了独立的CID,请检查是否其他某个环境使用了该环境的CID

20、java.lang.reflect.InvocationTargetException告警

业务系统引用客户端包在1.0.15之前版本在启动时可能会出现该异常,不影响消息的收发,1.0.15的客户端版本修复了该问题

解决方法:

更新客户端包为最新的版本,最新包版本可在包库查询。客户端包不断在迭代优化,建议业务系统及时更新客户端包。客户端包每个版本更新内容可在官网“相关文档---CLIENT包版本线”查询。

需注意:客户端包在1.0.20优化了发布订阅关系的校验方式,配置文件的配置项有变化,从原来的name_server_address、cluster_id

、virtual_account变为了:server_address、virtual_account,具体请查阅demo

21、服务启动时报错:MQCP_CLIENT->Initial cache failed,no cache message received after waiting for 90s

客户端在重启的时报错初始化发布订阅关系缓存数据失败。

解决方法:

1、检查是否新增了配置文件:mqcp_client.properties,需新增

2、检查mqcp_client.properties配置文件,客户端版本为1.0.20之前的版本,需检查name_server_address、cluster_id的值是否正确,前后是否包含空格,检查virtual_account配置的虚拟用户是否与admin平台配置的一致,客户端版本为1.0.15之前的,还需在代码里新增name_server_address的配置:p.setProperty(MQCPConstant.NAME_SERVER_ADDRESS,"不同环境配置不同的nameserver地址" ); 客户端版本为1.0.20及之后的,需要检查server_address、virtual_account是否正确,是否包含空格。

3、建议更新客户端包为最新的包。

mq使用经验的更多相关文章

  1. R - MQ

    1. rabbit MQ 安装 Rabbit MQ 是建立在强大的Erlang OTP平台上,因此安装Rabbit MQ的前提是安装Erlang.通过下面两个连接下载安装3.2.3 版本: 下载并安装 ...

  2. MyEclipse10--的使用经验

    MyEclipse10--的使用经验总结 ------------------ 1.MyEclipse中的验证validation----->>用MyEclipse做ExtJs项目研发的时 ...

  3. 在 Windows 上安装Rabbit MQ 指南

    rabbitMQ是一个在AMQP协议标准基础上完整的,可服用的企业消息系统.他遵循Mozilla Public License开源协议.采用 Erlang 实现的工业级的消息队列(MQ)服务器. Ra ...

  4. XCode的个人使用经验

    Xcode是强大的IDE(但个人觉得不如Visual Studio做得好),其强大功能无需本人再赘述,本文也不是一篇“快捷键列表”,因为XCode上的快捷键极其多,而且还有不少是需要同时按下四个按键的 ...

  5. 【转】Spring mvc集成ZBUS--轻量级MQ、RPC、服务总线

    本文转自:http://www.cnblogs.com/top15from/p/4899954.html ZBUS = MQ + RPC + PROXY 支持消息队列, 发布订阅, RPC, 代理(T ...

  6. ZeroMQ接口函数之 :zmq_tcp – 使用TCP协议的ØMQ网络单播协议

    ZeroMQ 官方地址 :http://api.zeromq.org/4-1:zmq-tcp zmq_tcp(7)          ØMQ Manual - ØMQ/4.1.0 Name zmq_t ...

  7. ZeroMQ接口函数之 :zmq_inproc – ØMQ 本地进程内(线程间)传输方式

    ZeroMQ API 目录 :http://www.cnblogs.com/fengbohello/p/4230135.html ——————————————————————————————————— ...

  8. ZeroMQ接口函数之 :zmq_pgm – ØMQ 使用PGM 进行可靠的多路传输

    ZeroMQ API 目录 :http://www.cnblogs.com/fengbohello/p/4230135.html ——————————————————————————————————— ...

  9. MQ通道配置

    转自:http://www.cnblogs.com/me115/p/3471788.html MQ通道配置 通道是用来连接两个队列管理器的: 在单个队列管理器内读写消息不需要建立通道:但在一个队列管理 ...

随机推荐

  1. nginx的常规配置

    程序员们,在北上广你还能买房吗? >>>   nginx的常规配置 nginx的使用非常简单,只需要配置好我们需要的各种指令,就能跑起来.如果你需要添加模块,还需要添加模块方面的配 ...

  2. svn无法checkout大文件的解决办法

    美术组同事checkout出现,在网络好的情况下,有同事更新下来了,后来在配置文件http.conf的最后加入下面压缩参数才解决问题,配置如下: <IfModule deflate_module ...

  3. C++类学习

    一.C++类的定义     C++中使用关键字 class 来定义类, 其基本形式如下:class 类名{ public: //行为或属性  protected: //行为或属性 private: / ...

  4. 【bzoj2212&3702】二叉树

    线段树合并入门题. 分别计算左子树的逆序对,右子树的逆序对,合并的时候计算贡献. #include<bits/stdc++.h> #define N 8000005 using names ...

  5. 【转】如何只用CSS做到完全居中

    英文原版链接:http://codepen.io/shshaw/full/gEiDt 我们都知道 margin:0 auto; 的样式能让元素水平居中,而 margin: auto; 却不能做到垂直居 ...

  6. 【cocos2d-js官方文档】十一、cc.path

    概述 该单例是为了方便开发者操作文件路径所设计的.定义为cc.path的目的是为了跟nodejs的path保持一致.里面定义的api也基本跟nodejs的path模块一致,但不全有,今后可能还会继续根 ...

  7. django实现动态菜单的方式

    1.model from django.contrib.auth.models import User #django自带 class UserProfile(models.Model): " ...

  8. <div>之定位

    在使用盒子模型的过程中,如何放置各种类型的“盒子”,就存在定位.浮动等问题.下面就日常运用过程中出现过的情况总结如下(陆续加入中....) 一.图片直接做<div>的背景 在<div ...

  9. python 去掉所有空白字符【解决】

    今天用python从access数据库读取内容,组合成sql语句时,空白字符把我给搞疯了.... 所幸找到了一个好办法: ''.join(s.split())

  10. NYOJ 914 Yougth的最大化【二分/最大化平均值模板/01分数规划】

    914-Yougth的最大化 内存限制:64MB 时间限制:1000ms 特判: No 通过数:3 提交数:4 难度:4 题目描述: Yougth现在有n个物品的重量和价值分别是Wi和Vi,你能帮他从 ...