1、项目中使用的Direct方式,云端和子系统均有自己的ID,使用ID在MQ上注册队列,云端管理子系统端的关系,每个子系统同步与自己相关的其他系统的ID到本地,同步完成后,在MQ上绑定对应的Key,这样,当子系统发送消息的routekey为自己的ID时,所有与其相关的子系统均会收到该条消息,间接的实现了群发。每个子系统同时绑定ID+"_recv”到队列的Exchange上,这样当其他系统发送消息到该系统时,可以直接使用该routekey。
2、使用AMQP方式操作rabbitMQ,使用Spring对AMQP的xml配置发现太不灵活,在项目中自己声明队列,绑定EXchange,使用的是单例。
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
public class AmqpFactory {
private static AmqpFactory amqpFactory = new AmqpFactory();
private boolean started = false;
private static String MQUSERNAME;
private static String MQPASSWORD ;
private static String MQHOST;
private static String MQDIRECTEXCHANGENAME;
private List<String> routeKeyList = new ArrayList<String>();
private List<String> oldRoutingkeyList = null;
private RabbitTemplate rabbitTemplate = null;
private String mqQueueName;
private CachingConnectionFactory cf = null;
private SimpleMessageListenerContainer container = null;
public static AmqpFactory getInstance(){
return amqpFactory;
}
public void setMQQueueName(String mQQueueName) {
mqQueueName = mQQueueName;
}
public void setRouteKeyList(List<String> routeKeyList) {
this.routeKeyList = routeKeyList;
}
public void setOldRoutingkeyList(List<String> oldRoutingkeyList) {
this.oldRoutingkeyList = oldRoutingkeyList;
}
public String getMQHOST() {
return MQHOST;
}
public String getExchange(){
return MQDIRECTEXCHANGENAME;
}
public synchronized void initCofig(){
if (Strings.isBlank(CommonConfig.LOCAL_HOSPOTAL_ID)){
return ;
}
mqQueueName = CommonConfig.LOCAL_HOSPOTAL_ID;
if (started == false){
List<String> routekeyList = refreshHospitalIdAndRouteKeyList();
if(routekeyList == null || routekeyList.size() == 0){
return ;
}
this.setRouteKeyList(routekeyList);
refreshRabbitConfig();
}
started = true;
}
public synchronized void reladCofig(){
if (started == true){
List<String> routekeyList = refreshHospitalIdAndRouteKeyList();
if(routekeyList == null || routekeyList.size() == 0){
return ;
}
this.setRouteKeyList(routekeyList);
refreshRabbitConfig();
}
}
/**
* 加载mq的配置文件
*/
public static void loadProperties(){
Properties p = CommonConfig.loadProperties("mq.properties");
MQUSERNAME = p.getProperty("MQUSERNAME");
MQPASSWORD = p.getProperty("MQPASSWORD");
MQHOST = p.getProperty("MQHOST");
MQDIRECTEXCHANGENAME = p.getProperty("MQDIRECTEXCHANGENAME");
}
/**
* 注册到MQ服务器
*/
private void refreshRabbitConfig(){
if (container != null){
container.stop();
}
cf = new CachingConnectionFactory(MQHOST);
cf.setUsername(MQUSERNAME);
cf.setPassword(MQPASSWORD);
cf.setRequestedHeartBeat(20);
cf.setChannelCacheSize(2);
cf.setChannelCheckoutTimeout(20000);
// set up the queue, exchange, binding on the broker
RabbitAdmin admin = new RabbitAdmin(cf);
//设置队列持久化和不自动删除
Queue queue = new Queue(mqQueueName, true, false, false);
log.info("queue name : "+mqQueueName);
admin.declareQueue(queue);
DirectExchange exchange = new DirectExchange(MQDIRECTEXCHANGENAME,true,false);
admin.declareExchange(exchange);
if (routeKeyList == null || routeKeyList.size() == 0) return;
//删除绑定路由
if (oldRoutingkeyList != null){
this.deleteAllBinding(admin, oldRoutingkeyList,queue,exchange);
}
//重新绑定
for (String routekey : routeKeyList){
admin.declareBinding(
BindingBuilder.bind(queue).to(exchange).with(routekey));
}
// set up the listener and container
container = new SimpleMessageListenerContainer(cf);
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageHandler());
container.setMessageListener(adapter);
container.setQueueNames(mqQueueName);
container.start();
// send template
rabbitTemplate = new RabbitTemplate(cf);
}
//获取到发送模板
public RabbitTemplate getTemplate(){
return rabbitTemplate;
}
public List<String> refreshClientIdAndRouteKeyList(){
List<String> clientIdList = getAllClientId();
if( clientIdList == null || clientIdList.size() == 0){
}
List<String> routeKeyList = new ArrayList<String>();
if (routeKeyList != null){
for (String clientId : clientIdList ){
routeKeyList.add(clientId);
}
}
//云端的广播key
routeKeyList.add(CommonConfig.CLOUD_SENT_KEY);
//自己队列的接收key
routeKeyList.add(CommonConfig.ID + "_recv");
return routeKeyList;
}
public void deleteAllBinding(RabbitAdmin admin,List<String> routingkeyList, Queue queue, DirectExchange exchange){
for (String routekey : routingkeyList){
admin.removeBinding(BindingBuilder.bind(queue).to(exchange).with(routekey));
}
}
}
3、项目中的消息内容均是json串,结构为{type:"com.zz.AAMessage",data:""},type为class类名,data为class的内容,当收到消息后,使用反射获取到类,根据类的类型进处理。再加新的消息类型的时候,只需要继承父类,实现处理方法,然后注册到解析类中。
4、子系统中,由于可能会断网,所以增加了一个消息表,发送消息和接收消息都经过一次中转,有定时器定时处理消息表。