SpringBoot+RabbitMQ整合(附工具类demo),订阅,广播,组播,模糊播

SpringBoot+RabbitMQ整合(附工具类demo)

 

  1. direct(订阅,直连)直连模式 一对一的绑定关系, Routing key(路由关键字)和Binding key只有完全匹配,才能消费成功。
  2. fanout (广播) 发送到该交换机的所有信息都将转发到与该exchange绑定的queue中。
  3. topic (模糊播,组播) 在exchange中routing key 和binding key相匹配就可以绑定成功,可以一对一也可一对多。bingding key 可以存在两种特殊字符,# :匹配一个或多个单词。 *:匹配一个单词。
  4. headers Exchange不依赖于routing key与binding key的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配。

 

  • maven 依赖

   org.springframework.boot
   spring-boot-starter-amqp
  • yml配置
spring:
  rabbitmq:
    host: 11.53.56.69
    port: 5672
    username: scp
    password: Passc0de33!
    ## druid配置
    druid:
      filters: stat,wall,log4j,config
      max-active: 100
      initial-size: 1
      max-wait: 60000
      min-idle: 1
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      validation-query: select 'x'
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      pool-prepared-statements: true
      max-open-prepared-statements: 50
      max-pool-prepared-statement-per-connection-size: 20
  •  订阅模式(Direct简单的运用)

  • 配置类
package com.config.mq;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Binding;
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.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.SerializerMessageConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

/**
 * rabbitMQ config by CHENYB of 2019-07-22
 */
@Configuration
public class RabbitMQConfig {
 
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 连接
     */
    @Value("${spring.rabbitmq.host}")
    private String host;
    @Value("${spring.rabbitmq.port}")
    private int port;
    @Value("${spring.rabbitmq.username}")
    private String username;
    @Value("${spring.rabbitmq.password}")
    private String password;

    @Bean
    public ConnectionFactory connectionFactory() {
        //rabbitMQ 连接
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host,port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setPublisherConfirms(true);
        return connectionFactory;
    }

    @Bean
    @Scope("prototype")//必须是prototype类型,处理rabbitTemplate 对象唯一性
    public RabbitTemplate rabbitTemplate() {
        //生成模板对象
        RabbitTemplate template = new RabbitTemplate(connectionFactory());
        template.setMandatory(true);
        template.setMessageConverter(new SerializerMessageConverter());
        return template;
    }

    /**
     * 针对消费者配置
     * 1. 设置交换机类型
     * 2. 将队列绑定到交换机
     FanoutExchange: 将消息分发到所有的绑定队列,无routingkey的概念
     HeadersExchange :通过添加属性key-value匹配
     DirectExchange:按照routingkey分发到指定队列
     TopicExchange:多关键字匹配
     */
    @Bean
    public DirectExchange defaultExchangeA() {
        return new DirectExchange( MQCoordinate.EXCHANGE_A);
    }

    /**
     * 获取队列A
     * @return
     */
    @Bean
    public Queue queueA() {
        return new Queue(MQCoordinate.QUEUE_A, true);
    }

    /**
     * 一个交换机可以绑定多个消息队列,也就是消息通过一个交换机,可以分发到不同的队列当中去。
     * 设置多个 , 添加多个binding方法即可
     * @return
     */
    @Bean
    public Binding bindingA() {

        return BindingBuilder.bind(queueA()).to(defaultExchangeA()).with( MQCoordinate.ROUTINGKEY_A);
    }

}

 

  • 消息生产者(业务单一,可以将一个类作为消费者,想要一个类处理多个监听消息,见下文)
package com.config.mq;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.UUID;

/**
 * 消息生成者配置类 by CHENYB of 2019-07-22
 */
public class MsgProducer implements RabbitTemplate.ConfirmCallback {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    public MsgProducer(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
//        rabbitTemplate.setConfirmCallback(this);
    }

