自娱自乐
RocketMQ与其他的消息中间件对比具有很明显的有事,最大的特点就是吞吐量巨大,以及毫秒级别的推送深受广大推荐。
直接上代码,看代码理解
基于项目使用,特地配置化操作
Yml:
rocketmq: name-server: 集群地址
producer: access-key: 项目
secret-key: 项目
push-consumer: access-key: 项目 secret-key: 项目
1、由于每个项目的topic以及分组、tag等都有很多 因此可以写在配置中实时读取,以上均为写,因为在代码中有写死操作
2.本文仅仅以发送同步详细,异步消息,异步回调函数消息,事务消息,作为示例
代码篇
1.发送消息基类
package com.databus.data.tests;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.remoting.exception.RemotingException;
/**
* 消息发送基类 每个生产者 都继承本类,重写发送
* @author xm
*/
public interface RocketMqEngine<T,U> {
/**
* 同步发送
* @param t
*/
void send(T t) throws MQBrokerException, RemotingException, InterruptedException, MQClientException;
/**
* 异步发送
* @param t
*/
void asyncSend(T t);
/**
* 异步发送
* @param t
*/
void syncSend(T t);
/**
*
* @param t 消息体BODY
* @param u 本地事务处理DATA
* @throws MQBrokerException 异常类型
* @throws RemotingException 异常类型
* @throws InterruptedException 异常类型
* @throws MQClientException 异常类型
*/
void sendTransaction(T t,U u) throws MQBrokerException, RemotingException, InterruptedException, MQClientException;
}
注:该基类用于具体实现那些发送方式,根据需要,可以扩充,以上示例定义了三个发送方式
void send(T t); 同步发送
void asyncSend(T t); 异步发送 回调
void syncSend(T t);异步发送
void sendTransaction(T t,U u) throws MQBrokerException, RemotingException, InterruptedException, MQClientException; 发送事务消息
2.发送消息工厂主类:Test1Producer
package com.databus.data.tests;
/**
* 第一个测试用的MQ生产者
*
* @author xm
*/
@Component
public class Test1Producer implements RocketMqEngine {
@Resource
private RocketMQTemplate rocketMQTemplate;
private String topic = "topic";
private String namesrvAddr = "集群地址";
private String accesskey = "你的accesskey";
private String secretKey = "你的secretKey ";
private String group = "分组";
@Resource
private TransactionMQProducer producer;
public Test1Producer() {
}
/**
* 封装BODY参数
*
* @param o
* @return
*/
private Message setMessageInfo(Object o) {
Message message = new Message() {
@Override
public Object getPayload() {
return JSON.toJSONString(o);
}
@Override
public MessageHeaders getHeaders() {
Map<String, Object> map = new HashMap<>();
return new MessageHeaders(map);
}
};
return message;
}
/**
* 创建事务消息体返回
*
* @param o
* @return
*/
private org.apache.rocketmq.common.message.Message setTransactionMessageInfo(Object o) {
org.apache.rocketmq.common.message.Message message = new org.apache.rocketmq.common.message.Message();
message.setTopic(topic);
message.setTags("tag2");
message.setBody(o.toString().getBytes());
return message;
}
/**
* 同步发送
*
* @param o
*/
@Override
public void send(Object o) throws MQBrokerException, RemotingException, InterruptedException, MQClientException {
producer.send(setTransactionMessageInfo(o));
}
@Override
public void asyncSend(Object o) {
rocketMQTemplate.asyncSend(topic, setMessageInfo(o), new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("发送成功了,消息体:" + JSON.toJSONString(o));
}
@Override
public void onException(Throwable throwable) {
System.out.println("发送失败了" + throwable.getMessage());
}
});
}
@Override
public void syncSend(Object o) {
rocketMQTemplate.syncSend(topic, setMessageInfo(o));
}
/**
* 事务消息发送
*
* @param o
*/
@Override
public void sendTransaction(Object o, Object localData) throws MQBrokerException, RemotingException, InterruptedException, MQClientException {
rocketMQTemplate.setProducer(producer);
this.producer.sendMessageInTransaction(setTransactionMessageInfo(o), localData);
}
/**
* 初始化事务执行器
*/
@PostConstruct
private void initTransactionMq() throws MQClientException {
this.producer.setTransactionListener(new Test1ProducerListener());
}
}
该类实现了事务的基类(自行定义),并且重写了定义的四个方法
@Resource private RocketMQTemplate rocketMQTemplate;
注入该类,可直接使用代理发送同步消息/异步消息,当人也可以使用我们手动创建的生产者直接发送消息(TransactionMQProducer )
@Resource private TransactionMQProducer producer;
注入该类,是因为我们在发送事务消息的时候,需要输入监听类,监听每个消息发送成功后,去实现本地事务,因此必须要注入。
initTransactionMq()该方法主要是用于设置监听类,没有监听类是不可以的
同时,该类也是发送事务消息必须的
3.监听类
package com.databus.data.tests;
/**
* 事务消息监听器
* @author xm
*/
@Component
public class Test1ProducerListener implements TransactionListener {
/**
* 本地事务处理方法
* @param msg Half(prepare) message
* @param arg Custom business parameter
* @return
*/
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
System.out.println("消息体:"+msg);
System.out.println("本地事务消息体:"+arg);
System.out.println("我处理好了");
return LocalTransactionState.COMMIT_MESSAGE;
}
/**
* 事务会查方法
* @param msg Check message
* @return
*/
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
System.out.println("呜呜呜,我处理失败了");
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}
该监听类主要用于实现本地事务以及MQ回查消息,他需要实现TransactionListener 类,并且重写他的两个方法
executeLocalTransaction:本地事务的实现
checkLocalTransaction:MQ回查处理
具体的实现由开发者决定,回查的处理多数是用本地存储的消息来验证或者是直接去自己本地事务处理是否成功的log日志
4、消费者
package com.databus.data.event;
@Component
@RocketMQMessageListener(topic = "data_service_test",
selectorExpression = "tag2||tag3",
consumerGroup = "data_service_dev_2")
public class DataServiceConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("我正在消费呢,"+message);
}
}
消费者主要是处理业务
具体实现开发者决定,示例钟,我监听了data_service_test主题下的tag2以及tag3的消息,进行处理,具体在开发中,message的消息体重,大多数会增设一个参数,比如tag标记他的来源,或者是具体的业务场景的key,用于事务处理时进行不同的逻辑,但是个人觉得,不同的tag不同的topic做不同的事,因此该种设计应该摒弃,用于解耦,我是什么场景,就给什么参数,其他无用的统统丢掉
注,该模型可以用于多topic,即如果你的业务中,有很多的topic,那么根据需要建多个生产者类,个人觉得,多个工厂类会比较合适,目前有使用rocketMQTemplate进行统一处理的,因此topic没有进行封装,刚刚接收项目的根本就不知道这个怎么处理的,特别是还使用了enevt等监听器操作,业务和消息分离,带来巨大隐患,因此个人觉得二阶段提交是不错的选择。