import com.alibaba.fastjson.JSON;
import com.alibaba.rocketmq.client.producer.SendResult;
import com.tf56.hermesContract.model.HermesContract;
import com.tf56.hermesContractApi.DTO.EContractSendMqDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import tf56.shared.rocketmq.TfmqClient;
import javax.annotation.Resource;
@Service
@Slf4j
public class SendMsgMQService {
@Resource
private TfmqClient tfmqClient;
@Value("${rocketmq.producer.topic}")
private String eContractTopic;
@Value("${rocketmq.producer.topic}")
private String tag;
public void sendContractMQ(HermesContract hc){
EContractSendMqDTO dto = new EContractSendMqDTO();// 消息体对象
// 业务处理
SendResult sr = tfmqClient.sendUTF(eContractTopic, tag, hc.getContractNum(), JSON.toJSONString(dto));
log.info("状态变更MQ消息:合同编号[{}],状态[{}],send[{}]",hc.getContractNum(),hc.getContractStatus(),JSON.toJSONString(sr));
}
}
import com.alibaba.fastjson.JSON;
import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import com.alibaba.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import com.alibaba.rocketmq.common.message.MessageExt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* mq消息监听
*/
@SuppressWarnings("Duplicates")
@Slf4j
@Service
public class RocketMqMessageConsumer implements MessageListenerConcurrently {
/**
* 此topic、tag与applicationContext-mqClient.xml中consumerTopicTags配置相对应
*/
@Value("${rocketmq.consumer.topic}")
private String topic;
@Value("${rocketmq.consumer.tag}")
private String tag;
@Override
public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
String msgBody = new String(msg.getBody(), StandardCharsets.UTF_8);
log.info("MQ消息体:{}", msgBody);
if(topic.equals(msg.getTopic()) && tag.equals(msg.getTags())){
ContractSendSignFileMqDTO mqDTO = JSON.parseObject(msgBody, ContractSendSignFileMqDTO.class);
// 处理业务代码
// TODO
return ConsumeConcurrentlyStatus.RECONSUME_LATER;// 处理失败
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.rocketmq.client.consumer.DefaultMQPushConsumer;
import com.alibaba.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import com.alibaba.rocketmq.client.exception.MQClientException;
import com.alibaba.rocketmq.client.producer.DefaultMQProducer;
import com.alibaba.rocketmq.client.producer.SendResult;
import com.alibaba.rocketmq.common.message.Message;
/**
* RocketMqClient封装类,包含DefaultMQPushConsumer,DefaultMQProducer。
*
* 设计说明:
* 1.1.1, 支持同时连接多个集群(多套namesrv)
*
* 1.2.1, 简化API,支持扩展,更好支持消息跟踪。v1.0.0重载了太多方法,选择困难。
* 1.2.2, 全部废弃v1.0.0的API,为了避免,升级版本忘记切换方法。
*
* 使用说明:
* 1.1,DefaultMQPushConsumer自动启动充要条件:consumerNamesrvAddr,consumerGroupName,consumerTopicTags, messageListener 配置正确齐全
* 1.2,consumerTopicTags:格式形如:topic1,tag1;topic2,tag2 或 topic1;topic2 或 topic1。详情{TfmqFactory.java}
*
* 2.1,DefaultMQProducer自动启动充要条件:producerNamesrvAddr,producerGroupName 配置正确齐全
*
* 3.1,目前仅支持log4j日志,其他日志如logback请自行检查
*
*
* @version 1.1.0
* @author naijiang.wang
* @date 2016年10月17日 下午7:56:17
*/
public class TfmqClient {
private static final Logger logger = LoggerFactory.getLogger(TfmqClient.class);
private DefaultMQPushConsumer consumer;
private DefaultMQProducer producer;
private MessageListenerConcurrently messageListener;
private String producerNamesrvAddr;
private String producerGroupName;
private String consumerNamesrvAddr;
private String consumerGroupName;
private String consumerTopicTags;
private boolean started = false;
public synchronized void init() throws MQClientException {
if (!started) {
startProducer();
startConsumer();
registerHook();
started = true;
}
}
private void startProducer() throws MQClientException {
producer = TfmqFactory.newDefaultMQProducer(producerGroupName, producerNamesrvAddr);
if (producer != null) {
logger.info("producer starting...");
producer.start();
logger.info("producer started OK...");
}
}
private void startConsumer() throws MQClientException {
consumer = TfmqFactory.newDefaultMQPushConsumer(consumerGroupName, consumerNamesrvAddr, messageListener,
consumerTopicTags);
if (consumer != null) {
logger.info("consumer starting...");
consumer.start();
logger.info("consumer started OK...");
}
}
/**
* 释放资源,关闭客户端, tomcat环境下使用
*/
public synchronized void destroy(){
logger.info("开始调用destroy()方法,started="+started);
if(started){
logger.info("开始执行destroy()方法,started="+started);
destroyProducer();
destroyConsumer();
started = false;
}
}
/**
* 非tomcat应用的关闭钩子
*
* Runtime.getRuntime().addShutdownHook注释:
* it will start all registered shutdown hooks in some unspecified order and let them run concurrently.
* tomcat自身也有CatalinaShutdownHook。使用tomcat的shutdown.sh会报以下错误:
* 16:31:56.627 [Thread-6] INFO TfmqClient - producer shutdowning...1f190f5193fd6afb220cd10d2b136bbd
Exception in thread "Thread-6" java.lang.IllegalStateException: Can't overwrite cause with java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [ch.qos.logback.classic.spi.ThrowableProxy]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
at java.lang.Throwable.initCause(Throwable.java:456)
at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForClassLoading(WebappClassLoaderBase.java:1324)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1203)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1164)
at ch.qos.logback.classic.spi.LoggingEvent.(LoggingEvent.java:125)
at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:468)
at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:424)
at ch.qos.logback.classic.Logger.error(Logger.java:587)
at java.lang.Thread.run(Thread.java:744)
*
*/
private void registerHook() {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
logger.info("ShutdownHook开始调用destroy()方法,started="+started);
destroy();
}
}));
}
/**
* 发送mq消息, msg使用utf-8编码。
*
* 消息跟踪有两种方式:
* 1, SendResult中的msgId
* 2, topic + keys 组合方式
*
* @param topic
* @param tags 不需要时传 null 或者 空串 ""
* @param keys 用于消息跟踪。最好使用不会重复的值,比如交易Num之类的。
* @param msg 消息体字符串,使用utf-8编码
* @throws TfmqException
*/
public SendResult sendUTF(String topic, String tags, String keys, String msg) throws TfmqException{
SendResult sendResult;
if(tags == null){
tags = "";
}
if(keys == null){
keys = "";
}
try {
Message message = new Message(topic, tags, keys, msg.getBytes("utf-8"));
sendResult = producer.send(message);
} catch (Exception e) {
throw new TfmqException("tfmq发送消息失败", e);
}
return sendResult;
}
/**
* 发送mq消息,可自定义message属性
*
* //设置延迟等级,默认不设置,没有延迟 。从1开始。对应的值:"1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h"
* 1, message.setDelayTimeLevel(delayTimeLevel);
* 2, 其他详见com.alibaba.rocketmq.common.message.Message构造方法
*
*/
public SendResult send(Message message)throws TfmqException {
SendResult sendResult;
try {
sendResult = producer.send(message);
} catch (Exception e) {
throw new TfmqException("tfmq发送消息失败", e);
}
return sendResult;
}
private void destroyProducer(){
// RocketMq服务器上注销自己
try {
if (null != producer) {
String instanceName = producer.getInstanceName();
logger.info("producer shutdowning..."+instanceName);
producer.shutdown();
producer = null;
logger.info("producer shutdown OK.."+instanceName);
}
} catch (Exception e) {
logger.error("destroyProducer发生异常", e);
}
}
private void destroyConsumer(){
// RocketMq服务器上注销自己
try {
if (null != consumer) {
String instanceName = consumer.getInstanceName();
logger.info("consumer shutdowning..."+instanceName);
consumer.shutdown();
consumer = null;
logger.info("consumer shutdown OK.."+instanceName);
}
} catch (Exception e) {
logger.error("destroyConsumer发生异常", e);
}
}
public MessageListenerConcurrently getMessageListener() {
return messageListener;
}
public void setMessageListener(MessageListenerConcurrently messageListener) {
this.messageListener = messageListener;
}
public String getProducerNamesrvAddr() {
return producerNamesrvAddr;
}
public void setProducerNamesrvAddr(String producerNamesrvAddr) {
this.producerNamesrvAddr = producerNamesrvAddr;
}
public String getProducerGroupName() {
return producerGroupName;
}
public void setProducerGroupName(String producerGroupName) {
this.producerGroupName = producerGroupName;
}
public String getConsumerNamesrvAddr() {
return consumerNamesrvAddr;
}
public void setConsumerNamesrvAddr(String consumerNamesrvAddr) {
this.consumerNamesrvAddr = consumerNamesrvAddr;
}
public String getConsumerGroupName() {
return consumerGroupName;
}
public void setConsumerGroupName(String consumerGroupName) {
this.consumerGroupName = consumerGroupName;
}
public String getConsumerTopicTags() {
return consumerTopicTags;
}
public void setConsumerTopicTags(String consumerTopicTags) {
this.consumerTopicTags = consumerTopicTags;
}
}
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.rocketmq.client.consumer.DefaultMQPushConsumer;
import com.alibaba.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import com.alibaba.rocketmq.client.producer.DefaultMQProducer;
public class TfmqFactory {
private static final Logger logger = LoggerFactory.getLogger(TfmqFactory.class);
public static DefaultMQProducer newDefaultMQProducer(String producerGroupName, String producerNamesrvAddr) {
if (producerGroupName == null) {
logger.info("producerGroupName is null, will not init tfmqClient.producer");
return null;
}
if (producerNamesrvAddr == null) {
logger.info("producerNamesrvAddr is null, will not init tfmqClient.producer");
return null;
}
DefaultMQProducer producer = new DefaultMQProducer(producerGroupName);
producer.setNamesrvAddr(producerNamesrvAddr);
String instanceName = getInstanceName(producerNamesrvAddr);
// 关键:启动多个实例
producer.setInstanceName(instanceName);
logger.info("producer init OK, params[producerGroupName={},producerNamesrvAddr={},instanceName={},]",
producerGroupName, producerNamesrvAddr, instanceName);
return producer;
}
public static DefaultMQPushConsumer newDefaultMQPushConsumer(String consumerGroupName, String consumerNamesrvAddr,
MessageListenerConcurrently messageListener, String consumerTopicTags) {
if (consumerGroupName == null) {
logger.info("consumerGroupName is null, will not init tfmqClient.consumer");
return null;
}
if (consumerNamesrvAddr == null) {
logger.info("consumerNamesrvAddr is null, will not init tfmqClient.consumer");
return null;
}
if (messageListener == null) {
logger.info("messageListener is null, will not init tfmqClient.consumer");
return null;
}
if (consumerTopicTags == null) {
logger.info("consumerTopicTags is null, will not init tfmqClient.consumer");
return null;
}
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(consumerGroupName);
consumer.setNamesrvAddr(consumerNamesrvAddr);
consumer.registerMessageListener(messageListener);
String instanceName = getInstanceName(consumerNamesrvAddr);
// 关键:启动多个实例
consumer.setInstanceName(instanceName);
try {
String[] topicTags = consumerTopicTags.split(";");
for (String topicTag : topicTags) {
String[] items = topicTag.split(",");
String topic = items[0];
String tag = "*";
if (items.length == 2) {
tag = items[1];
}
consumer.subscribe(topic, tag);
logger.info("consumer subscribes topic={}, tag={}", topic, tag);
}
} catch (Exception e) {
logger.error("consumer parse error, consumerTopicTags={}", consumerTopicTags, e);
return null;
}
logger.info("consumer init OK, params[consumerGroupName={},consumerNamesrvAddr={},instanceName={},]",
consumerGroupName, consumerNamesrvAddr, instanceName);
return consumer;
}
private static String getInstanceName(String namesrvAddr) {
String text = getHostAddress() + namesrvAddr;
logger.info("用于生成instanceName的text="+text);
return MD5Utils.md5(text);
}
private static String getHostAddress(){
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
logger.error("getHostAddress error", e);
}
return "";
}
}