    public void sendMsg(String content) {
        CorrelationData correlationId = new CorrelationData( UUID.randomUUID().toString());
        //注意 交换机,路由,队列 定位
//        rabbitTemplate.convertAndSend( "exchange","routingKey","content",new CorrelationData() );//结构样例
        rabbitTemplate.convertAndSend( MQCoordinate.EXCHANGE_A, MQCoordinate.ROUTINGKEY_A, content, correlationId);
    }

    @Override
    public void confirm(CorrelationData correlationData, boolean b, String s) {
        logger.info(" 回调id:" + correlationData);
        if (b) {
            logger.info("消息成功消费");
        } else {
            logger.info("消息消费失败:" + s);
        }
    }


}
  •  消息监听者
//package com.config.mq;
//
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//import org.springframework.amqp.rabbit.annotation.RabbitHandler;
//import org.springframework.amqp.rabbit.annotation.RabbitListener;
//import org.springframework.stereotype.Component;
//
///**
// * 这里只做为监听者demo by CHENYB of date 2019-07-22
// */
//@Component
//@RabbitListener(queues = MQCoordinate.QUEUE_A)
//public class MsgReceiver {
//
//    private final Logger logger = LoggerFactory.getLogger(this.getClass());
//
//    @RabbitHandler
//    public void process(String content) {
//        logger.info("处理器one接收处理队列A当中的消息: " + content);
//    }
//}
  • 坐标类 
package com.config.mq;

/**
 * MQ坐标类
 * by ChenYb date 2019/7/22
 */
public class MQCoordinate {

    /**
     * 坐标
     */
    public static final String EXCHANGE_A = "sync_A"; //交换机
    public static final String ROUTINGKEY_A = "sync_routingKey_A";//路由
    public static final String QUEUE_A = "sync_queue_A";//队列

}
  • 测试代码
@Autowired
	private RabbitTemplate rabbitTemplate;

	@Test
	public void sendMsg (){
		MsgProducer msgProducer = new MsgProducer( rabbitTemplate );
		msgProducer.sendMsg( "张飞" );
		System.out.println("OK!");
	}
  •  效果

有了订阅模式做基础,接下来的模式就很好上手,很好理解了 ,主要就是交换机类型,和绑定与其队列的关系;如果需要做交换机持久化

  • 交换机持久化和交换机声明方法:
@Bean("EXCHANGE_TOPIC_KAIFENG")//声明
    public Exchange EXCHANGE_TOPIC_INFORM(){
        //声明了一个Topic类型的交换机,durable是持久化(重启rabbitmq这个交换机不会被自动删除)
        return ExchangeBuilder.directExchange("EXCHANGE_TOPIC_KAIFENG").durable(true).build();
    }

 

 

  •  广播模式(Fanout)

  • 坐标类
//广播
    public static final String FANOUT_EXCHANGE_NAME = "fanout.exchange.name";
    public static final String TEST_QUEUE1_NAME = "test.queue1.name";
    public static final String TEST_QUEUE2_NAME = "test.queue2.name";
  • 配置

这里只说一下核心的变动 , 重复的东西这里就不说了

  1. FanoutExchange 只注意返回值,返回值为 FanoutExchange的方法 , FanoutExchange对象为扇形传播,广播模式,交换机切换广播模式;
  2. Queue 返回值为 Queue方法,创建两个(更多)这里只做样例,意味着两个队列;
  3. 然后创建两个返回Binding方法(更多)这里只做样例,标记队列与交换机的绑定关系;这里两组队列,一个交换机,貌似关系型数据库1对多表关系;
/**
     * 设置交换机类型 扇形(广播)
     * @return
     */
    @Bean
    public FanoutExchange fanoutExchange() {
        logger.info("【【【广播交换机实例创建成功】】】");
        return new FanoutExchange(MQCoordinate.FANOUT_EXCHANGE_NAME);
    }


    /*广播队列1*/
    @Bean
    public Queue queue1() {
        return new Queue(MQCoordinate.TEST_QUEUE1_NAME);
    }
    /*广播队列2*/
    @Bean
    public Queue queue2() {
        return new Queue(MQCoordinate.TEST_QUEUE2_NAME);
    }


    /**
     * 绑定广播队列到交换机
     * @return
     */
    @Bean
    public Binding bingQueue1ToExchange() {
        return BindingBuilder.bind(queue1()).to(fanoutExchange());
    }
    @Bean
    public Binding bingQueue2ToExchange() {
        return BindingBuilder.bind(queue2()).to(fanoutExchange());
    }
  • 消息生产者:(这里只写了发送消息的方法,confirm方法依旧如上,所以没有写)
    /**
     * 广播-发送消息
     * 说明: routingKey可以指定也可以不指定,这里我们给一个空字符串""
     * 空字符串""就可以
     */
    public void fanoutSendMsg(Object message) {
        logger.info( "【消息发送者】发送消息到fanout交换机,消息内容为: {}", message );
        rabbitTemplate.convertAndSend( MQCoordinate.FANOUT_EXCHANGE_NAME, "", message );
    }
  • 监听者:(这里有两个队列为例,就写了两个监听类),主要看@RabbitListener()配置,其他如上
  • 监听类1
package com.config.mq;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 这里只做为监听者demo
 */
@Component
@RabbitListener(queues = MQCoordinate.TEST_QUEUE1_NAME)
public class MsgReceiver {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @RabbitHandler
    public void process(String content) {
        logger.info("广播消息-接收处理队列1当中的消息: " + content);
    }
}
  •  监听类2
package com.config.mq;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 这里只做为监听者demo
 */
@Component
@RabbitListener(queues = MQCoordinate.TEST_QUEUE2_NAME)
public class MsgReceiver2 {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @RabbitHandler
    public void process(String content) {
        logger.info("广播消息-接收处理队列2当中的消息: " + content);
    }
}
  •  测试代码:
@Test
	public void fanoutSendMsg (){
		MsgProducer msgProducer = new MsgProducer( rabbitTemplate );
		for (int i = 0; i < 100; i++) {
			msgProducer.fanoutSendMsg( "下面的朋友可以收到我的第<"+(i+1)+">次问候吗?" );
		}
	}
  • 完成效果:

SpringBoot+RabbitMQ整合(附工具类demo),订阅,广播,组播,模糊播_第1张图片

 

  • 组播(Topic)

  • 交换机对象TopicExchange 
  • #,*,精确,可以理解为权限,#匹配高,*低,精确要求完全匹配

 

配置类: (主要说交换机和binding)

/**
     * 创建模糊播-交换机
     * @return
     */
    @Bean
    public TopicExchange topicExchange (){
        logger.info("【【【模糊交换机实例创建成功】】】");
        return new TopicExchange( MQCoordinate.TOPIC_EXCHANGE_NAME );
    }

    /**
     * 创建两个队列
     * @return
     */
    @Bean
    public Queue topQueue1(){
        return new Queue( MQCoordinate.TOPIC_QUEUE1_NAME );
    }
    @Bean
    public Queue topQueue2(){
        return new Queue( MQCoordinate.TOPIC_QUEUE2_NAME );
    }
    @Bean
    public Queue topQueue3(){
        return new Queue( MQCoordinate.TOPIC_QUEUE3_NAME );
    }

    /**
     * routingkey可以有通配符:'*','#'.
     *
     * 其中'*'表示匹配一个单词, '#'则表示匹配没有或者多个单词
     * @return
     */
    @Bean
    public Binding topQueue1ToExchange(){
        return BindingBuilder.bind( topQueue1() ).to( topicExchange() ).with( MQCoordinate.TOPIC_QUEUE1_NAME );
    }

    @Bean
    public Binding topQueue2ToExchange(){
        return BindingBuilder.bind( topQueue2() ).to( topicExchange() ).with( MQCoordinate.TOPIC_QUEUE2_NAME );
    }

    @Bean
    public Binding topQueue3ToExchange(){
        return BindingBuilder.bind( topQueue3() ).to( topicExchange() ).with( MQCoordinate.TOPIC_QUEUE3_NAME );
    }

