一、前言
首先声明:本篇中编写的案例,基本都是来自官网。
简单的入门案例:
http://rocketmq.apache.org/docs/simple-example/
更多案例:https://github.com/apache/rocketmq/tree/master/example
常见消息用法:
- 简单消息
- 广播消息
- 延时发送(计划消息)
- 批量发送消息
- 顺序消息
- 消息筛选
- 事务消息(这里不做讲解,专门在分布式事务里面讲解)
二、开发环境搭建
不管是生产者还是消费者,搭建都很简单,引入rocketmq-client相关包即可。
<dependencies>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.5.1</version>
</dependency>
</dependencies>
三、简单消息
(一)Consumer
这里简单的使用DefaultMQPushConsumer来接受消息,基于方法回调来接受消息
package com.firewolf.rocketmq;
import java.util.List;
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.message.MessageExt;
/**
* 作者:刘兴 时间:2019-07-11
**/
public class Consumer {
public static void main(String[] args) throws InterruptedException, MQClientException {
//创建消费者实例,指定消费者组
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer-group1");
//指定nameserver地址,多个用;隔开
consumer.setNamesrvAddr("10.211.55.6:9876");
//指定订阅的主题和tag,*表示订阅这个主题下的所有消息
consumer.subscribe("mytopic", "*");
//注册接受到消息的回调函数
consumer.registerMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext consumeConcurrentlyContext) {
System.out.printf("%s 接收到消息: %s %n", Thread.currentThread().getName(), msgs);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//Launch the consumer instance.
consumer.start();
System.out.printf("消费者启动了...%n");
}
}
(二)Producer
这里使用三种方式发送消息:
- 同步(同步等待投递消息的结构)。大部分场景都会使用这种模式,如:重要的通知消息、SMS等。
- 异步(回调方法异步接受处理的结果)。用于对响应时间敏感的商业场景。
- 单向(不关系结果)。用于可靠性要求不高的场景,如:记录日志。
其实就是调用的方法不同而已
1. 同步
package com.firewolf.rocketmq;
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.common.RemotingHelper;
/**
* 作者:刘兴 时间:2019-07-11
* 同步发送,就是立马知道消息投递的结果
**/
public class SyncProducer {
public static void main(String[] args) throws Exception {
//创建生产者,指定所在的组
DefaultMQProducer producer = new
DefaultMQProducer("producer-group1");
//指定nameserver,如果有多个,用;隔开
producer.setNamesrvAddr("10.211.55.6:9876");
//启动生产者
producer.start();
//发送消息
for (int i = 0; i < 20; i++) {
//创建要发送的消息对象
Message msg = new Message("mytopic" /* Topic */,
"TagA" /* Tag */,
("Hello RocketMQ " +
i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
);
//发送消息
SendResult sendResult = producer.send(msg);
System.out.printf("%s%n", sendResult);
}
//关闭生产者
producer.shutdown();
}
}
2. 异步
package com.firewolf.rocketmq;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
/**
* 作者:刘兴 时间:2019-07-11 异步发送,通过回调函数来处理发送的结果
**/
public class AsyncProducer {
public static void main(String[] args) throws Exception {
DefaultMQProducer mqProducer = new DefaultMQProducer("producer-group1");
mqProducer.setNamesrvAddr("10.211.55.6:9876");
mqProducer.start();
for (int i = 0; i < 10; i++) {
Message message = new Message("mytopic", "TagN", ("liuxing" + i).getBytes("UTF-8"));
mqProducer.send(message,
//回调函数,用来处理请求的结果,
new SendCallback() {
//投递成功
public void onSuccess(SendResult sendResult) {
System.out.println(sendResult);
}
//投递失败
public void onException(Throwable throwable) {
throwable.printStackTrace();
}
});
}
}
}
3. 单向
package com.firewolf.rocketmq;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
/**
* 作者:刘兴 时间:2019-07-11
* 单向生产者,不关心投递的结果
**/
public class OneWayProducer {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new
DefaultMQProducer("producer-group1");
producer.setNamesrvAddr("10.211.55.6:9876");
producer.start();
for (int i = 0; i < 5; i++) {
//创建要发送的消息对象
Message msg = new Message("mytopic" /* Topic */,
"TagA" /* Tag */,
("OneWay Message " +
i).getBytes("UTF-8") /* Message body */
);
//发送消息
producer.sendOneway(msg);
}
//关闭生产者
producer.shutdown();
}
}
启动生产者和消费者,就能看到控制台打印的消息投递和接受的信息,当然,我们也可以通过web管理控制台来查看。
四、广播消息
消息模型有两种:广播消息和集群,被定义在MessageModel中
public enum MessageModel {
BROADCASTING("BROADCASTING"),
CLUSTERING("CLUSTERING");
....
默认情况下,消费者会使用CLUSTERING,这种模式下,各个消费者只处理其中的一部分,而BROADCASTING模式下,每个消费者会处理所有的消息。
用法
在消费者注册监听之前调用Consumer.setMessageModel
进行设置即可
五、延时发送
通过给Message设置Message.setDelayTimeLevel(int level)
即可让消息延迟发送,默认级别有:“1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”,当我们把级别设置成3的时候,就会延时10秒。
六、批量发送消息
批量发送调用MQProducer的类似于SendResult send(final Collection<Message> msgs)
的方法即可,注意的是,批量发送要求一次的消息量不能大于1M。
七、顺序消息
(一)分析
实际上,RocketMQ是支持顺序消费的。 但这个顺序,不是全局顺序,只是分区(就是一个Message Queue)顺序,要全局顺序只能一个分区。
默认情况下,我们收到的消息看起来不是顺序的,是因为发送消息的时候,消息发送默认是会采用轮询的方式发送到不通的queue(分区)。而消费端消费的时候,是会分配到多个queue的,多个queue是同时拉取提交消费。
但是同一条queue里面,RocketMQ的确是能保证FIFO的。那么要做到顺序消息,需要如下两方面进行处理:
- 把消息确保投递到同一条queue。 rocketmq消息生产端把需要保证顺序的消息放到同一个MessageQueue中。如:实例中把订单号取了做了一个取模运算再丢到selector中,selector保证同一个模的都会投递到同一条queue。 即: 相同订单号的—>有相同的模—>有相同的queue。
- 保证消费端顺序消费。通过第1条,会出现如下情况:同一批你需要做到顺序消费的肯定会投递到同一个queue,同一个queue肯定会投递到同一个消费实例,同一个消费实例肯定是顺序拉取并顺序提交线程池的,接下来,只要保证消费端顺序消费,就可以保证消息有序了。
保证消费有序有两种方式:
- 使用MessageListenerOrderly来进行消费,这个已经保证了顺序;
- 使用MessageListenerConcurrently,把消费者线程设置成单线程,即可
(二)示例
生产者
package com.firewolf.rocketmq;
import java.util.List;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.common.RemotingHelper;
public class OrderedProducer {
public static void main(String[] args) throws Exception {
//Instantiate with a producer group name.
DefaultMQProducer producer = new DefaultMQProducer("example_group_name");
//Launch the instance.
producer.setNamesrvAddr("10.211.55.6:9876");
producer.start();
String[] tags = new String[]{"TagA", "TagB", "TagC", "TagD", "TagE"};
for (int i = 0; i < 100; i++) {
int orderId = i % 10;
Message msg = new Message("mytopic", tags[i % tags.length],
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
Integer id = (Integer) arg;
int index = id % mqs.size();
return mqs.get(index);
}
}, orderId);
System.out.printf("%s%n", sendResult);
}
//server shutdown
producer.shutdown();
}
}
消费者
package com.firewolf.rocketmq;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
public class OrderedConsumer {
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("example_group_name");
consumer.setNamesrvAddr("10.211.55.6:9876");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe("mytopic", "*");
consumer.registerMessageListener(new MessageListenerOrderly() {
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeOrderlyContext context) {
for (MessageExt msg : msgs) {
System.out.println(msg.getQueueId() + "," + new String(msg.getBody()));
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
consumer.start();
System.out.printf("Consumer Started.%n");
}
}
八、消息筛选
可以通过简单的类似SQL的语法对消息进行过滤。
(一)支持的语法
数字比较: >, >=, <, <=, BETWEEN, =;
字符操作:<>, IN;
空值判断:IS NULL 、IS NOT NULL;
逻辑操作:AND, OR, NOT;
(二)生产者
DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
producer.start();
Message msg = new Message("TopicTest",
tag,
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET)
);
// Set some properties.
msg.putUserProperty("a", String.valueOf(i));
SendResult sendResult = producer.send(msg);
producer.shutdown();
(三)消费者
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_4");
// only subsribe messages have property a, also a >=0 and a <= 3
consumer.subscribe("TopicTest", MessageSelector.bySql("a between 0 and 3");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();