阿里云RocketMQ
1、maven依赖
首先去阿里云控制台创建所需消息队列资源,包括消息队列 RocketMQ 的实例、Topic、Group ID (GID),以及鉴权需要的 AccessKey(AK)。
<dependency>
<groupId>com.aliyun.openservices</groupId>
<artifactId>ons-client</artifactId>
<version>1.8.4.Final</version>
</dependency>
2、配置文件
##-------鉴权需要的 AccessKey(AK)(实际项目,这里填写阿里云自己的账号信息)---
rocketmq.accessKey=xxAxxxxxxxxxx
rocketmq.secretKey=xxxxxxxxxiHxxxxxxxxxxxxxx## 实例TCP 协议公网接入地址(实际项目,填写自己阿里云MQ的公网地址)
rocketmq.nameSrvAddr=http://MQ_INST_***********85_BbM********************yuncs.com:80
#普通消息topic (实际项目,填写自己阿里云MQ中的topic名称和groupid)
rocketmq.topic=common
rocketmq.groupId=GID-message`在这里插入代码片`
rocketmq.tag=*
#定时/延时消息
rocketmq.timeTopic=time-lapse
rocketmq.timeGroupId=GID-message
rocketmq.timeTag=*
3、配置类
/**
* MQ配置加载
* @author xf
*/
@Slf4j
@Component
@Data
@Configuration
@EnableApolloConfig(value = {"sysConfig.yml"})
public class MqConfig {
private String accessKey;
private String secretKey;
private String nameSrvAddr;
private String topic;
private String groupId;
private String tag;
private String timeTopic;
private String timeGroupId;
private String timeTag;
public Properties getMqProperties() {
Properties properties = new Properties();
properties.setProperty(PropertyKeyConst.AccessKey, this.accessKey);
properties.setProperty(PropertyKeyConst.SecretKey, this.secretKey);
properties.setProperty(PropertyKeyConst.NAMESRV_ADDR, this.nameSrvAddr);
//设置发送超时时间,单位毫秒
properties.setProperty(PropertyKeyConst.SendMsgTimeoutMillis, "4000");
return properties;
}
}
4、给消息生产者注入配置信息,ProducerBean用于将Producer集成至Spring Bean中
/**
* MQ配置注入生成消息实例
*/
@Configuration
public class ProducerClient {
@Autowired
private MqConfig mqConfig;
@Bean(initMethod = "start", destroyMethod = "shutdown")
public ProducerBean buildProducer() {
//ProducerBean用于将Producer集成至Spring Bean中
ProducerBean producer = new ProducerBean();
producer.setProperties(mqConfig.getMqPropertie());
return producer;
}
}
5、生产者发送消息
/**
* MQ发送消息.
*
* @author xf
*/
@Component
public class ProducerUtil {
private Logger logger = LoggerFactory.getLogger(ProducerUtil.class);
@Autowired
private MqConfig config;
@Autowired
private ProducerBean producer;
/**
* 同步发送消息
* @param msgTag 标签,可用于消息小分类标注
* @param messageBody 消息body内容,生产者自定义内容
* @param msgKey 消息key值,建议设置全局唯一,可不传,不影响消息投递
* @return success:SendResult or error:null
*/
public SendResult sendMsg(String msgTag,byte[] messageBody,String msgKey) {
Message msg = new Message(config.getTopic(),msgTag,msgKey,messageBody);
return this.send(msg,Boolean.FALSE);
}
/**
* 同步发送定时/延时消息
* @param msgTag 标签,可用于消息小分类标注,对消息进行再归类
* @param messageBody 消息body内容,生产者自定义内容,二进制形式的数据
* @param msgKey 消息key值,建议设置全局唯一值,可不设置,不影响消息收发
* @param delayTime 服务端发送消息时间,立即发送输入0或比更早的时间
* @return success:SendResult or error:null
*/
public SendResult sendTimeMsg(String msgTag,byte[] messageBody,String msgKey,long delayTime) {
Message msg = new Message(config.getTimeTopic(),msgTag,msgKey,messageBody);
msg.setStartDeliverTime(delayTime);
return this.send(msg,Boolean.FALSE);
}
/**
* 发送单向消息
*/
public void sendOneWayMsg(String msgTag,byte[] messageBody,String msgKey) {
Message msg = new Message(config.getTopic(),msgTag,msgKey,messageBody);
this.send(msg,Boolean.TRUE);
}
/**
* 普通消息发送发放
* @param msg 消息
* @param isOneWay 是否单向发送
*/
private SendResult send(Message msg,Boolean isOneWay) {
try {
if(isOneWay) {
//由于在 oneway 方式发送消息时没有请求应答处理,一旦出现消息发送失败,则会因为没有重试而导致数据丢失。
//若数据不可丢,建议选用同步或异步发送方式。
producer.sendOneway(msg);
success(msg, "单向消息MsgId不返回");
return null;
}else {
//可靠同步发送
SendResult sendResult = producer.send(msg);
//获取发送结果,不抛异常即发送成功
if (sendResult != null) {
success(msg, sendResult.getMessageId());
return sendResult;
}else {
error(msg,null);
return null;
}
}
} catch (Exception e) {
error(msg,e);
return null;
}
}
//对于使用异步接口,可设置单独的回调处理线程池,拥有更灵活的配置和监控能力。
//根据项目需要,服务器配置合理设置线程数,线程太多有OOM 风险,
private ExecutorService threads = Executors.newFixedThreadPool(3);
//仅建议执行轻量级的Callback任务,避免阻塞公共线程池 引起其它链路超时。
/**
* 异步发送普通消息
* @param msgTag
* @param messageBody
* @param msgKey
*/
public void sendAsyncMsg(String msgTag,byte[] messageBody,String msgKey) {
producer.setCallbackExecutor(threads);
Message msg = new Message(config.getTopic(),msgTag,msgKey,messageBody);
try {
producer.sendAsync(msg, new SendCallback() {
@Override
public void onSuccess(final SendResult sendResult) {
assert sendResult != null;
success(msg, sendResult.getMessageId());
}
@Override
public void onException(final OnExceptionContext context) {
//出现异常意味着发送失败,为了避免消息丢失,建议缓存该消息然后进行重试。
error(msg,context.getException());
}
});
} catch (ONSClientException e) {
error(msg,e);
}
}
//--------------日志打印----------
private void error(Message msg,Exception e) {
logger.error("发送MQ消息失败-- Topic:{}, Key:{}, tag:{}, body:{}"
,msg.getTopic(),msg.getKey(),msg.getTag(),new String(msg.getBody()));
logger.error("errorMsg --- {}",e.getMessage());
}
private void success(Message msg,String messageId) {
logger.info("发送MQ消息成功 -- Topic:{} ,msgId:{} , Key:{}, tag:{}, body:{}"
,msg.getTopic(),messageId,msg.getKey(),msg.getTag(),new String(msg.getBody()));
}
}
6、消息消费者的配置
@Configuration
public class ConsumerClient {
@Autowired
private MqConfig mqConfig;
//普通消息监听器,Consumer注册消息监听器来订阅消息.
@Autowired
private MqMessageListener messageListener;
//定时消息监听器,Consumer注册消息监听器来订阅消息.
@Autowired
private MqTimeMessageListener timeMessageListener;
@Bean(initMethod = "start", destroyMethod = "shutdown")
public ConsumerBean buildConsumer() {
ConsumerBean consumerBean = new ConsumerBean();
//配置文件
Properties properties = mqConfig.getMqPropertie();
properties.setProperty(PropertyKeyConst.GROUP_ID, mqConfig.getGroupId());
//将消费者线程数固定为20个 20为默认值
properties.setProperty(PropertyKeyConst.ConsumeThreadNums, "20");
consumerBean.setProperties(properties);
//订阅消息
Map<Subscription, MessageListener> subscriptionTable = new HashMap<Subscription, MessageListener>();
//订阅普通消息
Subscription subscription = new Subscription();
subscription.setTopic(mqConfig.getTopic());
subscription.setExpression(mqConfig.getTag());
subscriptionTable.put(subscription, messageListener);
//订阅定时/延时消息
Subscription subscriptionTime = new Subscription();
subscriptionTime.setTopic(mqConfig.getTimeTopic());
subscriptionTime.setExpression(mqConfig.getTimeTag());
subscriptionTable.put(subscriptionTime, timeMessageListener);
consumerBean.setSubscriptionTable(subscriptionTable);
return consumerBean;
}
}
7、消费者消费消息
/**
* 普通(默认同步)MQ消息监听消费
* @author xf
*/
@Component
public class MqMessageListener implements MessageListener {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public Action consume(Message message, ConsumeContext context) {
logger.info("接收到MQ消息. Topic :" + message.getTopic() + ", tag :" + message.getTag()+ " msgId : " + message.getMsgID()+", Key :" + message.getKey()+", body:" + new String(message.getBody()));
try {
String msgTag = message.getTag();//消息类型
String msgKey = message.getKey();//唯一key
switch (msgTag) {
//--------普通通知
case "userMessage":
break;
}
return Action.CommitMessage;
} catch (Exception e) {
logger.error("消费MQ消息失败! msgId:" + message.getMsgID()+"----ExceptionMsg:"+e.getMessage());
return Action.ReconsumeLater;
}
}
}