在进行业务开发过程中肯定会存在业务数据的传递调用,正常情况下的做法可能有很多,webService、REST接口等等,都可以实现此类的需求,但是接口会存在什么问题呢,有可能挂掉或者网络问题等等原因。如果多个业务都需要同一个数据,那么就要调用多次接口,如果推送到MQ中,其他业务按需进行订阅数据,这样就减少系统间的接口调用等等。那么下面就来说一下Springboot集成RocketMQ进行消息的生产订阅。
首先在POM中加入RocketMQ的依赖。
此处使用的是阿里云线上的RocketMQ,不是自己搭建的(特此说明)。
<!-- rocketMQ -->
<!-- 因为需要使用fastjson的1.2.58版本,去掉MQ中的fastjson依赖 -->
<dependency>
<groupId>com.aliyun.openservices</groupId>
<artifactId>ons-client</artifactId>
<version>1.8.0.Final</version>
<exclusions>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</exclusion>
</exclusions>
</dependency>
然后进行application.yml的配置:
rocketMQ:
# 线上MQ的GroupId固定以GID_开头
groupId: GID_Form
topic: form
consumerTag: form
# MQ 提供:广播订阅和集群订阅模式
messageModel: BROADCASTING #广播订阅方式
然后进行RocketMQ的配置类开发:
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
@Configuration
public class RocketMQConfig {
public Properties getProperties() {
Properties properties = new Properties();
// AccessKey 线上MQ 会生成
properties.setProperty("AccessKey", "*****");
// SecretKey 线上MQ 会生成
properties.setProperty("SecretKey", "******");
properties.setProperty(PropertyKeyConst.SendMsgTimeoutMillis, "3000");
// 顺序消息消费失败进行重试前的等待时间,单位(毫秒)
properties.put(PropertyKeyConst.SuspendTimeMillis, "100");
// 消息消费失败时的最大重试次数
properties.put(PropertyKeyConst.MaxReconsumeTimes, "20");
// address地址 线上MQ 会提供
properties.put(PropertyKeyConst.NAMESRV_ADDR, "");
return properties;
}
}
然后进行消息的生产service类:
import com.aliyun.openservices.ons.api.Message;
import com.**.**.form.rocketProducer.RocketProducer;
import com.**.**.form.service.PushMessageService;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Iterator;
@Service
@Transactional
public class PushMessageServiceImpl implements PushMessageService {
private final static Logger logger = LoggerFactory.getLogger(PushMessageServiceImpl.class);
@Value(value = "${rocketMQ.topic}")
String topic;
@Value(value = "${rocketMQ.consumerTag}")
String messageTag;
@Value(value = "${rocketMQ.groupId}")
String groupId;
@Autowired
RocketProducer rocketProducer;
@Override
public boolean pushMessage(String requestParam) {
boolean sendStatus = true;
if (!StringUtils.isEmpty(requestParam)) {
JSONObject jsonObject = JSONObject.fromObject(requestParam);
// RocketMQ 每条消息的messageKey,可以不进行填写,也可以重复,为了方面后续查询,设置为不重复
String businessId = jsonObject.getString(BUSINESS_ID_KEY);
String messageBody = jsonObject.toString();
Message message = new Message(topic, messageTag, messageBody.getBytes());
message.setKey(businessId);
boolean value = rocketProducer.sendNormalMessage(message, groupId);
if (!value) {
sendStatus = false;
logger.info("推送消息失败,数据为:" + requestParam);
}
}
return sendStatus;
}
}
发送消息类:
package com.**.**.form.rocketProducer;
import com.aliyun.openservices.ons.api.*;
import com.**.**.form.config.RocketMQConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Properties;
@Component
@SuppressWarnings({"unused"})
public class RocketProducer {
private static final Logger logger = LoggerFactory.getLogger(RocketProducer.class);
@Autowired
RocketMQConfig rocketMQConfig;
/**
* @Description 发送普通消息
*/
public boolean sendNormalMessage(Message message, String groupId) {
Properties properties = rocketMQConfig.getProperties();
properties.setProperty(PropertyKeyConst.GROUP_ID, groupId);
Producer producer = ONSFactory.createProducer(properties);
// 在发送消息前,必须调用 start 方法来启动 Producer,只需调用一次即可
producer.start();
try {
SendResult sendResult = producer.send(message);
// 同步发送消息,只要不抛异常就是成功
if (sendResult != null) {
logger.info("消息发送成功:messageID:" + sendResult.getMessageId());
return true;
}
} catch (Exception e) {
// 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理
logger.info("发送消息失败:" + e);
} finally {
producer.shutdown();
}
return false;
}
}
然后进行消息订阅类:
import com.aliyun.openservices.ons.api.*;
import com.**.**.form.config.RocketMQConfig;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.Properties;
@Component
@Slf4j
public class RocketMQConsumer {
public static final Logger logger = LoggerFactory.getLogger(RocketMQConsumer.class);
@Autowired
RocketMQConfig rocketMQConfig;
@Value(value = "${rocketMQ.groupId}")
String groupId;
@Value(value = "${rocketMQ.topic}")
String topic;
@Value(value = "${rocketMQ.consumerTag}")
String consumerTag;
@Value(value = "${rocketMQ.messageModel}")
String messageModel;
public void normalSubscribe() {
final Properties properties = rocketMQConfig.getProperties();
properties.put(PropertyKeyConst.GROUP_ID, groupId);
if (!StringUtils.isEmpty(messageModel)){
properties.put(PropertyKeyConst.MessageModel, PropertyValueConst.BROADCASTING);
}
// 批量消费,每次拉取10条
consumer.subscribe(topic, consumerTag, (message, context) -> {
System.out.println("Receive: " + new String(message.getBody()));
String messageBody = new String(message.getBody());
JSONObject jsonObject = JSONObject.fromObject(messageBody);
try {
// 订阅到消息体后进行自己的业务
} catch (Exception e) {
e.printStackTrace();
if (!PropertyValueConst.BROADCASTING.equals(properties.getProperty(PropertyKeyConst.MessageModel))){
return Action.ReconsumeLater;
} else {
return Action.CommitMessage;
}
}
return Action.CommitMessage;
});
consumer.start();
}
}
消息订阅需要程序启动时进行一次订阅,否则订阅不到消息:
import com.**.**.rocketmq.consumer.RocketMQConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class RocketConsumerListener implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(RocketConsumerListener.class);
@Autowired
RocketMQConsumer rocketMQConsumer;
@Override
public void run(String... args) throws Exception {
//System.out.println("=======rocketMQ消费者启动=======");
logger.info("======RocketMQ消费者启动======");
rocketMQConsumer.normalSubscribe();
}
}