本篇文章默认你已经有RocketMQ的基础:
本篇内容默认你已经有SpringBoot的基础:
你只有简单具备上述知识,就可以继续往下阅读文章
com.alibaba
fastjson
1.2.47
org.apache.rocketmq
rocketmq-client
4.1.0-incubating
引入fastjson及rocketmq-client依赖,这两个都是必须的。版本号根据自己实际需求可更改
思想:利用@Compoent注解让生产者实例受Spring容器管理,并且利用@PostConstruct实现生产者启动以及@PreDestory实现生产者关闭
注意事项:
/**
* 长连接producer抽象
*/
public abstract class AbstractMqProducer {
protected DefaultMQProducer producer;
@PostConstruct
public abstract void start() throws MQClientException;
@PreDestroy
public void shutdown() {
System.err.println("AbstractMqProducer @PreDestroy调用");
producer.shutdown();
}
}
生产者的抽象类,定义了start()和shutdown()方法。其中start()方法需要开发者进行重写。开发者需要在start()方法中,为producer进行初始化和启动工作。
/**
* 生产者示例1
*
* 利用SpringBoot的特性,首先将其注解Component,让Spring容器接管这个实例
* 利用PostConstruct来让实例化后的Bean进行后置处理
*/
@Component
public class TestProducer1 extends AbstractMqProducer{
@Value("${dy.rocketmq.producer.producerGroup}")
private String producerGroup;
@Value("${dy.rocketmq.namesrvAddr}")
private String namesrvAddr;
@Value("${dy.rocketmq.producer.instanceName}")
private String instanceName;
@Override
public void start() throws MQClientException {
if (null == producer) {
producer = new DefaultMQProducer(producerGroup);
producer.setNamesrvAddr(namesrvAddr);
producer.setInstanceName(instanceName);
}
producer.start();
System.out.println(namesrvAddr);
System.err.println("rocketmq producer is starting...");
}
public boolean send(String topic, String tag, String key, TestMqMessageDto msg) {
try {
Message message = new Message(
topic, tag, key,
JSONObject.toJSONString(msg).getBytes("utf-8")
);
SendResult sendResult = producer.send(message);
System.err.println("消息生产结果:" + sendResult);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
// 使用方式:在Spring接管的Bean中,直接使用@Autowired来获取producer实例
@Autowired
private TestProducer1 testProducer1;
{
// 直接使用 发送消息
testProducer1.send("topic", "tag", "key", 内容);
}
/**
* MQ消费者抽象类
*
* 定义消费消息的逻辑
*/
public abstract class AbstractMqConsumer {
protected DefaultMQPushConsumer consumer;
// 是否允许顺序消费
protected boolean isOrderConsumer = false;
@Autowired
private ApplicationEventPublisher publisher;
/**
* 初始化consumer,由开发者控制
*
* 例如:
* try {
* consumer = new DefaultMQPushConsumer(consumerGroup);
* consumer.setNamesrvAddr(namesrvAddr);
* consumer.setMessageModel(MessageModel.CLUSTERING);
* consumer.setConsumeMessageBatchMaxSize(1);
* consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
* consumer.subscribe("TopicTest", "*");
* } catch (MQClientException e) {
* e.printStackTrace();
* }
*/
abstract void start0();
@PostConstruct
private void start() {
if (null == consumer) {
start0();
}
if (isOrderConsumer) {
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List msgs, ConsumeOrderlyContext consumeOrderlyContext) {
try {
consumeOrderlyContext.setAutoCommit(true);
if (null == msgs || msgs.size() == 0) {
return ConsumeOrderlyStatus.SUCCESS;
}
publisher.publishEvent(new MqMessageEvent(consumer, msgs));
} catch (Exception e) {
e.printStackTrace();
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
}
else {
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
try {
if (null == msgs || msgs.size() == 0) {
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
publisher.publishEvent(new MqMessageEvent(consumer, msgs));
} catch (Exception e) {
e.printStackTrace();
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
}
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000L);
consumer.start();
System.err.println("rocketmq consumer server is starting...");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
@PreDestroy
public void shutdown() {
consumer.shutdown();
}
}
抽象了消费者,所有消费者继承这个类,然后实现start0()方法。
start0方法:由开发者去编写,目的是初始化consumer,但无需调用consumer.start();
start()方法: 该方法定义了ApplicationEventPublisher发布消息事件的逻辑,他会根据你的consumer类型(顺序消费,并发消费)来注册不同的MessageListener
PS:
/**
* 消费者1示例
*/
@Component
public class TestConsumer1 extends AbstractMqConsumer {
@Value("${dy.rocketmq.consumer.consumerGroup}")
private String consumerGroup;
@Value("${dy.rocketmq.namesrvAddr}")
private String namesrvAddr;
@Override
void start0() {
try {
consumer = new DefaultMQPushConsumer(consumerGroup);
// 设置namesrv地址
consumer.setNamesrvAddr(namesrvAddr);
// 设置集群消费
consumer.setMessageModel(MessageModel.CLUSTERING);
// 设置每次消费消息的数量,官方一般建议1条,除非你有批量处理的需求
consumer.setConsumeMessageBatchMaxSize(1);
// 设置消费策略
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
// 设置订阅的topic和tags
consumer.subscribe("TopicTest", "*");
// ... 根据自己的需求设置consumer其他参数
} catch (MQClientException e) {
e.printStackTrace();
}
}
}
/**
* 监听rocketMQ消费消息的spring event
*/
public class MqMessageEvent extends ApplicationEvent {
private DefaultMQPushConsumer consumer;
private List msgs;
public MqMessageEvent(DefaultMQPushConsumer consumer, List msgs) {
super(msgs);
this.consumer = consumer;
this.msgs = msgs;
}
public DefaultMQPushConsumer getConsumer() {
return consumer;
}
public void setConsumer(DefaultMQPushConsumer consumer) {
this.consumer = consumer;
}
public List getMsgs() {
return msgs;
}
public void setMsgs(List msgs) {
this.msgs = msgs;
}
}
最后定义消费消息的Service:
/**
* 用于监听MqMessageEvent的服务
* 消费MQ消息
*
* 一般两种方式:
* (1)第一种:这个类的作用就是监听SpringEvent事件,然后再根据消息分发给其他Service进行处理,所以这里一般不会包含业务逻辑代码
* (2)第二种:这个类的作用就是具体的消费消息类
*/
@Service
public class TestConsumerService {
/**
* 消费TopicTest下的TagA
* @param event
*/
@EventListener(condition = "#event.msgs[0].topic=='TopicTest' && #event.msgs[0].tags=='TagA'")
public void testConsumer(MqMessageEvent event) {
// 由于mq消费者设置了batch=1,所以每次都只会消费一条
MessageExt msg = event.getMsgs().get(0);
if (null != msg) {
// 具体的消费MessageExt的逻辑
}
}
/**
* 消费TopicTestB
* @param event
*/
@EventListener(condition = "#event.msgs[0].topic=='TopicTestB' ")
public void testConsumer(MqMessageEvent event) {
// 由于mq消费者设置了batch=1,所以每次都只会消费一条
MessageExt msg = event.getMsgs().get(0);
if (null != msg) {
// 具体的消费MessageExt的逻辑
if (msg.getTags() == "TagA") {
//消费TagA的消息
}
else if (msg.getTags() == 'TagB'){
//消费TagB的消息
}
}
}
}
这些注意点都是在学习源码的过程中总结的,希望对很多还未深入源码了解rocketmq的程序员,在使用mq的过程中有所注意: