RabbitMq
一、概念
rabbitMQ 的核心就是 交换器,路由键,队列;
消息来了之后,会发送到交换器,交换器将根据路由键将消息发送到相对应的队列里面去!
消息不用关系到达了那个队列.只需要带着自己的路由键到了交换器就可以了,交换器会帮你把消息发送到指定的队列里面去!
RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。
- 左侧 P 代表 生产者,也就是往 RabbitMQ 发消息的程序。
- 中间即是 RabbitMQ,其中包括了 交换机 和 队列。在发消息者和队列之间, 加入了交换器 (Exchange)
- 右侧 C 代表 消费者,也就是往 RabbitMQ 拿消息的程序。
交换器 (Exchange):消息生产者并没有直接将消息发送给消息队列,而是通过建立与Exchange的Channel,将消息发送给Exchange。Exchange根据路由规则,将消息转发给指定的消息队列。消息队列储存消息,等待消费者取出消息。消费者通过建立与消息队列相连的Channel,从消息队列中获取消息。提供Producer到Queue之间的匹配,接收生产者发送的消息并将这些消息按照路由规则转发到消息队列。交换器用于转发消息,它不会存储消息 ,如果没有 Queue绑定到 Exchange 的话,它会直接丢弃掉 Producer 发送过来的消息。
Channel(信道):多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内的虚拟连接,复用TCP连接的通道。
Routing Key(路由键):消息头的一个属性,用于标记消息的路由规则,决定了交换机的转发路径。最大长度255 字节。
Queue(消息队列):存储消息的一种数据结构,用来保存消息,直到消息发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将消息取走。
Binding(绑定):用于建立Exchange和Queue之间的关联。一个绑定就是基于Binding Key将Exchange和Queue连接起来的路由规则,所以可以将交换器理解成一个由Binding构成的路由表。
Binding Key(绑定键):Exchange与Queue的绑定关系,用于匹配Routing Key。最大长度255 字节。

二、安装
RabbitMQ由Erlang语言开发,需要安装与RabbitMQ版本对应的Erlang语言环境.
1、window环境
 RabbitMQ官网下载址:http://www.rabbitmq.com/install-windows.html
 Erlang官网下载地址:http://www.erlang.org/downloads
 点击下载好的.exe文件进行傻瓜式安装(一直next)即可
 Erlang配置Erlang环境变量
    
    
