由于业务需要, 有的时候, 基于注解形式的代码, 不能满足业务需求.
必须动态变化的绑定和执行. 于是经过一番研究, 记录一下如何通过纯代码的形式Spring实现队列创建和Routingkey 绑定和 消息监听处理.
package com.qcd.SpringRebbitMQ.Consumer;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.TimeoutException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.qcd.DDD.RedisService;
import com.qcd.db.service.SuanLiFenPeiConfigService;
import com.qcd.db.service.ThirdDataPoolService;
import com.qcd.db.domain.Record;
// import com.qcd.db.model.AppConfig;
// import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.listener.MessageListenerContainer;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
/**
*
* 监听来自MQ的 "CMD.WebAPI.TakeImage+gp" 事件 然后执行抓图.
*/
@Slf4j
@Component
public class Q_WebAPI_TakeImage implements MessageListener, ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
// log.info("\t \033[1;32;40m Q_WebAPI_TakeImage 抓图服务 已启动 \033[0m ");
}
@Autowired
SuanLiFenPeiConfigService suanLiFenPeiConfigService;
// @Autowired
// RabbitMQHelper rabbitMQHelper;
@Autowired
RabbitMQBindConfig_Local rabbitMQBindConfig_Local;
@Value("${app.JQNum}")
String JQNum;
@Autowired
public ApplicationContext applicationContext;
@Autowired
public ConnectionFactory connectionFactory;
//开关是否启用本队列, 在配置文件中定义配置
@Value("${switch.Q_WebAPI_TakeImage}")
String switch_Function;
@Bean
public void InitMQBindingTakeImage()
{
this.DeclareQueue_CMDTakeImage();
this.InitConsumer();
log.info("\t \033[1;32;40m Q_WebAPI_TakeImage 抓图服务 已启动 \033[0m ");
}
/**
* 定义队列 Q.WebAPI.TakeImage.on.OtherSys.CMDTakeImage
* 还需要 InitConsumer() 进行动态绑定,
*/
public Integer DeclareQueue_CMDTakeImage(){
// log.info("配置抓图拍照队列 Q_WebAPI_TakeImage_on_OtherSys_CMDTakeImage ...");
TopicExchange com_qcd_ai = new TopicExchange(exchange);
ArrayList<SuanLiFenPeiConfig> suanliconfigs = suanLiFenPeiConfigService .FindByComputer_num(JQNum);
RabbitAdmin rabbitAdmin = new RabbitAdmin(this.rabbitTemplate.getConnectionFactory());
rabbitAdmin.declareExchange(com_qcd_ai);
// 多通道, 分布式.
for (SuanLiFenPeiConfig slcfg : suanliconfigs) {
Integer gp = slcfg.getBh_group(); // 线程ID
int Priority = slcfg.getPriority(); // 优先级
String queuename = "Q.WebAPI.TakeImage.on.OtherSys.CMDTakeImage." + gp;
String event_routing_key = "E.*.CMDTakeImage." + gp;
String cmd_routing_key = "CMD.WebAPI.TakeImage." + gp ;
log.info("抓图拍照"+ queuename+ "绑定" + event_routing_key + "和" + cmd_routing_key);
Queue CMDTakeImageQueue = new Queue(queuename);
rabbitAdmin.declareQueue(CMDTakeImageQueue);
Binding b1 = BindingBuilder
.bind(CMDTakeImageQueue)
.to(com_qcd_ai)
.with(event_routing_key);
b1.addArgument("x-priority", Priority);
Binding b2 = BindingBuilder
.bind(CMDTakeImageQueue)
.to(com_qcd_ai)
.with(cmd_routing_key);
b2.addArgument("x-priority", Priority);
//绑定两个routingkey
rabbitAdmin.declareBinding(b1);
rabbitAdmin.declareBinding(b2);
}
return 1;
}
// 动态添加 处理类 因为需要gp参数, 所以必须是动态的.
// @Bean
public void InitConsumer() {
try {
int maxbhgroup = 10;
// 多通道, 分布式.
for (int gp=0; gp<= maxbhgroup;gp++)
{
this.AddHandler( "Q.WebAPI.TakeImage.on.OtherSys.CMDTakeImage." + gp, 1);
}
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
}
}
private void AddHandler(String queueName, int onOfConsumer) throws Exception {
// this.queueName = queueName;
// this.routingKey = routingKey;
// this.onOfConsumer = onOfConsumer;
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(queueName);
//container.set
container.setConcurrentConsumers(onOfConsumer);
// 创建一个处理类的实例, 因为是 gp多实例的, 所以必须是 动态创建.
Q_WebAPI_TakeImage handler = applicationContext.getBean(Q_WebAPI_TakeImage.class);
container.setMessageListener(handler);
container.start();
log.info(queueName +" 已启动监听 " );
}
// 抓图处理主代码
@Override
public void onMessage(org.springframework.amqp.core.Message message) {
// 将消息转换成String类型然后打印
try {
String data = new String(message.getBody(),"utf-8");
log.info("Q_WebAPI_TakeImage 接收到抓图消息=>"+ data);
com.qcd.db.domain.Record record = JSON.parseObject(data,com.qcd.db.domain.Record.class);
// 这个地方是内部的mq接口, 所有不会传递参数 sysname
//String roukingkey = message.getMessageProperties().getReceivedRoutingKey();
//String sysname = roukingkey.split("[.]")[1]; //roukingkey 为 "E.{系统名}.CMDTakeImage"
// JSONObject jsonRecord = new JSONObject();
// record.setSysname(sysname);
// jsonRecord.put("record", record);
// String deviceId = ljxconfig.getYun_id();
....
....
}
catch (IOException ee) {
log.error("抓图失败 超时", ee);
}catch (TimeoutException e) {
// e.printStackTrace();
log.error("抓图失败 超时", e);
}
// catch (UnsupportedEncodingException e) {
// // e.printStackTrace();
// log.error("抓图失败 不支持的编码", e);
// }
// finally{
// //channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
// }
}
}
首先队列的注册是在, 函数 InitMQBindingTakeImage() 中开始注册的, 具体实现代码是在 DeclareQueue_CMDTakeImage 中实现的.
绑定队列与代码之间的关系是在 InitConsumer中初始化的, 具体实现代码是在 AddHandler() 中.