之前一直搞rabbitMQ的安装与推送数据,今天组长突然让我做一个mq队列监听,突然就没了头绪不过后来根据原有项目提取出来监听发现还可以用,甚是欣慰于是修修补补做成了一个通用的队列监听,话不多说,代码贴上留作参考
首先是RabbitMQListen 类
package cn.com.iot.domain;
import cn.com.iot.bean.bo.DeviceData;
import cn.com.iot.controller.CSCommon;
import cn.com.iot.service.CsService;
import cn.com.iot.service.DeviceService;
import cn.com.iot.util.RabbitMQUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
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.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Configuration
@PropertySource("classpath:config.properties")
public class RabbitMQListen implements ApplicationListener<ContextRefreshedEvent> {
/**
* 日志
*/
private static final Logger LOGGER = LoggerFactory.getLogger(RabbitMQListen.class);
/**
* Redis 模板
*/
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedisTemplate redisTemplate;
/**
* 同步数据接口,将数据从Redis服务中持久化到MySQL数据库中
*/
/**
* MQ的host地址
*/
@Value("${mq.host}")
private String host;
/**
* MQ的端口
*/
@Value("${mq.port}")
private int port;
/**
* MQ的登录名
*/
@Value("${mq.name}")
private String name;
/**
* MQ的密码
*/
@Value("${mq.password}")
private String password;
/**
* 监听原始业务数据的队列名
*/
@Value("${business.orgData.queue}")
private String businessDataQueue;
/**
* 处理业务数据的线程数量
*/
@Value("${business.collection.conn}")
private int businessCollectionConn;
/**
* 每个线程每次处理多少条数据
*/
@Value("${business.getData.count}")
private int businessGetDataCount;
/**
* 同步指标数据到MySql中线程数量
*/
@Value("${save.targetData.conn}")
private int saveTargetDataConn;
/**
* 每个线程每次同步多少条指标数据到数据库中
*/
@Value("${save.targetData.count}")
private int saveTargetDataCount;
/**
* 同步原始数据到MySql中线程数量
*/
@Value("${save.collectionData.conn}")
private int saveCollectionDataConn;
/**
* 每个线程每次同步多少条原始数据到数据库中
*/
@Value("${save.collectionData.count}")
private int saveCollectionDataCount;
/**
* 资源数据同步模块监听的MQ队列名
*/
@Value("${sync.resourceData.queue}")
private String syncResourceDataQueue;
/**
* 资源数据同步模块每次从MQ中取出多少条数据
*/
@Value("${sync.resourceData.count}")
private int syncResourceDataCount;
/**
* list 统计
*/
public static int listCount = 0;
/**
* 开始时间
*/
private static long startTime = 0;
@Autowired
private CsService csService;
@Autowired
private DeviceService deviceService;
/**
* TODO 测试方法,用来记录测试时间以及测试的数据量,后续删除
*
* @param count
*/
public static synchronized void addListCount(int count) {
if (listCount == 0 && count != 0) {
startTime = System.currentTimeMillis();
System.out.println("startTime : " + startTime);
}
listCount += count;
if (listCount == 200000) {
long endTime = System.currentTimeMillis();
System.out.println("endTime : " + endTime);
System.out.println("totalTime : " + (endTime - startTime));
listCount = 0;
}
}
/**
* 该方法会被spring在服务启动完成之后自动执行
* 利用该方法,可以实现在服务启动之后,自动调用我们需要的功能。
* 通过异步,则能实现自定义的各功能模块自动运行
*
* @param contextRefreshedEvent
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
try {
// 初始化MQ的工厂
RabbitMQUtil.initFactory2(host,port,name,password);
// 启用业务数据处理功能(从MQ拿原始数据---转换为指标数据---存入redis)
listenOrgData();
} catch (Exception e) {
e.printStackTrace();
}
}
private void listenOrgData() {
LOGGER.info("================= Mq监听功能启动成功 =================");
new Thread(new Runnable() {
@Override
public void run() {
try {
// 通过线程池来控制该业务功能的线程数,具体线程数通过配置文件指定
ExecutorService executorService = Executors.newFixedThreadPool(businessCollectionConn);
while (true){
executorService.execute(new Runnable() {
public void run() {
try {
RabbitMQUtil.initFactory(host,port);
// 调用消息接收方法,指定接收的队列以及本次获取的数据数量。队列名以及数据量由配置文件指定
List<String> orgDatas = RabbitMQUtil.receiveMessages(businessDataQueue, businessGetDataCount);
if (!orgDatas.isEmpty()){
System.out.println("监听到的数据为:"+orgDatas);
//进行数据处理给下层业务判断是否告警
for (int i =0 ;i<orgDatas.size();i++){
String orgData = orgDatas.get(i);
//拿到deviceId进行解析
/* JSONObject jsonObject = JSON.parseObject(orgData);*/
DeviceData data = JSON.parseObject(orgData, new TypeReference<DeviceData>() {});
System.out.println(data.getDeviceId());
String deviceId = data.getDeviceId();
String datas = data.getData();
CSCommon cs = new CSCommon();
Object commonmq = cs.commonmq(deviceId, datas);
System.out.println(commonmq);
}
}
} catch (Exception e) {
LOGGER.error("数据处理失败", e);
}
}
});
// 每间隔300毫秒创建一个新的线程向MQ取数据//错
//休眠15秒
Thread.sleep(10000);
}
} catch (Throwable e) {
LOGGER.error("指标数据持久化功能异常,将于10秒后重新启动", e);
try {
Thread.sleep(10000);
} catch (Exception e1) {
}
}
}
}).start();
}
}
然后是用到的rabbitmqUtil
package cn.com.iot.util;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@PropertySource("classpath:config.properties")
public class RabbitMQUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(RabbitMQUtil.class);
private static final ConnectionFactory FACTORY = new ConnectionFactory();
private static final List<Connection> CONNECTIONS = new ArrayList<Connection>();
private static final int MAX_CONNECTION = 20;
/**
* MQ的host地址
*/
@Value("${mq.host}")
private String host;
/**
* MQ的端口
*/
@Value("${mq.port}")
private int port;
static {
initFactory("192.168.10.156",5672);
}
/**
* 向指定的消息队列发送消息
*
* @param message 消息体
* @param queue 队列名
*/
public static void sendMessage(String message, String queue) {
try {
// 从队列中获取一个连接
Connection connection = getConnection();
// 创建一个MQ的管道
Channel channel = connection.createChannel();
// 将管道绑定到一个队列上
channel.queueDeclare(queue, false, false, false, null);
// 向指定的队列发送消息
channel.basicPublish("", queue, null, message.getBytes("UTF-8"));
// 关闭管道
channel.close();
// 将连接放回到队列中
setConnection(connection);
} catch (Exception e) {
throw new RuntimeException("RabbitMQ connection fail, send message fail!", e);
}
}
public static void sendMessageList(List<Map<String, Object>> message, String queue) {
try {
// 从队列中获取一个连接
Connection connection = getConnection();
// 创建一个MQ的管道
Channel channel = connection.createChannel();
// 将管道绑定到一个队列上
channel.queueDeclare(queue, false, false, false, null);
for (Map<String, Object> map : message) {
// 向指定的队列发送消息
channel.basicPublish("", queue, null, JsonUtil.toJson(map).getBytes("UTF-8"));
}
// 关闭管道
channel.close();
// 将连接放回到队列中
setConnection(connection);
} catch (Exception e) {
throw new RuntimeException("RabbitMQ connection fail, send message fail!", e);
}
}
/**
* 向指定的消息队列取出固定数量的数据
*
* @param queue 消息队列名
* @param count 取出的消息数量
* @return
*/
public static List<String> receiveMessages(String queue, int count) {
List<String> list = new ArrayList<String>();
try {
// 从队列中获取连接
Connection connection = getConnection();
// 创建一个管道
Channel channel = connection.createChannel();
// 将管道绑定到队列上
channel.queueDeclare(queue, false, false, false, null);
// 向指定的队列中获取数据,通过循环,每次循环获取一条数据,总共循环count次
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queue, false, consumer);
for (int i = 0; i < count; i++) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery(300);
if (delivery == null) {
break;
}
String message = new String(delivery.getBody());
list.add(message);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
// 关闭管道
channel.close();
// 将连接放回到队列中
setConnection(connection);
} catch (Exception e) {
LOGGER.error("RabbitMQ connection fail, receive message fail!", e);
} finally {
return list;
}
}
/**
* 从队列中获取连接
*
* @return
*/
public static Connection getConnection() {
try {
return getAndSetConnection(true, null);
} catch (Exception e) {
throw new RuntimeException("connection MQ fail", e);
}
}
/**
* 将使用完毕的连接放回到队列中
*
* @param connection
*/
private static void setConnection(Connection connection) {
try {
getAndSetConnection(false, connection);
} catch (Exception e) {
throw new RuntimeException("connection MQ fail", e);
}
}
/**
* 通过同步锁控制连接队列,根据参数isGet来区分本次调用是从队列中取连接还是存放连接
* @param isGet 取出或者放回的标记,true表示取连接,false表示放回连接
* @param connection 取连接:null, 放回连接:具体连接对象
* @return 取连接时,返回具体连接对象,放回连接时,返回null
* @throws Exception
*/
private static synchronized Connection getAndSetConnection(boolean isGet, Connection connection) throws Exception {
if (isGet) {
// 取连接,如果队列中不存在连接,则新建一个连接
if (CONNECTIONS.isEmpty()) {
return FACTORY.newConnection();
}
Connection newConnection = CONNECTIONS.get(0);
CONNECTIONS.remove(0);
if (newConnection.isOpen()) {
return newConnection;
} else {
return FACTORY.newConnection();
}
} else {
// 放回连接,如果队列中的连接数超过了MAX_CONNECTION指定数量的连接,则抛弃该连接
if (CONNECTIONS.size() < MAX_CONNECTION) {
CONNECTIONS.add(connection);
}
return null;
}
}
public static void initFactory(String host, int port) {
FACTORY.setHost(host);
FACTORY.setPort(port);
FACTORY.setUsername("admin");
FACTORY.setPassword("admin");
}
public static void initFactory2(String host, int port,String name,String password) {
System.out.println("mq登录成功");
FACTORY.setHost(host);
FACTORY.setPort(port);
FACTORY.setUsername(name);
FACTORY.setPassword(password);
}
}
最后就是配置文件 config.properties
#mq地址、端口
mq.host = 192.168.10.156
mq.port = 5672
mq.name = admin
mq.password = admin
#mq队列名、处理业务数据的线程数量、每个线程每次处理多少条数据
business.orgData.queue = test
business.collection.conn = 4
business.getData.count = 7000
# 线程数量、数据
save.targetData.conn = 4
save.targetData.count = 10000
# 线程数量、数据
save.collectionData.conn = 4
save.collectionData.count = 10000
# 队列名。处理数据条数
sync.resourceData.queue = guangzhou_syncResource_role
sync.resourceData.count = 1000
order.business.type = guangzhou_orderBusiness
# 最大连接数数
box.data.process.conn = 4
box.data.process.queue = guangzhou_orgDataConfig
box.data.process.count = 1
项目一启动,调用线程实时监听功能实时监听配置mq队列中的消息然后进行业务处理