RocketMQ消息队列
一、什么是RocketMQ
RocketMQ是一款分布式、队列模型的消息中间件,具有以下特点:
1、支持严格的消息顺序;
2、支持Topic与Queue两种模式;
3、亿级消息堆积能力;
4、比较友好的分布式特性;
5、同时支持Push与Pull方式消费消息;
二、Spring中的配置
1. producer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 顺序执行(可分区): 单线程,一个执行完毕继续执行下一个
分区顺序消息中区分不同分区的关键字段,sharding key于普通消息的key是完全不同的概念。
全局顺序消息,该字段可以设置为任意非空字符串。
-->
<bean id="scProducer" class="com.aliyun.openservices.ons.api.bean.OrderProducerBean" init-method="start"
destroy-method="shutdown">
<property name="properties"> <!--生产者配置信息-->
<props>
<prop key="ProducerId">${ProducerId_SC}</prop> <!--请替换为自己的账户信息-->
<prop key="AccessKey">${AccessKey}</prop>
<prop key="SecretKey">${SecretKey}</prop>
</props>
</property>
</bean>
<!-- 普通模式:多线程 先进先出 -->
<bean id="mongoDBProducer" class="com.aliyun.openservices.ons.api.bean.ProducerBean" init-method="start"
destroy-method="shutdown">
<property name="properties"> <!--生产者配置信息-->
<props>
<prop key="ProducerId">${ProducerId_Mongodb}</prop> <!--请替换为自己的账户信息-->
<prop key="AccessKey">${AccessKey}</prop>
<prop key="SecretKey">${SecretKey}</prop>
</props>
</property>
</bean>
</beans>
- consumer.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="consumerSc" class="com.aliyun.openservices.ons.api.bean.OrderConsumerBean"
init-method="start" destroy-method="shutdown">
<property name="properties">
<map>
<entry key="ConsumerId" value="${ConsumerId_SC}"/> <!-- CID,请替换 -->
<entry key="AccessKey" value="${AccessKey}"/> <!-- ACCESS_KEY,请替换 -->
<entry key="SecretKey" value="${SecretKey}"/><!-- SECRET_KEY,请替换 -->
<entry key="ONSAddr" value="${ONSAddr}"/>
</map>
</property>
<property name="subscriptionTable">
<map>
<entry value-ref="messageListenerSc">
<key>
<bean class="com.aliyun.openservices.ons.api.bean.Subscription">
<property name="topic" value="${Topic_SC}"/> <!-- Topic_SMS,请替换 -->
<property name="expression" value="*"/><!-- MessageType名: 多个messageType 用 “||”分割 -->
</bean>
</key>
</entry>
</map>
</property>
</bean>
<bean id="consumerMongoDB" class="com.aliyun.openservices.ons.api.bean.ConsumerBean"
init-method="start" destroy-method="shutdown">
<property name="properties">
<map>
<entry key="ConsumerId" value="${ConsumerId_Mongodb}"/> <!-- CID,请替换 -->
<entry key="AccessKey" value="${AccessKey}"/> <!-- ACCESS_KEY,请替换 -->
<entry key="SecretKey" value="${SecretKey}"/><!-- SECRET_KEY,请替换 -->
<entry key="ONSAddr" value="${ONSAddr}"/>
</map>
</property>
<property name="subscriptionTable">
<map>
<entry value-ref="messageListenerMongoDB">
<key>
<bean class="com.aliyun.openservices.ons.api.bean.Subscription">
<property name="topic" value="${Topic_Mongodb}"/> <!-- Topic_SMS,请替换 -->
<property name="expression" value="*"/><!-- MessageType名: 多个messageType 用 “||”分割 -->
</bean>
</key>
</entry>
</map>
</property>
</bean>
<!-- 消息处理器 mongodb -->
<bean id="messageListenerMongoDB" class="com.ztyijia.erp.task.consumer.listener.MessageListenerMongoDBImpl"/>
<!-- 消息处理器 社交 -->
<bean id="messageListenerSc" class="com.ztyijia.erp.task.consumer.listener.MessageListenerScImpl"/>
</beans>
三、代码中的应用
1.发送消息 (顺序)
private boolean sendScMsg(OrderProducer producer, Message msg,String userId) {
try {
// 设置代表消息的业务关键属性,请尽可能全局唯一
// 以方便您在无法正常收到消息情况下,可通过MQ 控制台查询消息并补发
// 注意:不设置也不会影响消息正常收发
msg.setKey(getUniqueKey());
// 分区顺序消息中区分不同分区的关键字段,sharding key于普通消息的key是完全不同的概念。
// 全局顺序消息,该字段可以设置为任意非空字符串。
// 以userId分区
SendResult sendResult = producer.send(msg,userId);
assert sendResult != null;
System.out.println("mq send success: " + sendResult.getMessageId());
return true;
} catch (ONSClientException e) {
System.out.println("发送失败");
return false;
}
}
- 发送消息(普通)
private boolean sendMsg(Producer producer, Message msg) {
try {
// 设置代表消息的业务关键属性,请尽可能全局唯一
// 以方便您在无法正常收到消息情况下,可通过MQ 控制台查询消息并补发
// 注意:不设置也不会影响消息正常收发
msg.setKey(getUniqueKey());
SendResult sendResult = producer.send(msg);
assert sendResult != null;
System.out.println("mq send success: " + sendResult.getMessageId());
return true;
} catch (ONSClientException e) {
System.out.println("发送失败");
return false;
}
}
- 生产消息
CommentBean commentBean = new CommentBean();
commentBean.setUserId(userId);
commentBean.setCommentId(commentId);
// 将对象序列化
byte[] commentBeanByte = TranscoderUtil.serialize(commentBean, CommentBean.class);
producerMQService.sendSCProducer(RocketMqTags.Comment_Point, commentBeanByte, userId.toString());
- 消费消息
package com.ztyijia.erp.task.consumer.listener;
/**
* mq监听
*/
public class MessageListenerScImpl implements MessageOrderListener {
@Autowired
@Qualifier(value = "scCommentTask")
private SCCommentTask commentTask;
@Override
public OrderAction consume(Message message, ConsumeOrderContext context) {
System.out.println("SMS Receive: " + message.getMsgID());
try {
String tag = message.getTag();
System.err.println("【当前的TAG】:【" + tag + "】");
// 序列化的消息内容
byte[] body = message.getBody();
// 根据类型处理
if (tag.equals(RocketMqTags.Card_Num.toString())) {
// 评论点赞
ErpCard erpCard = TranscoderUtil.deserializeGetObj(body, ErpCard.class);
cardNumService.updateCardNum(erpCard);
return OrderAction.Success;
}
System.err.println("无数据");
return OrderAction.Success;
} catch (Exception e) {
//消费失败
System.err.println("【社交队列】消费出现异常");
e.printStackTrace();
return OrderAction.Suspend;
}
}
}