什么是RocketMQ?
消息队列 RocketMQ 版(原ONS)是阿里云基于 Apache RocketMQ 构建的低延迟、高并发、高可用、高可靠的分布式消息中间件。最初由阿里巴巴自研并捐赠给 Apache 基金会,服务阿里集团13年,覆盖全集团所有业务。作为双十一交易核心链路的官方指定产品,支撑千万级并发、万亿级数据洪峰,历年刷新大规模交易消息流转记录。
工作流程图:
工作流程文字描述:
1.启动namesvr后,等待broker发送心跳,把broker的ip信息管理起来
2.启动broker,向namesvr发送心跳
3.启动生产者producer,随机和某个namesvr建立长连接,建立topic,从namesvr中获取对应topic的broker的ip,先broker发送消息
4.启动消费者consumer,随机和某个namesvr建立长连接,broker推送新消息,consumer获取已订阅的topic中的消息进行消费
准备工作:
添加依赖:
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.4.0</version>
</dependency>
入门案例:
生产者:
public class Producer {
public static void main(String[] args) throws Exception{
//1 创建一个生产者对象, 并且指定一个生产者组
DefaultMQProducer defaultMQProducer =new DefaultMQProducer("rocketmq-producer");
//2 设置名字服务的地址
defaultMQProducer.setNamesrvAddr("localhost:9876");
//3 启动生产者
defaultMQProducer.start();
//4 创建一个消息
Message message = new Message("mq1",("hello RocketMQ---"+System.currentTimeMillis()).getBytes());
//5 发送消息
defaultMQProducer.send(message);
//6 关闭连接
defaultMQProducer.shutdown();
}
}
消费者:
public class Consumer {
public static void main(String[] args) throws Exception{
//创建一个拉取消息的消费者对象
DefaultMQPushConsumer Consumer =new DefaultMQPushConsumer("rocketmq-consumer");
//设置名字地址
Consumer.setNamesrvAddr("localhost:9876");
//绑定消息的主题
Consumer.subscribe("mq1","*");
//消费者监听处理消息方法
Consumer.registerMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
//list就是订阅topic的消息集合
for (MessageExt messageExt : list) {
String msg = new String(messageExt.getBody());
System.out.println(msg);
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//启动消费者
Consumer.start();
}
}
原生API的使用
发送方式
同步发送
同步发送,有返回值,发送消息后需要等待消息持久化到磁盘
defaultMQProducer.send(msg);
public class SyncProducer {
public static void main(String[] args) throws Exception {
// 实例化消息生产者Producer
DefaultMQProducer defaultMQProducer = new DefaultMQProducer("rocketmq-producer");
// 设置NameServer的地址
defaultMQProducer.setNamesrvAddr("localhost:9876");
// 启动Producer实例
defaultMQProducer.start();
for (int i = 0; i <10 ; i++) {
Message msg = new Message("mq2", ("rocketmq-sync---" + System.currentTimeMillis()).getBytes());
SendResult send = defaultMQProducer.send(msg);
System.out.println(send.getMsgId());
}
// 如果不再发送消息,关闭Producer实例。
defaultMQProducer.shutdown();
}
}
异步发送
异步发送,有返回值,但它发送消息后,不需要等待消息持久化完成,就可以执行其他操作,直到sendCallBack的回调再做相应的处理
producer.send(msg, new SendCallback() {
public void onSuccess(SendResult sendResult) {}
public void onException(Throwable throwable) {}
});
public class AsyncProducer {
public static void main(String[] args) throws Exception {
// 实例化消息生产者Producer
DefaultMQProducer producer = new DefaultMQProducer("rocketmq-producer");
// 设置NameServer的地址
producer.setNamesrvAddr("localhost:9876");
// 启动Producer实例
producer.start();
final CountDownLatch count = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
// 创建消息,并指定Topic,Tag和消息体
Message msg = new Message("mq3", ("rocketmq-async " + System.currentTimeMillis()).getBytes());
//发送同步消息到一个Broker
producer.send(msg, new SendCallback() {
public void onSuccess(SendResult sendResult) {
System.out.println("发送成功--"+sendResult.getMsgId());
count.countDown();
}
public void onException(Throwable throwable) {
System.out.println("发送失败");
count.countDown();
}
});
}
count.await();
// 如果不再发送消息,关闭Producer实例。
producer.shutdown();
}
}
一次性发送
一次性发送没有返回值的,因为它不关心消息是否成功持久化到磁盘中
producer.sendOneway(msg);
public class OneWayProducer {
public static void main(String[] args) throws Exception {
// 实例化消息生产者Producer
DefaultMQProducer producer = new DefaultMQProducer("rocketmq-producer");
// 设置NameServer的地址
producer.setNamesrvAddr("localhost:9876");
// 启动Producer实例
producer.start();
for (int i = 0; i < 10; i++) {
// 创建消息,并指定Topic,Tag和消息体
Message msg = new Message("mq4", ("rocketmq-oneway---" + System.currentTimeMillis()).getBytes());
//发送同步消息到一个Broker
producer.sendOneway(msg);
}
// 如果不再发送消息,关闭Producer实例。
producer.shutdown();
}
}
消费模式
集群
设置集群消费模式:consumer.setMessageModel(MessageModel.CLUSTERING);
集群模式下,某一条消息只能被某一个消费者消费
public class Consumer {
public static void main(String[] args) throws Exception{
//创建一个拉取消息的消费者对象
DefaultMQPushConsumer consumer =new DefaultMQPushConsumer("rocketmq-consumer");
//设置消费模式
consumer.setMessageModel(MessageModel.CLUSTERING);
//设置名字地址
consumer.setNamesrvAddr("localhost:9876");
//绑定消息的主题
consumer.subscribe("mq5","*");
//消费者监听处理消息方法
consumer.registerMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt messageExt : list) {
String msg = new String(messageExt.getBody());
System.out.println(msg);
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//启动消费者
consumer.start();
}
}
广播:
设置广播消费模式:consumer.setMessageModel(MessageModel.BROADCASTING);
广播模式下,每条消息都会被所有消费者消费一次
public class Consumer {
public static void main(String[] args) throws Exception{
//创建一个拉取消息的消费者对象
DefaultMQPushConsumer consumer =new DefaultMQPushConsumer("rocketmq-consumer");
//设置消费模式
consumer.setMessageModel(MessageModel.BROADCASTING);
//设置名字地址
consumer.setNamesrvAddr("localhost:9876");
//绑定消息的主题
consumer.subscribe("mq6","*");
//消费者监听处理消息方法
consumer.registerMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt messageExt : list) {
String msg = new String(messageExt.getBody());
System.out.println(msg);
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//启动消费者
consumer.start();
}
}
消费方式
一般情况下,我们都是使用推送的方式。
推送
上面所有的消费者都属于推送消费方式,这里就不说了,来看下拉取消费方式就行了
拉取
public class PullConsumer {
public static void main(String[] args) throws Exception{
//创建一个拉取消息的消费者对象
DefaultMQPullConsumer consumer =new DefaultMQPullConsumer("rocketmq-consumer");
//设置名字地址
consumer.setNamesrvAddr("localhost:9876");
//启动消费者
consumer.start();
/**
* messageQueue 1:topic 2:brokerName 3:queueId
* subExpression 过滤表达式
* offset 从第offset条消息开始拉取
* maxNums 拉取多少条
*/
PullResult pullResult = consumer.pull(new MessageQueue("mq7", "broker-a", 0),
"*", 0, 10);
List<MessageExt> messageExts = pullResult.getMsgFoundList();
for (MessageExt messageExt : messageExts) {
System.out.println("拉取消费方式:"+new String(messageExt.getBody()));
}
consumer.shutdown();
}
}
延迟消息
设置延时消息等级:msg.setDelayTimeLevel(2); 1为1s,2为5s,3为10s…
public class Producer {
public static void main(String[] args) throws Exception {
// 实例化消息生产者Producer
DefaultMQProducer producer = new DefaultMQProducer("rocketmq-producer");
// 设置NameServer的地址
producer.setNamesrvAddr("localhost:9876");
// 启动Producer实例
producer.start();
for (int i = 0; i <5 ; i++) {
Message msg = new Message("mq8", ("rocketmq-delay---" + System.currentTimeMillis()).getBytes());
// 设置延时等级2,这个消息将在5s之后发送(现在只支持固定的几个时间,详看delayTimeLevel)
msg.setDelayTimeLevel(2);
SendResult send = producer.send(msg);
System.out.println(send.getMsgId());
}
// 如果不再发送消息,关闭Producer实例。
producer.shutdown();
}
}
消息过滤
Tag消息过滤
生产者
//TagB作为过滤条件
Message msg = new Message(“mq9”,“TagB”,(“rocketmq-filter-tag—” + System.currentTimeMillis()).getBytes());
public class Producer {
public static void main(String[] args) throws Exception {
// 实例化消息生产者Producer
DefaultMQProducer defaultMQProducer = new DefaultMQProducer("rocketmq-producer");
// 设置NameServer的地址
defaultMQProducer.setNamesrvAddr("localhost:9876");
// 启动Producer实例
defaultMQProducer.start();
//TagB作为过滤条件
Message msg = new Message("mq9","TagB",("rocketmq-filter-tag---" + System.currentTimeMillis()).getBytes());
SendResult send = defaultMQProducer.send(msg);
System.out.println(send.getMsgId());
// 如果不再发送消息,关闭Producer实例。
defaultMQProducer.shutdown();
}
}
消费者
Consumer.subscribe(“mq9”,“TagA || TagB”);
绑定消息的主题,参数一:topic,参数二:过滤表达式,每个Tag之间用||分割,意思是只消费该topic中,Tag为TagA或者TagB的消息,假如生产者发送了Tag为TagC的消息,它就不会消费
public class Consumer {
public static void main(String[] args) throws Exception{
//创建一个拉取消息的消费者对象
DefaultMQPushConsumer Consumer =new DefaultMQPushConsumer("rocketmq-consumer");
//设置名字地址
Consumer.setNamesrvAddr("localhost:9876");
//绑定消息的主题
Consumer.subscribe("mq9","TagA || TagB");
//消费者监听处理消息方法
Consumer.registerMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt messageExt : list) {
String msg = new String(messageExt.getBody());
System.out.println(msg);
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//启动消费者
Consumer.start();
}
}
Sql92消息过滤
生产者
//设置sql92表达式,相当于:age=21
msg.putUserProperty(“age”,String.valueOf(21));
public class Producer {
public static void main(String[] args) throws Exception {
// 实例化消息生产者Producer
DefaultMQProducer defaultMQProducer = new DefaultMQProducer("rocketmq-producer");
// 设置NameServer的地址
defaultMQProducer.setNamesrvAddr("localhost:9876");
// 启动Producer实例
defaultMQProducer.start();
Message msg = new Message("mq10",("rocketmq-filter-sql92---" + System.currentTimeMillis()).getBytes());
//设置sql92表达式
msg.putUserProperty("age",String.valueOf(21));
SendResult send = defaultMQProducer.send(msg);
System.out.println(send.getMsgId());
// 如果不再发送消息,关闭Producer实例。
defaultMQProducer.shutdown();
}
}
消费者
//绑定消息的主题,通过sql92表达式过滤,这里的意思是这个消费者只会消费topic为mq10,且表达式中age>18的消息,上面的消费者发送的消息中age设置为21了,所以它会消费这条消息
Consumer.subscribe(“mq10”, MessageSelector.bySql(“age>18”));
public class Consumer {
public static void main(String[] args) throws Exception{
//创建一个拉取消息的消费者对象
DefaultMQPushConsumer Consumer =new DefaultMQPushConsumer("rocketmq-consumer");
//设置名字地址
Consumer.setNamesrvAddr("localhost:9876");
//绑定消息的主题
Consumer.subscribe("mq10", MessageSelector.bySql("age>18"));
//消费者监听处理消息方法
Consumer.registerMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt messageExt : list) {
String msg = new String(messageExt.getBody());
System.out.println(msg);
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//启动消费者
Consumer.start();
}
}
SpringBoot集成RocketMQ
创建生产者工程
添加依赖:
<!--父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.3</version>
</dependency>
</dependencies>
在resources目录下创建application.properties文件
rocketmq.name-server=127.0.0.1:9876
rocketmq.producer.group=my-group
server.port=9090
写一个生产者的Cotroller:
@RestController
public class ProducerController {
@Autowired
private RocketMQTemplate rocketMQTemplate;
/*
* 同步发送
*/
@RequestMapping("/sync")
public String sync(String msg){
rocketMQTemplate.syncSend("01-sync",msg);
return "发送成功:"+msg;
}
/*
* 异步发送
*/
@RequestMapping("/async")
public String async(String msg){
rocketMQTemplate.asyncSend("02-async",msg, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println(sendResult.getMsgId());
}
@Override
public void onException(Throwable throwable) {
System.out.println(throwable.getMessage());
}
});
return "发送成功:"+msg;
}
/*
* 一次性发送
*/
@RequestMapping("/oneWay")
public String oneWay(String msg){
rocketMQTemplate.sendOneWay("03-oneWay",msg);
return "发送成功:"+msg;
}
/*
* 集群消费模式
* 在集群了两个消费者的情况下,我们发送10条消息来进行测试,
* 这10条消息会被两个消费者分工消费,一条消息只会被某个消费者消费
*/
@RequestMapping("/clustering")
public String clustering(String msg){
for (int i = 0; i <10 ; i++) {
rocketMQTemplate.syncSend("clustering",msg);
}
return "发送成功:"+msg;
}
/*
* 广播消费模式
* 有两个消费者的广播模式下,我们发送一条消息,发现两个消费者都去消费了这条数据
*/
@RequestMapping("/broad")
public String broad(String msg){
rocketMQTemplate.syncSend("broad",msg);
return "发送成功:"+msg;
}
/*
* 延迟发送
* message: 消息
* timeout: 超时时间,单位毫秒
* delayLevel:延迟等级 1为1s,2为5s,3为10s.......
* rocketMQTemplate.syncSend("delay",MessageBuilder.withPayload(msg).build(),3000,2);
*/
@RequestMapping("/delay")
public String delay(String msg){
//Message message = new Message("delay",msg.getBytes());
rocketMQTemplate.syncSend("delay",
MessageBuilder.withPayload(msg).build(),3000,2);
return "发送成功:"+msg;
}
/*
* Tag过滤
* destination参数topic和Tag用冒号":"分割:topic:Tag => filter-tag:TagA
* rocketMQTemplate.syncSend("filter-tag"+":"+"TagA",msg);
*/
@RequestMapping("/tag")
public String tag(String msg){
rocketMQTemplate.syncSend("filter-tag"+":"+"TagA",msg);
return "发送成功:"+msg;
}
/*
* sql92过滤
* map参数把key-value作为过滤条件 => age=20
* rocketMQTemplate.convertAndSend("filter-sql92",msg,map);
*/
@RequestMapping("/sql92")
public String sql92(String msg){
HashMap<String, Object> map = new HashMap<>();
map.put("age",20);
rocketMQTemplate.convertAndSend("filter-sql92",msg,map);
return "发送成功:"+msg;
}
}
创建一个消费者的工程
添加依赖:
<!--父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.3</version>
</dependency>
</dependencies>
在resources目录下创建application.properties文件
rocketmq.name-server=127.0.0.1:9876
server.port=9091
编写consumer来测试
在SpringBoot项目中,使用@RocketMQMessageListener注解来设置topic,消费分组,消费模式,过滤类型,过滤表达式…
1.创建consumer1测试同步发送
@Component
@RocketMQMessageListener(
topic = "01-sync",
consumerGroup = "wolfcode-consumer"
)
public class Consumer implements RocketMQListener<String>{
@Override
public void onMessage(String s) {
System.out.println(s);
}
}
访问http://127.0.0.1:9090/sync?msg=sync123,进行测试
2.创建consumer2测试异步发送
@Component
@RocketMQMessageListener(
topic = "02-async",
consumerGroup = "wolfcode-consumer"
)
public class Consumer implements RocketMQListener<String>{
@Override
public void onMessage(String s) {
System.out.println(s);
}
}
访问http://127.0.0.1:9090/async?msg=async123,进行测试
3.创建consumer3测试一次性发送
@Component
@RocketMQMessageListener(
topic = "03-oneWay",
consumerGroup = "wolfcode-consumer"
)
public class Consumer implements RocketMQListener<String>{
@Override
public void onMessage(String s) {
System.out.println(s);
}
}
访问http://127.0.0.1:9090/oneWay?msg=oneWay123,进行测试
4.创建consumer4和consumer5测试集群模式
@Component
@RocketMQMessageListener(
topic = "broad",
consumerGroup = "wolfcode-consumer",
messageModel = MessageModel.CLUSTERING
)
public class Consumer4 implements RocketMQListener<String>{
@Override
public void onMessage(String s) {
System.out.println("消费者1:"+s);
}
}
@Component
@RocketMQMessageListener(
topic = "broad",
consumerGroup = "wolfcode-consumer",
messageModel = MessageModel.CLUSTERING
)
public class Consumer5 implements RocketMQListener<String>{
@Override
public void onMessage(String s) {
System.out.println("消费者2:"+s);
}
}
访问http://127.0.0.1:9090/clustering?msg=clustering123,发现一条消息只会被某个消费者消费
5.创建consumer4和consumer5测试广播模式
这里跟上面的集群一样,只需要把messageModel 修改为 MessageModel.BROADCASTING就可以了
访问http://127.0.0.1:9090/broad?msg=broad123,发现两个消费者都消费了同一条消息
6.创建consumer6测试消息延迟
@Component
@RocketMQMessageListener(
topic = "delay",
consumerGroup = "wolfcode-consumer1"
)
public class Consumer6 implements RocketMQListener<String>{
@Override
public void onMessage(String s) {
System.out.println(s);
}
}
访问http://127.0.0.1:9090/delay?msg=delay123,发现消费者在消息发送成功,过了5秒钟才去消费这条消息
7.创建consumer7测试Tag消息过滤
@Component
@RocketMQMessageListener(
topic = "filter-tag",
consumerGroup = "wolfcode-consumer1",
selectorType = SelectorType.TAG,
selectorExpression = "TagA"
)
public class Consumer7 implements RocketMQListener<String>{
@Override
public void onMessage(String s) {
System.out.println(s);
}
}
访问http://127.0.0.1:9090/tag?msg=tag123,发现消费者只会消费topic = “filter-tag” 且 Tag=TagA的消息,要是生产者发送的消息Tag=TagB,则该消费者不会消费这条消息
8.创建consumer8测试sql92消息过滤
@Component
@RocketMQMessageListener(
topic = "filter-sql92",
consumerGroup = "wolfcode-consumer1",
selectorType = SelectorType.SQL92,
selectorExpression = "age > 18"
)
public class Consumer8 implements RocketMQListener<String>{
@Override
public void onMessage(String s) {
System.out.println(s);
}
}
访问http://127.0.0.1:9090/sql92?msg=sql92123,发现消费者只会消费topic = “filter-sql92” 且 表达式中的age>18的消息,要是生产者发送的消息age=15,则该消费者不会消费这条消息