mq使用
maven
xml
配置
<!-- mq-->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.1.1</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</exclusion>
</exclusions>
</dependency>
# mq
参数
rocketmq:
name-server: 127.0.0.1:9876
producer:
group:
/**
* @Author
* @Date Created in 2022/06/07
* @Describe:
*/
import org.apache.rocketmq.spring.annotation.ExtRocketMQTemplateConfiguration;
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQHeaders;
import org.apache.rocketmq.spring.support.RocketMQUtil;
import org.springframework.messaging.Message;
import org.springframework.messaging.converter.StringMessageConverter;
import java.util.concurrent.ConcurrentHashMap;
/**
* @description:
事务消息监听器
*
关于
@RocketMQTransactionListener
这个注解,有点奇怪。
2.0.4
版本中,是需要指定
txProducerGroup
指向一个消息发送者组。不同的组可以有不同的事务消息逻辑。
*
但是到了
2.1.1
版本,只能指定
rocketMQTemplateBeanMame
,也就是说如果你有多个发送者组需要有不同的事务消息逻辑,那就需要定义多个
RocketMQTemplate
。
*
而且这个版本中,虽然重现了我们在原生
API
中的事务消息逻辑,但是测试过程中还是发现一些奇怪的特性,用的时候要注意点。
**/
//@RocketMQTransactionListener(txProducerGroup = "springBootGroup2")
@RocketMQTransactionListener(rocketMQTemplateBeanName = "rocketMQTemplate")
public class MyTransactionImpl implements RocketMQLocalTransactionListener {
private ConcurrentHashMap<Object, Message> localTrans = new ConcurrentHashMap<>();
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
Object transId = msg.getHeaders().get(RocketMQHeaders.PREFIX + RocketMQHeaders.TRANSACTION_ID);
String destination = arg.toString();
localTrans.put(transId, msg);
//
这个
msg
的实现类是
GenericMessage
,里面实现了
toString
方法
//
在
Header
中自定义的
RocketMQHeaders.TAGS
属性,到这里就没了。但是
RocketMQHeaders.TRANSACTION_ID
这个属性就还在。
//
而
message
的
Header
里面会默认保存
RocketMQHeaders
里的属性,但是都会加上一个
RocketMQHeaders.PREFIX
前缀
System.out.println("executeLocalTransaction msg = " + msg);
//
转成
RocketMQ
的
Message
对象
org.apache.rocketmq.common.message.Message message = RocketMQUtil.convertToRocketMessage(new StringMessageConverter(), "UTF-8"
String tags = message.getTags();
if (StringUtils.contains(tags, "TagA")) {
return RocketMQLocalTransactionState.COMMIT;
} else if (StringUtils.contains(tags, "TagB")) {
return RocketMQLocalTransactionState.ROLLBACK;
} else {
return RocketMQLocalTransactionState.UNKNOWN;
}
}
//
延迟检查的时间间隔要有点奇怪。
@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
String transId = msg.getHeaders().get(RocketMQHeaders.PREFIX + RocketMQHeaders.TRANSACTION_ID).toString();
Message originalMessage = localTrans.get(transId);
//
这里能够获取到自定义的
transaction_id
属性
System.out.println("checkLocalTransaction msg = " + originalMessage);
//
获取标签时,自定义的
RocketMQHeaders.TAGS
拿不到,但是框架会封装成一个带
RocketMQHeaders.PREFIX
的属性
// String tags = msg.getHeaders().get(RocketMQHeaders.TAGS).toString();
String tags = msg.getHeaders().get(RocketMQHeaders.PREFIX + RocketMQHeaders.TAGS).toString();
if (StringUtils.contains(tags, "TagC")) {
return RocketMQLocalTransactionState.COMMIT;
} else if (StringUtils.contains(tags, "TagD")) {
return RocketMQLocalTransactionState.ROLLBACK;
} else {
return RocketMQLocalTransactionState.UNKNOWN;
}
}
}
import org.apache.rocketmq.spring.annotation.ExtRocketMQTemplateConfiguration;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
生产者
/**
* @Author
* @Date Created in 2022/06/07
* @Describe:
*/
// -------
@ExtRocketMQTemplateConfiguration()
public class ExtRocketMQTemplate extends RocketMQTemplate {
}
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
/**
* @Author
* @Date Created in 2022/06/07
* @Describe:
*/
@Component
public class SpringBootProducer {
@Autowired
private RocketMQTemplate rocketMQTemplate;
//
发送消息的实例
public void sendMessage(String topic, String msg) {
rocketMQTemplate.convertAndSend(topic, msg);
}
//
发送事务消息的实例
public void sendMessageInTransaction(String topic, String msg) throws InterruptedException {
String[] tags = {"TagA", "TagB", "TagC", "TagD", "TagE"};
for (int i = 0; i < 10; i++) {
Message<String> message = MessageBuilder.withPayload(msg).build();
String destination = topic + ":" + tags[i % tags.length];
TransactionSendResult sendResult =
rocketMQTemplate.sendMessageInTransaction(destination, message, destination);
System.out.printf("%s%n", sendResult);
Thread.sleep(10);
}
}
}
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.dataease.controller.sys.request.SysRoleCreateRequest;
import io.dataease.controller.sys.request.SysUserCreateRequest;
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author
* @Date Created in 2022/06/07
* @Describe:
*/
@Component
@RestController
@RocketMQMessageListener(consumerGroup = "springBootGroup", topic = "TestTopi2", consumeMode = ConsumeMode.ORDERLY)
public class SpringBootConsumer implements RocketMQListener {
@Override
public void onMessage(Object message) {
//
业务逻辑
}
}
自己创建消费者(原)可不用,现未经过测试,原来可用
@GetMapping("/getMqData")
public String getMqOfGz() throws MQClientException {
//
消费者的组名,这个和
kafka
是一样
,
这里需要注意的是,
String url = environment.getProperty("mq.url");
String group = environment.getProperty("mq.group");
String title = environment.getProperty("mq.title");
Integer num = parMqGroupService.selectParMqGroupByLastId()+1;
parMqGroupService.insertParMqGroup(new ParMqGroup());
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(group+num);
//
指定
NameServer
地址,多个地址以
;
隔开
consumer.setNamesrvAddr(url);
//
设置
Consumer
第一次启动是从队列头部开始消费还是队列尾部开始消费
//
如果非第一次启动,那么按照上次消费的位置继续消费
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//
订阅
PushTopic
下
Tag
为
push
的消息
consumer.subscribe(title, "*"); //*
表示不过滤,可以通过
tag
来过滤,比如
:”tagA”
/*
*
注册消息监听回调这里有两种监听,
MessageListenerConcurrently
以及
MessageListenerOrderly
*
前者是普通监听,后者是顺序监听。这块在后续单独说明
*/
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt>
msgs,
ConsumeConcurrentlyContext context) {
System.out.println(Thread.currentThread().getName()
+ " Receive New Messages: " + msgs.size());
MessageExt msg = msgs.get(0);
if (msg.getTopic().equals(title)) {
//
执行
TopicTest1
的消费逻辑
if (msg.getTags() != null) {
//
执行
TagA
的消费
System.out.println(new String(msg.getBody()));
LoginLog loginLog = new LoginLog();
loginLog.setLoginOperation(new String(msg.getBody()));
loginLogService.insertLoginLog(loginLog);
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;//
返回消息消费状态
}