打开命令窗口,输入erl验证环境是否配置成功。
RabbitMQ配置环境变量
 
  
输入指令激活插件:rabbitmq-plugins.bat enable rabbitmq_management
重启服务器:net stop RabbitMQ && net start RabbitMQ
最后输入http://localhost:15672(默认账号:guest,密码:guest)就能进入RabbitMQ管理界面
2、linux
通过yum等软件仓库都可以直接安装RabbitMQ,但版本一般都较为保守。
RabbitMQ官网提供了新版的rpm包(http://www.rabbitmq.com/download.html),但是安装的时候会提示需要erlang版本>=19.3,然而默认yum仓库中的版本较低。
其实RabbitMQ在github上有提供新的erlang包(https://github.com/rabbitmq/erlang-rpm)
也可以直接加到yum源中
- #vim /etc/yum.repos.d/rabbitmq-erlang.repo
- [rabbitmq-erlang]
- name=rabbitmq-erlang
- baseurl=https://dl.bintray.com/rabbitmq/rpm/erlang/20/el/7
- gpgcheck=1
- gpgkey=https://dl.bintray.com/rabbitmq/Keys/rabbitmq-release-signing-key.asc
- repo_gpgcheck=0
- enabled=1
- #yum clean all
- #yum makecache
然后下载RabbitMQ的RPM包(http://www.rabbitmq.com/download.html)
- 这里是centos7的版本
- #wget https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.4/rabbitmq-server-3.7.4-1.el7.noarch.rpm
- #yum install rabbitmq-server-3.7.4-1.el7.noarch.rpm
- yum会自动去源里安装依赖包
安装到这里就完成了,下面进行简单的配置
- 启动RabbitMQ服务
- #service rabbitmq-server start
- 状态查看
- #rabbitmqctl status
- 启用插件
- #rabbitmq-plugins enable rabbitmq_management
- 重启服务
- #service rabbitmq-server restart
- 添加帐号:name 密码:passwd
- #rabbitmqctl add_user name passwd
- 赋予其administrator角色
- #rabbitmqctl set_user_tags name administrator
- 设置权限
- #rabbitmqctl set_permissions -p / name ".*" ".*" ".*"
然后就能够访问http://ip:15672进入web管理页面了(外部访问别忘记修改防火墙)。
三、工具类RabbitMQUtil
1、往指定路由发送数据 sendMsg(Object event,String exchangePara,String routingkeyPara){}
2、往指定队列发送数据 sendMsg(String queueName,Object event){}
3、发送通知 sendJsonMessage(String dataType,Integer id,String empNo,String oldEmpNo,Integer type) {} //调用1
4、发送消息 send(MessageParam param){} //调用1
 
@Component @Slf4j public class RabbitMQUtil { @Autowired private RabbitTemplate rabbitTemplate; @Autowired private ObjectMapper objectMapper; /** * 往指定路由发送数据 * @param event 消息结构体 * @param exchangePara * @param routingkeyPara */ public void sendMsg(Object event,String exchangePara,String routingkeyPara){ try { rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter()); rabbitTemplate.setExchange(exchangePara); rabbitTemplate.setRoutingKey(routingkeyPara); rabbitTemplate.convertAndSend(MessageBuilder.withBody(objectMapper.writeValueAsBytes(event)).build()); }catch (Exception e){ log.error("RabbitMQUtil:sendMsg error:"+e.getMessage()); e.printStackTrace(); } } /** * 往指定队列发送消息 * @param event 消息结构体 * @param queueName 队列名称 */ public void sendMsg(String queueName,Object event){ try { rabbitTemplate.convertAndSend(queueName,MessageBuilder.withBody(objectMapper.writeValueAsBytes(event)).build()); }catch (Exception e){ log.error("RabbitMQUtil_sendMsg error:"+e.getMessage()); e.printStackTrace(); } } @Value("${spring.rabbitmq.exchange}") public String exchange; @Value("${spring.rabbitmq.routingkey}") public String routingkey; @Value("${spring.rabbitmq.subscribe.exchange}") public String subscribeexchange; @Value("${spring.rabbitmq.subscribe.routingkey}") public String subscriberoutingkey; /** * 发送通知 imine-main-data 服务 * @param dataType * @param id * @param empNo * @param oldEmpNo * @param type */ public void sendJsonMessage(String dataType,Integer id,String empNo,String oldEmpNo,Integer type) { //通知 JsonMessage message = new JsonMessage(); message.setDataType(dataType); message.setType(type); MessageValue messageValue = new MessageValue(); messageValue.setId(id); messageValue.setEmpNo(empNo); messageValue.setOldEmpNo(oldEmpNo); message.setValue(messageValue); this.sendMsg(message,exchange,routingkey); } /** * 发送通知 * @param param */ public void send(MessageParam param){ this.sendMsg(param,subscribeexchange,subscriberoutingkey); } }
四、发送消息
1、基本模型:直接往消息队列发送消息

2、work消息模型
两个消费端共同消费同一个队列中的消息,但是一个消息只能被一个消费者获取。
   
3、Publish/subscribe(交换机类型:Fanout,也称为广播 )
- 
1)声明Exchange,不再声明Queue 
- 
2) 发送消息到Exchange,不再发送到Queue 

4、Routing 路由模型(交换机类型:direct)

Exchange(交换机),接收生产者的消息,然后把消息递交给 与routing key完全匹配的队列,C1:消费者,其所在队列指定了需要routing key 为 error 的消息,C2:消费者,其所在队列指定了需要routing key 为 info、error、warning 的消息。
5、Topics 通配符模式(交换机类型:topics)
每个消费者监听自己的队列,并且设置带统配符的routingkey,生产者将消息发给broker,由交换机根据routingkey来转发消息到指定的队列。
Routingkey一般都是有一个或者多个单词组成,多个单词之间以“.”分割,例如:inform.sms

6、RPC
     
业务:在增删改数据时往中间件发送消息,让别的系统消费消息可进行数据同步
生产者:向交换机和路由发送消息 在rabbit管理页面配置交换机下路由键和队列的关系,在队列下配置交换机和路由键的绑定
//区分新增还是编辑 if(position.getPositionId() == null){//新增 count = positionMapper.selectCount(queryWrapper); if(count >0){ return R.failed(false, CodeConstants.FAILURE_CODE,"职务编号/名称不可重复,请修改"); }else{ baseMapper.insert(position); rabbitMQUtil.sendJsonMessage("position",position.getPositionId(),position.getPositionNo(),position.getPositionNo(),1); MessageParam param = new MessageParam(); param.setObjId(position.getPositionId()); param.setObjNo(position.getPositionNo()); param.setDataType(RabbitDataType.POSITION_DATA_TYPE); param.setAction(1); rabbitMQUtil.send(param); //继续调用sendMsg(Object event,String exchangePara,String routingkeyPara){},向交换机发送消息,属订阅模式 return R.ok(true,"新增成功"); } }
消费者:监听队列消息
@RabbitListener(queues = "${spring.rabbitmq.queuename}")
public void receiveUserInfo(String message) {
    try {
        Gson gson = new Gson();
        ResUtil command = gson.fromJson(message, ResUtil.class);
        if (command != null) {
            MessageParam param = command.getParam();
            String dataType = param.getDataType();
            switch (dataType) { //根据消息的类型区分是哪个基础信息
                case RabbitmqDataType.USER:
                    threadDispatch(message, RabbitmqDataType.USER);
                    break;
                case RabbitmqDataType.VEHICLE:
                    threadDispatch(message, RabbitmqDataType.VEHICLE);
                    break;
                case RabbitmqDataType.DEPT:
                    threadDispatch(message, RabbitmqDataType.DEPT);
                    break;
                default:
                    break;
            }
        }
    } catch (Exception e) {
        log.info("接收基础数据解析失败:" + e);
    }
}
 
路由与队列的对应
五、类
 
1 @Component 2 @Slf4j 3 public class RabbitMqListener { 4 @Autowired 5 private AlarmSaveService alarmSaveService; 6 7 /** 8 * 监听基础数据 vehicle_queue 9 * 10 * @param message 11 */ 12 private AtomicInteger dispatchKeyGenerator = new AtomicInteger(); 13 14 @RabbitListener(queues = "${spring.rabbitmq.queuename}") 15 public void receiveUserInfo(String message) { 16 try { 17 Gson gson = new Gson(); 18 ResUtil command = gson.fromJson(message, ResUtil.class); 19 if (command != null) { 20 MessageParam param = command.getParam(); 21 String dataType = param.getDataType(); 22 switch (dataType) { 23 case RabbitmqDataType.USER: 24 threadDispatch(message, RabbitmqDataType.USER); 25 break; 26 case RabbitmqDataType.VEHICLE: 27 threadDispatch(message, RabbitmqDataType.VEHICLE); 28 break; 29 case RabbitmqDataType.DEPT: 30 threadDispatch(message, RabbitmqDataType.DEPT); 31 break; 32 case RabbitmqDataType.VEHICLETYPE: 33 threadDispatch(message, RabbitmqDataType.VEHICLETYPE); 34 break; 35 case RabbitmqDataType.DRIVER: 36 threadDispatch(message, RabbitmqDataType.DRIVER); 37 break; 38 case RabbitmqDataType.SHIFT: 39 threadDispatch(message, RabbitmqDataType.SHIFT); 40 break; 41 case RabbitmqDataType.WAITROOM: 42 threadDispatch(message, RabbitmqDataType.WAITROOM); 43 break; 44 case RabbitmqDataType.DISPATCH: 45 threadDispatch(message, RabbitmqDataType.DISPATCH); 46 break; 47 case RabbitmqDataType.DRIVERLICENSE: 48 threadDispatch(message, RabbitmqDataType.DRIVERLICENSE); 49 break; 50 case RabbitmqDataType.TRAFFICLIGHTMANAGE: 51 threadDispatch(message,RabbitmqDataType.TRAFFICLIGHTMANAGE); 52 break; 53 case RabbitmqDataType.TRAFFICLIGHT: 54 threadDispatch(message,RabbitmqDataType.TRAFFICLIGHT); 55 break; 56 case RabbitmqDataType.CARD: 57 threadDispatch(message,RabbitmqDataType.CARD); 58 break; 59 default: 60 break; 61 } 62 } 63 } catch (Exception e) { 64 log.info("接收基础数据解析失败:" + e); 65 } 66 } 67 68 /** 69 * 监听告警信息 vehicle_alarm_queue 70 * 71 * @param message 72 */ 73 @RabbitListener(queues = "${spring.rabbitmq.alarm.queuename}") 74 public void receiveAlarm(String message) { 75 try { 76 Gson gson = new Gson(); 77 ResUtil command = gson.fromJson(message, ResUtil.class); 78 AlarmDto alarmDto = command.getMsg(); 79 if (alarmDto != null) { 80 String alarmType = alarmDto.getAlarmTypeCode(); 81 if (alarmDto.getDataType().equals(2)) { 82 log.info("收到告警数据:" + alarmDto); 83 List<AlarmVehicle> vehiclelist = JSON.parseArray(command.getData().toString(), AlarmVehicle.class); 84 alarmDto.setVehiclelist(vehiclelist); 85 } else{ 86 return; 87 } 88 switch (alarmType) { 89 case RabbitmqAlarmType.AREA_SPEED: // 区间超速告警 90 alarmSaveService.saveAndUpdateAreaSpeedAlarm(alarmDto); 91 break; 92 case RabbitmqAlarmType.VEHICLE_OVERWEIGHT: 93 alarmSaveService.saveAndUpdateVehicleOverweightAlarm(alarmDto); 94 break; 95 case RabbitmqAlarmType.VEHICLE_STAY: 96 alarmSaveService.saveAndUpdateVehicleStayAlarm(alarmDto); 97 break; 98 case RabbitmqAlarmType.AREA_ATRESIA: // 闭锁区间告警 99 alarmSaveService.saveAndUpdateAreaAtresiaAlarm(alarmDto); 100 break; 101 case RabbitmqAlarmType.AREA_CARD: // 卡电量不足告警 102 alarmSaveService.saveAndUpdateCardAlarm(alarmDto); 103 break; 104 default: 105 break; 106 } 107 } 108 } catch (Exception e) { 109 log.info("告警数据解析失败:" + e); 110 } 111 } 112 113 114 public void threadDispatch(Object data, String type) { 115 // 耗时操作,扔到业务线程池处理 116 MessageTask cmdTask = MessageTask.valueOf(dispatchKeyGenerator.getAndIncrement(), type, data); 117 MessageDispatcher dispatcher = new MessageDispatcher(); 118 dispatcher.init(); 119 dispatcher.addMessageTask(cmdTask); 120 } 121 }
六、项目实例
1、springboot配置类
@Configuration
public class RabbitMQConfig {
    @Value("${spring.rabbitmq.queuename}")
    private String queueName;
    @Value("${spring.rabbitmq.exchange}")
    public String exchange;
    @Value("${spring.rabbitmq.routingkey}")
    public String routingkey;
    
    @Bean(name ="basicInfo") //队列
    public Queue basicInfo() {
        return new Queue(queueName,true);
    }
    @Bean  //交换机
    public DirectExchange directExchange(){
        // durable:是否持久化;auto_delete:当所有消费客户端连接断开后,是否自动删除队列;
        return new DirectExchange(exchange, true, false);
    }
    @Bean  //绑定
    public Binding binding(){
        return BindingBuilder.bind(basicInfo()).to(directExchange()).with(routingkey);
    }
}
2、直接监听队列
@RabbitListener注解指定目标方法来作为消费消息的方法,通过注解参数指定所监听的队列或者Binding
@RabbitListener(queues = "${spring.rabbitmq.alarm.queuename}")
    public void receiveAlarm(String message) {
        try{
            log.info("收到告警数据:"+message);
            }catch (Exception e){
            log.error("收到告警数据:"+message);
            log.error("告警数据解析失败:"+e);
        }
    }
指定向多个队列发送消息,逗号隔开
@Component
public class MessageHandler {
 
    //支持自动声明绑定,声明之后自动监听队列的队列,此时@RabbitListener注解的queue和bindings不能同时指定,否则报错
    @RabbitListener(bindings ={@QueueBinding(value = @Queue(value = "q5",durable = "true"),
            exchange =@Exchange(value = "zhihao.miao.exchange",durable = "true"),key = "welcome")})
    public void handleMessage(Message message){
        System.out.println("====消费消息"+message.getMessageProperties().getConsumerQueue()+"===handleMessage");
        System.out.println(message.getMessageProperties());
        System.out.println(new String(message.getBody()));
    }
 }
 七、优秀博文
   https://blog.csdn.net/qq_35387940/article/details/100514134
 
                    
                     
                    
                 
                    
                
 
 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号