springboot整合rocketmq消费者拉取消息模式
一、rocketmq消息队列的推拉模式介绍:
- 推送模式(push):消息的生产者将消息发送到broker,然后broker将消息主动推送给订阅了该消息的消费者端。
- 拉取模式(pull):消息生产者将消息发送到broker上,然后由消费者自发的去broker去拉取消息。
为什么要使用消费端拉取消息消费的模式:
我们都知道,消息中间件可以发布或者推送消息给消费者端,如果请求的并发不是很高,且消费者模块的处理能力还不错的情况下,这种推送处理的模式是没什么问题的。
但是,如果是高并发请求情况下,像这种直接将消息推送给消费端模块的方法很可能会导致消费端模块出现宕机或者程序卡死的问题,此时不妨尝试使用消费端拉取模式(pull)来实现业务消息处理。
二、案例展示
案例采用springboot项目,java1.8版本,rocketmq4.3版本;
2.1添加pom依赖
<!--rocket客户端依赖-->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.3.0</version>
</dependency>
2.2 配置生产者
@Configuration
@Slf4j
public class RocketMQConfiguration {
/**
* 创建支持消息事务发送的实例
* @return
* @throws MQClientException
*/
@Bean
public DefaultMQProducer transactionProducer()throws MQClientException{
TransactionMQProducer producer = new TransactionMQProducer("user_group");
producer.setInstanceName("user_producer_instance");
producer.setNamesrvAddr("localhost:9876");
producer.setRetryTimesWhenSendAsyncFailed(10);
producer.start();
log.info("支持事务消息的实例创建完成....");
return producer;
}
}
2.3 发布消息,并在另一个接口拉取消息测试效果
package com.demo.rocketmq.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.demo.rocketmq.dto.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.PullResult;
import org.apache.rocketmq.client.consumer.PullStatus;
import org.apache.rocketmq.client.producer.*;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Set;
@Slf4j
@RestController
@Api
public class ProducerController {
@Autowired
private DefaultMQProducer defaultProducer;
@Autowired
private TransactionMQProducer transactionProducer;
/**
* 发送普通消息
*/
@GetMapping("/sendMessage")
@ApiOperation(value = "发送普通消息", notes = "发送普通消息")
public void sendMsg() {
log.info("进入发送普通消息方法,发送5个用户==={}",defaultProducer.toString());
User user = new User();
for(int i=0;i<5;i++){
user.setId(String.valueOf(i));
user.setUsername("yangshilei"+i);
String json = JSON.toJSONString(user);
Message msg = new Message("user-topic","white",json.getBytes());
try {
SendResult result = defaultProducer.send(msg);
log.info("发送次数{}:消息id={}:发送状态={}",i,result.getMsgId(),result.getSendStatus());
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 测试消费者拉取消息,本样例中只拉取了几条消息,并将拉取到的消息日志写出来。
*/
@ApiOperation(value = "测试消费者拉取消息", notes = "测试消费者拉取消息")
@GetMapping("/test/getmessage")
public void testConsumer() throws Exception {
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("user_consumer_group");
consumer.setNamesrvAddr("127.0.0.1:9876");
consumer.setInstanceName("user_consumer_instance");
consumer.start();
System.out.println("消费者创建完成");
Set<MessageQueue> messageQueues = consumer.fetchSubscribeMessageQueues("user-topic");
System.out.println("拉取到的队列数量"+messageQueues.size());
for(MessageQueue queue : messageQueues){
System.out.println("遍历队列queue"+queue);
long offset = consumer.fetchConsumeOffset(queue, true);
System.out.println("consumer from the queue:" + queue + ":" + offset);
for(int i = 0; i < 10;i++){
// 在队列中拉取不到消息会一直阻塞等待着,直到能拉取到消息
// PullResult pullResult = consumer.pullBlockIfNotFound(queue, null, consumer.fetchConsumeOffset(queue, false), 1);
// 在队列中拉取不到消息就结束
PullResult pullResult = consumer.pull(queue, null, consumer.fetchConsumeOffset(queue, false), 1);
consumer.updateConsumeOffset(queue,pullResult.getNextBeginOffset());
switch (pullResult.getPullStatus()){
case FOUND:
List<MessageExt> messageExtList = pullResult.getMsgFoundList();
for (MessageExt m : messageExtList) {
System.out.println("拉取到数据===="+JSONObject.toJSONString(m));
}
break;
case NO_MATCHED_MSG:
break;
case NO_NEW_MSG:
break;
case OFFSET_ILLEGAL:
break;
default:
break;
}
}
}
System.out.println("关闭消费者");
consumer.shutdown();
}
}
2.4 测试一下效果:
模块中添加的swagger的依赖,没有的可以用postman测试:先调用消息,然后调用拉取接口;
日志展示效果:
我们可以复制一条被加密的消息,然后反编译出来:比如下面这条body中的字符串
“body”:“eyJpZCI6IjMiLCJ1c2VybmFtZSI6InlhbmdzaGlsZWkzIn0=”