RocketMQ入门(生产者)_2
从 RocketMQ环境搭建_1 我们已经建立了MQ的Server,接下来就是简单的生产和消费的过程。
1. rocketMQ的源码中有个示例代码example ,我们从Apache官网中可以下载源码source找到example,进行学习。
下载地址:http://rocketmq.apache.org/docs/quick-start/
建立简单的工程,mvn最主要依赖client
- <dependency>
- <groupId>org.apache.rocketmq</groupId>
- <artifactId>rocketmq-client</artifactId>
- <version>4.3.</version>
- </dependency>
在做transactionProducer时,发现无法消费,问题是需要依赖parent,因此借鉴demo中的mvn依赖:
- <parent>
- <groupId>org.apache.rocketmq</groupId>
- <artifactId>rocketmq-all</artifactId>
- <version>4.3.0</version>
- </parent>
- <modelVersion>4.0.0</modelVersion>
- <packaging>jar</packaging>
- <artifactId>rocketmq-example</artifactId>
- <name>rocketmq-example ${project.version}</name>
- <dependencies>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>rocketmq-client</artifactId>
- </dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>rocketmq-srvutil</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- </dependency>
- <dependency>
- <groupId>org.javassist</groupId>
- <artifactId>javassist</artifactId>
- </dependency>
- <dependency>
- <groupId>io.openmessaging</groupId>
- <artifactId>openmessaging-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.rocketmq</groupId>
- <artifactId>rocketmq-openmessaging</artifactId>
- <version>4.3.0</version>
- </dependency>
- </dependencies>
创建Producer类:
- /**
- * 简单生产者
- *
- * @author DennyZhao
- * @since 2018/10/29
- * @version 1.0
- */
- public class Producer {
- /**
- * main方法
- *
- * @param args
- * @throws InterruptedException
- * @throws MQBrokerException
- * @throws RemotingException
- * @throws MQClientException
- * @throws UnsupportedEncodingException
- */
- public static void main(String[] args) throws MQClientException, RemotingException, MQBrokerException,
- InterruptedException, UnsupportedEncodingException {
- //创建生产者实例,并确定生产组
- DefaultMQProducer producer = new DefaultMQProducer("fruitProducerGroup");
- // 指定服务NameServer服务
- producer.setNamesrvAddr("192.168.68.137:9876;192.168.68.138:9876;");
- // 生产者启动
- producer.start();
- String[] fruitArray = { "apple", "strawbarry", "pear", "banana", "orange" };
- for (String fruit : fruitArray) {
- // 创建消息
- Message message = new Message("fruit", "common", fruit.getBytes(RemotingHelper.DEFAULT_CHARSET));
- // 发送消息
- SendResult result = producer.send(message);
- SendStatus sendStatus = result.getSendStatus();
- // 获取回执
- System.out.println(result);
- if (sendStatus == SendStatus.SEND_OK) {
- System.out.println("信息发送成功!");
- } else {
- System.out.println("信息发送失败!");
- }
- }
- // 关闭生产者
- producer.shutdown();
- }
- }
创建Consumer类:
- /**
- * 消費者群體
- * @author DennyZhao
- * @since 2018/10/29
- * @version 1.0
- */
- public class Consumer {
- public static void main(String[] args) throws MQClientException {
- //创建消费者实例和组
- DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("fruitConsumerGroup");
- // 指定nameServer服务地址
- consumer.setNamesrvAddr("192.168.68.137:9876;192.168.68.138:9876;");
- // 订阅消费Topic
- consumer.subscribe("fruit", "*");
- // 订阅从何地方开始读(先进先出,还是先进后出)
- consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
- // 添加监听
- consumer.registerMessageListener(new MessageListenerConcurrently() {
- //获取数据,防止一次获取太多无法消化,可一次取单个条数。
- public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgList, ConsumeConcurrentlyContext arg1) {
- if(msgList != null && msgList.size() > 0) {
- MessageExt msg = msgList.get(0);
- System.out.println(msg);
- try {
- System.out.println(new String(msg.getBody(), RemotingHelper.DEFAULT_CHARSET));
- } catch (UnsupportedEncodingException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
- }
- });
- // 启动消费者
- consumer.start();
- }
- }
※注意:启动最好先启动消费者然后再启动服务者。
从RocketMQ-console中可以看到创建的消费者群和生产者群。至此,简单的生产消费就算大功告成。
配置说明:
1.producer-生产者类型
- 1. NormalProducer (普通生产者)
- 2. OrderProducer (严格顺序生产者,例如:订单创建,付款,发货等)
- 3. TransactionProducer (事务生产者)
msgId和msgKey非常关键:msgId是mq自动生成的,可在控制台message中查找数据。
msgKey大多是业务主键key,用于跟踪数据,比如订单号等。
msg中有个很重要的属性:在producer端放置:msg.putUserProperty([key], [value]); //是个map,可放内容,在consumer端获取
主要可选参数:
- // 设置超过多大进行compress压缩
- producer.setCompressMsgBodyOverHowmuch(1024 * 10);
- // 设置发送失败的尝试次数。
- producer.setRetryTimesWhenSendFailed(3);
- // 设置如果返回值不是send_ok,是否要重新发送
- producer.setRetryAnotherBrokerWhenNotStoreOK(false);
- // 设置限制最大的文件大小
- producer.setMaxMessageSize(1024*50);
- // 设置默认主题对应的队列数
- producer.setDefaultTopicQueueNums(4);
- //创建新的topic
- producer.createTopic("1121", "vegetables", 4);
- // 设置发送超时时间 ms
- producer.setSendMsgTimeout(1000);
OrderProducer 采用将有序内容放在单个queue,保证消费的顺序进行。可参见示例中的order代码。
Producer类不同点展示,发送消息:
- // 发送消息
- SendResult result = producer.send(message, new MessageQueueSelector() {
- public MessageQueue select(List<MessageQueue> msgList, Message message, Object queueId) {
- return msgList.get(Integer.valueOf(queueId.toString()));
- }}, 0); //这个0表示将这些msg放入到队列0中
Consumer类不同点展示,接受消息:
- // 添加监听
- consumer.registerMessageListener(new MessageListenerOrderly() {
- //获取数据,防止一次获取太多无法消化,可一次取单个条数。
- public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgList, ConsumeOrderlyContext arg1) {
- if(msgList != null && msgList.size() > 0) {
- MessageExt msg = msgList.get(0);
- System.out.println(msg);
- System.out.println(msg.getBody());
- }
- return ConsumeOrderlyStatus.SUCCESS;
- }
- });
TransactionProducer :
用于解决事务同步,尤其是金融方面,在单体应用中我们可以通过数据库事务来控制,但大的电商系统表和表可能分属于不同的数据库,数据库事务则失效。
比如微信转银行操作:我们从银行扣款100元到微信,如果微信增加100元,而此时出现问题,银行扣款没有成功这样理论上是要回滚微信的增加100元。
一般分布式采用2pc(2 phase commit)模式(两阶段提交协议:预留和确认),安全性高,但是因为长连接导致长时间等待。
而RocketMQ采用两阶段补偿型,TCC(Try-Confirm-Cancel)的简称。
应用场景:买2张票(春运回家,从天津->上海->武汉),先买预留,然后在规定时间内付款则commit,否则过期后rollback.(无论哪个)
异常回溯:在3.2.6+版本的非商业版已经取消,
需要手动回查参见别人的文章RocketMQ事务消息回查设计方案
常对应参数:
- /**
- * 做数据反查轮询用 UN KNOW,时会用到反查目前已经 deprecated
- */
- producer.setCheckThreadPoolMaxSize();
- producer.setCheckThreadPoolMinSize();
producer.setCheckRequestHoldMax(200);//回查最大数
生产者修改:添加
- sendMessage变化:TransactionSendResult
producer变化: TransactionMQProducer
添加监听: TransactionListener
因非商业3.2.6取消回查:因此 producer.setExecutorService(executorService);没有作用,本来是用于开启多线程进行回查用
- /**
- * 事务生产者
- *
- * @author DennyZhao
- * @since 2018/10/31
- * @version 1.0
- */
- public class Producer {
- /**
- * main方法
- *
- * @param args
- * @throws InterruptedException
- * @throws MQBrokerException
- * @throws RemotingException
- * @throws MQClientException
- * @throws UnsupportedEncodingException
- */
- public static void main(String[] args) throws MQClientException, RemotingException, MQBrokerException,
- InterruptedException, UnsupportedEncodingException {
- //创建生产者实例,并确定生产组
- TransactionMQProducer producer = new TransactionMQProducer("transProducerGroup");
- TransactionListener transListener = new FruitTransactionListener();
- // 指定服务NameServer服务
- producer.setNamesrvAddr("192.168.68.137:9876;192.168.68.138:9876;");
- // // 设置超过多大进行compress压缩
- // producer.setCompressMsgBodyOverHowmuch(1024 * 10);
- // // 设置发送失败的尝试次数。
- // producer.setRetryTimesWhenSendFailed(3);
- // // 设置如果返回值不是send_ok,是否要重新发送
- // producer.setRetryAnotherBrokerWhenNotStoreOK(true);
- // // 设置限制最大的文件大小
- // producer.setMaxMessageSize(1024*50);
- // // 设置默认主题对应的队列数
- // producer.setDefaultTopicQueueNums(4);
- // // 设置发送超时时间 ms
- // producer.setSendMsgTimeout(1000);
- /**
- * 做数据反查轮询用
- */
- // producer.setCheckThreadPoolMaxSize(5);
- // producer.setCheckThreadPoolMinSize(2);
- // producer.setCheckRequestHoldMax(200);//回查最大数
- ExecutorService executorService = new ThreadPoolExecutor(2, 5, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2000), new ThreadFactory() {
- public Thread newThread(Runnable r) {
- Thread th = new Thread(r);
- th.setName("client-transaction-msg-check-thread");
- System.out.println("id:" + th.getId());
- System.out.println("name:" + th.getName());
- return th;
- }
- });
- producer.setExecutorService(executorService);
- /**
- * 添加监听
- */
- producer.setTransactionListener(transListener);
- // 生产者启动
- producer.start();
- String[] fruitArray = { "apple-苹果", "strawbarry-草莓", "pear-梨子", "banana-香蕉", "orange-橘子"};
- for (String fruit : fruitArray) {
- // 创建消息
- Message message = new Message("transactionFruit", "common", "key"+fruit, fruit.getBytes(RemotingHelper.DEFAULT_CHARSET));
- // 发送消息
- TransactionSendResult result = producer.sendMessageInTransaction(message, "abcd");
- Thread.sleep(10);
- SendStatus sendStatus = result.getSendStatus();
- // 获取回执
- System.out.println(result);
- if (sendStatus == SendStatus.SEND_OK) {
- System.out.println("信息发送成功!");
- } else {
- System.out.println("信息发送失败!");
- }
- }
- int j = 0;
- while(j <500) {
- Thread.sleep(1000);
- j++;
- }
- // 关闭生产者
- producer.shutdown();
- }
监听:
- 因回查被取消因此:checkLocalTransaction(MessageExt msg)没有作用了,所以如果
- LocalTransactionState.UNKNOW 将无法处理,会使得topic一直处于不显示状态。
- /**
- * 事务执行监听
- * @author DennyZhao
- *
- */
- public class FruitTransactionListener implements TransactionListener {
- /**
- * 执行事务,事务成功commit,不成功rollback,未知unknown
- * msg Message
- * arg 附加参数,用于处理传递内容加以判断,使用
- *
- */
- public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
- System.out.println((String) arg);
- // 执行事务处
- // 假设以水果入库为例:苹果,香蕉 commit,梨子 rollback, 橘子和草莓不知道怎么处理
- System.out.println(msg + "---executeLocal");
- System.out.println(msg.getTransactionId());
- String fruit = "";
- try {
- fruit = new String(msg.getBody(), RemotingHelper.DEFAULT_CHARSET);
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- return LocalTransactionState.ROLLBACK_MESSAGE;
- }
- if(StringUtils.contains(fruit, "苹果")
- || StringUtils.contains(fruit, "香蕉")) {//提交
- return LocalTransactionState.COMMIT_MESSAGE;
- }else if(StringUtils.contains(fruit, "梨")) {//回滚
- return LocalTransactionState.ROLLBACK_MESSAGE;
- }else { //通过轮询去处理
- return LocalTransactionState.UNKNOW;
- }
- }
- /**
- * 轮询反查,对于unknow的内容,进行反查获取结果
- */
- public LocalTransactionState checkLocalTransaction(MessageExt msg) {
- System.out.println(msg + "---checkAgain");
- String fruit = "";
- try {
- fruit = new String(msg.getBody(), RemotingHelper.DEFAULT_CHARSET);
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- return LocalTransactionState.ROLLBACK_MESSAGE;
- }
- //从库中反查知道库中 存在葡萄,不存在橘子
- if(StringUtils.contains(fruit, "葡萄")) {//提交
- return LocalTransactionState.COMMIT_MESSAGE;
- }else if(StringUtils.contains(fruit, "橘子")) {//回滚
- return LocalTransactionState.ROLLBACK_MESSAGE;
- }
- return LocalTransactionState.UNKNOW;
- }
错误说明:
1. No route info of this topic
因在启动broker时,参数中未设置可自动创建topic,因此生产者创建topic被认为不合法,需要在console中先创建topic,或者服务端先创建topic。
2. transactionProducer 生产后无法消费
mvn依赖中缺少 <parent>rocketmq-all</parent>导致。
3. 事务回查无效
版本 3.2.6 后rocketmq取消了事务回查机制,如果丢失需要自己手动通过key值回查.
RocketMQ入门(生产者)_2的更多相关文章
- RocketMQ入门(消费者)_3
消费者角色: 1. 推式(一般建议用推式) 2. 拉式 消费模式: 1. 集群(cluster) --均衡负载消费 2. 广播(broadcasting) --发布和订阅 ...
- RocketMQ入门(2)最佳实践
转自:http://www.changeself.net/archives/rocketmq入门(2)最佳实践.html RocketMQ入门(2)最佳实践 一.服务端安装部署 我是在虚拟机中的Cen ...
- 必须先理解的RocketMQ入门手册,才能再次深入解读
RocketMQ入门手册 RocketMQ是一个分布式.队列模型的开源消息中间件,前身是MetaQ,是阿里研发的一个队列模型的消息中间件,后开源给apache基金会成为了apache的顶级开源项目,具 ...
- RocketMQ入门到入土(二)事务消息&顺序消息
接上一篇:RocketMQ入门到入土(一)新手也能看懂的原理和实战! 一.事务消息的由来 1.案例 引用官方的购物案例: 小明购买一个100元的东西,账户扣款100元的同时需要保证在下游的积分系统给小 ...
- RocketMQ入门(3)拉取消息
转自:http://www.changeself.net/archives/rocketmq入门(3)拉取消息.html RocketMQ入门(3)拉取消息 RocketMQ不止可以直接推送消息,在消 ...
- RocketMQ入门(1)
转自:http://www.changeself.net/archives/rocketmq入门(1).html RocketMQ入门(1) RocketMQ是一款分布式.队列模型的消息中间件,具有以 ...
- 【rocketmq学习笔记】rocketmq入门学习
基本介绍 rocketmq是阿里巴巴团队使用java语言开发的一款基于发布订阅模型的分布式消息队列中间件,是一款低延迟,高可用,拥有海量消息堆积能力和灵活拓展性的消息队列. 特点 可以实现集群无单点故 ...
- 消息队列之-RocketMQ入门
简介 RocketMQ是阿里开源的消息中间件,目前已经捐献个Apache基金会,它是由Java语言开发的,具备高吞吐量.高可用性.适合大规模分布式系统应用等特点,经历过双11的洗礼,实力不容小觑. 官 ...
- 轻松搞定RocketMQ入门
RocketMQ是一款分布式.队列模型的消息中间件,具有以下特点: 能够保证严格的消息顺序 提供丰富的消息拉取模式 高效的订阅者水平扩展能力 实时的消息订阅机制 亿级消息堆积能力 RocketMQ网络 ...
随机推荐
- mvc项目远程发布到windows server服务器
1.安装IIS的时候需要将这两个选项勾选起来 2.确保 管理服务委派 这个选项存在 3.添加委派规则 4.配置IIS管理用户,后续需要用这个用户进行发布连接 5.配置站点的IIS权限 选择刚才在前面设 ...
- OpenStack搭建Q版在控制节点上的环境准备(step2)
接下来是只需要在控制节点上准备的环境配置.其中虽然NTP服务需要在所有节点上都安装,但NTP服务在控制节点和其他的节点上的配置是不同的,所以不把它放在step1的公共配置中进行准备.如下: 1.配置N ...
- 第二节 Python基础之变量,运算符,if语句,while和for循环语句
我们在上一节中,我们发现当我们用字符串进行一些功能处理的时候,我们都是把整个字符串写下来的,比如"jasonhy".startwith("j"),如果我们在程序 ...
- Java8-Optional与null
对null进行处理 程序中经常需要对null情况进行处理,比如Course类中有一个List stuList属性,Student有一个name属性. 现在想要查看某个student的name属性的长度 ...
- windows 命令相关
脚本执行后的返回值:echo %errorlevel%
- JavaScript 函数调用和this指针
函数调用和this指针 1. 全局环境的this指针 浏览器全局环境下this指向window对象 console.log(this); //Window {postMessage: ƒ, blur: ...
- C# Xamarin开发 GenyMotion adb List of devices attached
最近,公司要求要学习Xamarin,说是将来用到PDA上,所以最近对XaMarin开始接触,16年的时候就听说.Net开始着实跨平台,安卓和IOS,但是网上看过很多资料都说Xamarin比较坑,一般的 ...
- oracle 查询所有约束
主键约束SELECT USER_CONS_COLUMNS.CONSTRAINT_NAME AS 约束名, USER_CONS_COLUMNS.TABLE_NAME AS 表名, USER_CON ...
- 7.9 skippart.c 程序
7.9 skippart.c 程序 #include <stdio.h> int main(void) { const float MIN = 0.0f; const float MAX ...
- ionic2 调用 cordova非本地化native 插件方法
1,在项目中添加你的插件 cordova plugin add puginId|puginName|puginUrl|puginPath 2,查看插件clobbers标记 打开项目目录plug ...