注意
看此篇之前,你需要了解RocketMQ在本地是如何工作的,如果还不会请先去找找资源学习一下哦
配置文件
application.yml中关于RocketMQ的配置如下:
bg:
rocketmq:
# namesrvAddr地址
# namesrvAddr: 0.0.0.0:9876
namesrvAddr: 192.168.2.68:9876
# 生产者group名称
producerGroupName: producerGroupName
# 事务生产者group名称
transactionProducerGroupName: transactionProducerGroupName
# 消费者group名称
consumerGroupName: consumerGroupName
# 生产者实例名称
producerInstanceName: producerInstanceName
# 消费者实例名称
consumerInstanceName: consumerInstanceName
# 事务生产者实例名称
producerTranInstanceName: producerTranInstanceName
# 一次最大消费多少数量消息
consumerBatchMaxSize: 1
# 广播消费
consumerBroadcasting: false
# 消费的topic:tag
subscribe[0]: TopicTest1:TagA
# 启动的时候是否消费历史记录
enableHisConsumer: false
# 启动顺序消费
enableOrderConsumer: false
SpringBoot自动配置(核心)
需要写一个专门去读以bg.rockmq开头的配置并且需要注解
@Data
@ConfigurationProperties(RocketmqProperties.PREFIX)
public class RocketmqProperties {
public static final String PREFIX = "dg.rocketmq";
private String namesrvAddr;
private String producerGroupName;
private String transactionProducerGroupName;
private String consumerGroupName;
private String producerInstanceName;
private String consumerInstanceName;
private String producerTranInstanceName;
private int consumerBatchMaxSize;
private boolean consumerBroadcasting;
private boolean enableHisConsumer;
private boolean enableOrderConsumer;
private List<String> subscribe = new ArrayList<>();
}
根据SpringBoot的自动注入的原理还需要一个类来搞这个事
/**
* Producer:消息生产者,负责生产消息,一般由业务系统负责生产消息。
*
* Consumer:消息消费者,负责消费消息,一般是后台系统负责异步消费。
*
* Push Consumer:Consumer的一种,应用通常向Consumer对象注册一个Listener接口,一旦收到消息,Consumer对象立刻回调Linsener接口方法
*
* Pull Consumer:Consumer的一种,应用通常主动调用Consumer的拉消息方法从Broker拉消息,主动权由应用控制
*
* Consumer Group:一类Consumer的集合名称,这类Consumer通常消费一类消息,且消费逻辑一致。
*
* Broker:消息中转角色,负责存储消息,转发消息,一般也称为Server,在JMS规范中成为Provider
*
* Topic: 一个Topic有四个Queue
*/
@Configuration("MQConfiguration")
@EnableConfigurationProperties(RocketmqProperties.class)
@ConditionalOnProperty(prefix = RocketmqProperties.PREFIX, value = "namesrvAddr")
public class RocketmqAutoConfiguration {
private static final Logger log = LogManager.getLogger(RocketmqAutoConfiguration.class);
@Autowired
private RocketmqProperties properties;
@Autowired
private ApplicationEventPublisher publisher;
private static boolean isFirstSub = true;
private static long startTime = System.currentTimeMillis();
/**
* 初始化向rocketmq发送普通消息的生产者
*
* @throws Exception
*/
@Bean("DefaultMQProducer")
@ConditionalOnProperty(prefix = RocketmqProperties.PREFIX, value = "producerInstanceName")
public DefaultMQProducer defaultProducer() throws Exception {
/**
* 一个应用创建一个Producer,由应用来维护此对象,可以设置为全局对象或者单例<br>
* 注意:ProducerGroupName需要由应用来保证唯一<br>
* ProducerGroup这个概念发送普通的消息时,作用不大,但是发送分布式事务消息时,比较关键,
* 因为服务器会回查这个Group下的任意一个Producer
*/
DefaultMQProducer producer = new DefaultMQProducer(properties.getProducerGroupName());
producer.setNamesrvAddr(properties.getNamesrvAddr());
producer.setInstanceName(properties.getProducerInstanceName());
producer.setVipChannelEnabled(false);
producer.setRetryTimesWhenSendAsyncFailed(10);
producer.setRetryTimesWhenSendFailed(10);
/**
* Producer对象在使用之前必须要调用start初始化,初始化一次即可<br>
* 注意:切记不可以在每次发送消息时,都调用start方法
*/
producer.start();
log.info("RocketMq defaultProducer Started.");
// pubRegister(properties.getProducerGroupName());
return producer;
}
/**
* 初始化向rocketmq发送事务消息的生产者
*
* @throws Exception
*/
@Bean("TransactionMQProducer")
@ConditionalOnProperty(prefix = RocketmqProperties.PREFIX, value = "producerTranInstanceName")
public TransactionMQProducer transactionProducer() throws Exception {
/**
* 一个应用创建一个Producer,由应用来维护此对象,可以设置为全局对象或者单例<br>
* 注意:ProducerGroupName需要由应用来保证唯一<br>
* ProducerGroup这个概念发送普通的消息时,作用不大,但是发送分布式事务消息时,比较关键,
* 因为服务器会回查这个Group下的任意一个Producer
*/
TransactionMQProducer producer = new TransactionMQProducer(properties.getTransactionProducerGroupName());
producer.setNamesrvAddr(properties.getNamesrvAddr());
producer.setInstanceName(properties.getProducerTranInstanceName());
producer.setRetryTimesWhenSendAsyncFailed(10);
producer.setRetryTimesWhenSendFailed(10);
TransactionListener transactionListener = new TransactionListenerImpl();
ExecutorService executorService = new ThreadPoolExecutor(2, 5, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2000), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("client-transaction-msg-check-thread");
return thread;
}
});
producer.setVipChannelEnabled(false);
producer.setExecutorService(executorService);
producer.setTransactionListener(transactionListener);
/**
* Producer对象在使用之前必须要调用start初始化,初始化一次即可<br>
* 注意:切记不可以在每次发送消息时,都调用start方法
*/
producer.start();
log.info("RocketMq TransactionMQProducer Started.");
// pubRegister(properties.getProducerTranInstanceName());
return producer;
}
/**
* 初始化rocketmq消息监听方式的消费者
*
* @throws Exception
*/
@Bean
@ConditionalOnProperty(prefix = RocketmqProperties.PREFIX, value = "consumerInstanceName")
public DefaultMQPushConsumer pushConsumer() throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(properties.getConsumerGroupName());
consumer.setNamesrvAddr(properties.getNamesrvAddr());
consumer.setInstanceName(properties.getConsumerInstanceName());
//判断消息消费是否为广播模式
if (properties.isConsumerBroadcasting()) {
consumer.setMessageModel(MessageModel.BROADCASTING);
}
consumer.setConsumeMessageBatchMaxSize(
properties.getConsumerBatchMaxSize() == 0 ? 1 : properties.getConsumerBatchMaxSize());// 设置批量消费,以提升消费吞吐量,默认是1
consumer.setVipChannelEnabled(false);
/**
* 订阅指定topic下tags
*/
List<String> subscribeList = properties.getSubscribe();
for (String subscribe : subscribeList) {
consumer.subscribe(subscribe.split(":")[0], subscribe.split(":")[1]);
// subRegister(properties.getConsumerGroupName(), subscribe.split(":")[0]);
}
if (properties.isEnableOrderConsumer()) {
consumer.registerMessageListener((List<MessageExt> msgs, ConsumeOrderlyContext context) -> {
try {
context.setAutoCommit(true);
msgs = filter(msgs);
if (msgs.size() == 0)
return ConsumeOrderlyStatus.SUCCESS;
this.publisher.publishEvent(new RocketmqEvent(msgs, consumer));
} catch (Exception e) {
e.printStackTrace();
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
}
// 如果没有return success,consumer会重复消费此信息,直到success。
return ConsumeOrderlyStatus.SUCCESS;
});
} else {
consumer.registerMessageListener((List<MessageExt> msgs, ConsumeConcurrentlyContext context) -> {
try {
msgs = filter(msgs);
if (msgs.size() == 0)
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
this.publisher.publishEvent(new RocketmqEvent(msgs, consumer));
} catch (Exception e) {
e.printStackTrace();
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
// 如果没有return success,consumer会重复消费此信息,直到success。
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
}
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);// 延迟5秒再启动,主要是等待spring事件监听相关程序初始化完成,否则,回出现对RocketMQ的消息进行消费后立即发布消息到达的事件,然而此事件的监听程序还未初始化,从而造成消息的丢失
/**
* Consumer对象在使用之前必须要调用start初始化,初始化一次即可<br>
*/
try {
consumer.start();
} catch (Exception e) {
log.info("RocketMq pushConsumer Start failure!!!.");
log.error(e.getMessage(), e);
}
log.info("RocketMq pushConsumer Started.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
return consumer;
}
private List<MessageExt> filter(List<MessageExt> msgs) {
if (isFirstSub&& !properties.isEnableHisConsumer()) {
msgs = msgs.stream().filter(item -> startTime - item.getBornTimestamp() < 0).collect(Collectors.toList());
}
if (isFirstSub && msgs.size() > 0) {
isFirstSub = false;
}
return msgs;
}
}
工具类
上面用到的一个工具类RocketMqEvent,主要是把<msgs,consumer>封装成对象然后一起publishEvent
public class RocketmqEvent extends ApplicationEvent {
private static final long serialVersionUID = -4468405250074063206L;
private DefaultMQPushConsumer consumer;
private List<MessageExt> msgs;
public RocketmqEvent(List<MessageExt> msgs, DefaultMQPushConsumer consumer) throws Exception {
super(msgs);
this.consumer = consumer;
this.setMsgs(msgs);
}
}
而publishEvent是Spring的异步监听操作,所以紧跟着的是进入监听所以肯定有个监听器,配套起来看就是发布的时候发的参数是我们工具类RocketmqEvent对象,监听器收到的也是该对象
监听器会拿到该对象,之后通过对象里面的msgs属性来来记录日志
@Component
public class RocketMQListener implements ApplicationListener<RocketmqEvent> {
private static final Logger LOGGER = LoggerFactory.getLogger(RocketMQListener.class);
@Override
public void onApplicationEvent(RocketmqEvent event) {
List<MessageExt> msgs = event.getMsgs();
for (MessageExt messageExt: msgs) {
handle(messageExt);
}
}
private void handle(MessageExt messageExt) {
String key = messageExt.getKeys();
byte[] body = messageExt.getBody();
String value = new String(body, StandardCharsets.UTF_8);
String topic = messageExt.getTopic();
String tags = messageExt.getTags();
LOGGER.info("Received message, key : {}, value : {}", key, value);
System.out.println("topic:" + topic + ", tags:"+ tags);
}
}
两个请求规则(pojo类)
MQRequestData
@Data
public class MQRequestData {
/**
* 消息主题
*/
private String msgTopicName;
/**
* 消息主题标签
*/
private String msgTagName;
/**
* 消息key
*/
private String msgKey;
/**
* 消息内容主体
*/
private String msgBody;
}
MQResponseData
@Data
public class MQResponseData<T> {
//相应码
private String code;
//响应信息
private String msg;
//响应体
private T data;
}
目录
配置完毕之后Controller的架子就可以这么写让Service的实现类去具体操作消息队列(下一篇讲)