坐标类:

//模糊播
    public static final String TOPIC_EXCHANGE_NAME = "topic.exchange.name";
    public static final String TOPIC_QUEUE1_NAME = "*.weather";
    public static final String TOPIC_QUEUE2_NAME = "*.news";
    public static final String TOPIC_QUEUE3_NAME = "#";

生产者:

public void topicSendMsg (String message){
        logger.info( "【消息发送者】发送消息到topic交换机,消息内容为: {}", message );
        rabbitTemplate.convertAndSend( MQCoordinate.TOPIC_EXCHANGE_NAME, MQCoordinate.TOPIC_QUEUE2_NAME, message );
    }

消费者:(这里用一个类,处理多个监听消息)

package com.config.mq;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 这里只做为监听者demo
 */
@Component
public class MsgReceiver {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @RabbitListener(queues = MQCoordinate.TOPIC_QUEUE1_NAME)
    public void process(String content) {
        logger.info("模糊播消息-接收处理队列A当中的消息: " + content);
    }
    @RabbitListener(queues = MQCoordinate.TOPIC_QUEUE2_NAME)
    public void process2(String content) {
        logger.info("模糊播消息-接收处理队列B当中的消息: " + content);
    }
    @RabbitListener(queues = MQCoordinate.TOPIC_QUEUE3_NAME)
    public void process3(String content) {
        logger.info("模糊播消息-接收处理队列C当中的消息: " + content);
    }
}

 

测试代码:

	@Test
	public void topicSendMsg (){
		MsgProducer msgProducer = new MsgProducer( rabbitTemplate );
		msgProducer.topicSendMsg1( "黑龙江,多云!" );
		msgProducer.topicSendMsg2( "黑龙江,街头古惑仔" );
	}

效果:

  • headers(模糊播)

  • 其他基本同上,Binding需要定义属性,和匹配度

配置类:(这里只说交换机和Banding) 

/**
     * 创建headers 交换机
     */
    public HeadersExchange headersExchange (){
        logger.info("【【【headers交换机实例创建成功】】】");
        return new HeadersExchange( MQCoordinate.HEADERS_EXCHANGE_NAME );
    }

@Bean
    public Binding headers1Binding (){

        Map map = new HashMap<>();
        map.put("queueName","queueN1");
        map.put("bindType","whereAll");

        //whereAny表示部分匹配
        return BindingBuilder.bind(headersQueue2()).to(headersExchange()).whereAny(map).match();
    }

    @Bean
    public Binding headers2Binding (){

        Map map = new HashMap<>();
        map.put("queueName","queueN1");
        map.put("bindType","whereAll");

//        whereAll表示全部匹配
        return BindingBuilder.bind(headersQueue1()).to(headersExchange()).whereAll(map).match();
    }

 坐标:

    public static final String HEADERS_EXCHANGE_NAME = "headers.exchange";
    public static final String HEADERS_QUEUE_NAME1 = "queueN1";
    public static final String HEADERS_QUEUE_NAME2 = "queueN2";

异常忽略:(配置类中需要开启异常忽略,否则会IO异常问题)

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        //设置忽略声明异常
        rabbitAdmin.setIgnoreDeclarationExceptions(true);
        return rabbitAdmin;
    }

测试代码:

    @Test
    public void headersSendMsg1 (){
        MsgProducer msgProducer = new MsgProducer( rabbitTemplate );

        /**
         * 声明消息 (消息体, 消息属性)
         */
		String messageStr = "hello queueN1";
		MessageProperties messageProperties = new MessageProperties();
		messageProperties.setHeader("queueName","queueN1");
		messageProperties.setHeader("bindType","whereAll");
		Message message = new Message(messageStr.getBytes(), messageProperties);
        msgProducer.headersSendMsg(message);
    }

 

 

 Mr.chenyb 随笔记录,方便学习

201907-19

 

 

你可能感兴趣的:(Java技术,Utils,SpringBoot,MQ)