SpringBoot--整合RabbitMQ

整合SpringBoot

SpringBoot未AMQP提供了自动化配置依赖spring-boot-starter-amqp,因此首先创建项目,并添加依赖:

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

在application.properties中配置RabbitMQ的基本连接信息:

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

在RabbitMQ中,所有的消息生产者提交的消息都会交由Exchange进行再分配,Exchange会根据不同的策略将消息分发到不同的Queue中。RabbitMQ中一共提供了4中不同的Exchange:

  • Direct
  • Fanout
  • Topic
  • Header

Direct:

DirectExchange的路由策略是讲消息队列绑定到一个DirectExchange上,当一条消息到达DirectExchange时会被转发到与该条消息routing key 相同的Queue上,例如消息队列的列名为"hello-queue",则routingkey为"hello-queue"的消息会被该消息队列接收。

DirectExchange配置如下:

@Configuration
public class RabbitDirectConfig {

    public final static String DIRECTNAME = "my-direct";

    //提供一个消息队列Queue
    @Bean
    Queue queue(){
        return new Queue("hello-queue");
    }

    //创建一个DirectExchange对象,三个参数分别为:名字、重启后是否依然有效以及长期未使用是否删除
    @Bean
    DirectExchange directExchange(){
        return new DirectExchange(DIRECTNAME, true, false);
    }

    //创建一个Binding对象,将Exchange和Queue绑定在一起
    @Bean
    Binding binding(){
        return BindingBuilder.bind(queue()).to(directExchange()).with("direct");
    }
}

配置一个消费者:

@Component
public class DirectReceiver {

    @RabbitListener(queues = "hello-queue")
    public void handler1(String msg){
        System.out.println("DirectReceiver:" + msg);
    }
}

单元测试:

@RunWith(SpringRunner.class)
@SpringBootTest
class DemoApplicationTests {

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Test
    public void directTest(){
        rabbitTemplate.convertAndSend("hello-queue", "hellow direct!");
    }

}

确定RabbitMQ已经启动,然后启动项目,控制台打印:

DirectReceiver:hellow direct!

如果发送的消息为对象类型,可以将其转换为JSON格式,需要配置一个MessageConverter:

@Configuration
public class MyAMQPConfig {

    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
}

测试:

扫描二维码关注公众号,回复: 9056499 查看本文章
@Test
    public void directTest(){
        //rabbitTemplate.convertAndSend("hello-queue", "hellow direct!");
        rabbitTemplate.convertAndSend("hello-queue", new Book(1, "红楼梦", "曹雪芹"));
    }

在这里插入图片描述

Fanout:

FanoutExchange的数据交换策略是把所有到达FanoutExchange的消息转发给所有与它绑定的Queue,在这种策略中,routingkey将不起任何作用,FanoutExchange的配置方式如下:

@Configuration
public class RabbitFanoutConfig {
    
    public final static String FANOUTNAME = "my-fanout";
    
    @Bean
    FanoutExchange fanoutExchange(){
        return new FanoutExchange(FANOUTNAME, true, false);
    }
    
    @Bean
    Queue queue1(){
        return new Queue("queue-one");
    }
    
    @Bean
    Queue queue2(){
        return new Queue("queue-two");
    }
    
    @Bean
    Binding binding1(){
        return BindingBuilder.bind(queue1()).to(fanoutExchange());
    }
    
    @Bean
    Binding binding2(){
        return BindingBuilder.bind(queue2()).to(fanoutExchange());
    }
}

在这里收下创建FanoutExchange,参数的含义与DirectExchange一致,然后创建两个Queue,再将这两个Queue都绑定到FanoutExchange上。接下来创建两个消费者,代码如下:

@Component
public class FanoutReceiver {

    @RabbitListener(queues = "queue-one")
    public void handler1(String msg){
        System.out.println("FanoutReceiver:handler1:" + msg);
    }

    @RabbitListener(queues = "queue-two")
    public void handler2(String msg){
        System.out.println("FanoutReceiver:handler2:" + msg);
    }
}

单元测试:

@Test
    public void fanoutTest(){
        //这里发送消息不需要routingkey,指定exchange即可
        rabbitTemplate.convertAndSend(RabbitFanoutConfig.FANOUTNAME, null, "hello fanout!");
    }

运行测试,控制台打印:

FanoutReceiver:handler2:hello fanout!
FanoutReceiver:handler1:hello fanout!

Topic:

TopicExchange是比较复杂也比较灵活的一种路由策略,在TopicExchange中,Queue通过routingkey绑定到TopicExchange上,当消息到达TopicExchange后,TopicExchange根据消息的routingkey将消息路由到一个或者多个Queue上。TopicExchange配置如下:

@Configuration
public class RabbitTopicConfig {
    
    public final static String TOPICNAME = "my-topic";
    
    @Bean
    TopicExchange topicExchange(){
        return new TopicExchange(TOPICNAME, true, false);
    }
    
    @Bean
    Queue xiaomi(){
        return new Queue("xiaomi");
    }
    
    @Bean
    Queue huawei(){
        return new Queue("huawei");
    }
    
    @Bean
    Queue phone(){
        return new Queue("phone");
    }
    
    @Bean
    Binding xiaomiBinding(){
        return BindingBuilder.bind(xiaomi()).to(topicExchange()).with("xiaomi.#");
    }
    
    @Bean
    Binding huaweiBinding(){
        return BindingBuilder.bind(huawei()).to(topicExchange()).with("huawei.#");
    }
    
    @Bean
    Binding phoneBinding(){
        return BindingBuilder.bind(phone()).to(topicExchange()).with("#.phone.#");
    }
}

将三个Queue分别绑定到TopicExchange上,第一个Binding中的"xiaomi.#"表示消息的routingkey凡是以"xiaomi"开头的,都将被路由到名称为"xiaomi"的Queue上;第二个Binding中的"huawei.#“表示消息的routingkey凡是以"huawei"开头的,都将被路由到名称为"huawei"的Queue上;第三个Binding中的”#.phone.#"则表示消息的routingkey中凡是包含"phone"的,都将被路由到名称为"phone"的Queue上。

针对单个Queue创建三个消费者,代码如下:

@Component
public class TopicReceiver {
    
    @RabbitListener(queues = "phone")
    public void handler1(String msg){
        System.out.println("PhoneReceeiver:" + msg);
    }
    
    @RabbitListener(queues = "xiaomi")
    public void handler2(String msg){
        System.out.println("XiaoMiReceiver:" + msg);
    }
    
    @RabbitListener(queues = "huawei")
    public void handler3(String msg){
        System.out.println("HuaWeiReceiver:" + msg);
    }
}

单元测试:

@Test
    public void topicTest(){
        rabbitTemplate.convertAndSend(RabbitTopicConfig.TOPICNAME, "xiaomi.news", "小米新闻..");
        rabbitTemplate.convertAndSend(RabbitTopicConfig.TOPICNAME, "huawei.news", "华为新闻..");
        rabbitTemplate.convertAndSend(RabbitTopicConfig.TOPICNAME, "xiaomi.phone", "小米手机..");
        rabbitTemplate.convertAndSend(RabbitTopicConfig.TOPICNAME, "huawei.phone", "华为手机..");
        rabbitTemplate.convertAndSend(RabbitTopicConfig.TOPICNAME, "phone.news", "手机新闻..");
    }

运行单元测试,控制台打印:

XiaoMiReceiver:小米新闻..
PhoneReceeiver:小米手机..
HuaWeiReceiver:华为新闻..
PhoneReceeiver:华为手机..
XiaoMiReceiver:小米手机..
HuaWeiReceiver:华为手机..
PhoneReceeiver:手机新闻..

Header:
HeaderExchange是一种使用较少的路由策略,HeaderExchange会根据消息的Header将消息路由到不同的Queue傻姑娘,这种策略也和routingkey无关,配置如下:

@Configuration
public class RabbitHeaderConfig {
    
    public final static String HEADERNAME = "my-header";
    
    @Bean
    HeadersExchange headersExchange(){
        return new HeadersExchange(HEADERNAME, true, false);
    }
    
    @Bean
    Queue queueName(){
        return new Queue("name-queue");
    }
    
    @Bean
    Queue queueAge(){
        return new Queue("age=queue");
    }
    
    @Bean
    Binding bindingName(){
        Map<String, Object> map = new HashMap<>();
        map.put("name", "my");
        //whereAny表示消息的Header中只要有一个Header匹配上map中的key/value,就把该消息路由到名为"name-queue"的Queue上
        //也可以使用whereAll方法,表示消息的所有Header都要匹配
        //whereAny和whereAll实际上对应了一个名为x-match的属性
        return BindingBuilder.bind(queueName()).to(headersExchange()).whereAny(map).match();
    }
    
    @Bean
    Binding bindingAge(){
        //bindingAge中的配置则表示只要消息的Header中包含age,无论age的值是多少,都将消息路由到名为"age-queue"的Queue上
        return BindingBuilder.bind(queueAge()).to(headersExchange()).where("age").exists();
    }
}

创建两个消费者(参数用byte[]接收):

@Component
public class HeaderReceiver {
    
    @RabbitListener(queues = "name-queue")
    public void handler1(byte[] msg){
        System.out.println("HeaderReceiver:name:" + new String(msg, 0, msg.length));
    }
    
    @RabbitListener(queues = "age-queue")
    public void handler2(byte[] msg){
        System.out.println("HeaderReceiver:age:" + new String(msg, 0, msg.length));
    }
}

单元测试:

@Test
    public void headerTest(){
        Message nameMsg = MessageBuilder.withBody("hello header! name-queue".getBytes())
                .setHeader("name", "sang").build();
        Message ageMsg = MessageBuilder.withBody("hello header! age-queue".getBytes())
                .setHeader("age", "99").build();
        rabbitTemplate.send(RabbitHeaderConfig.HEADERNAME, null, ageMsg);
        rabbitTemplate.send(RabbitHeaderConfig.HEADERNAME, null, nameMsg);
    }

运行测试,控制台打印:

HeaderReceiver:age:hello header! age-queue
HeaderReceiver:name:hello header! name-queue
发布了716 篇原创文章 · 获赞 2079 · 访问量 26万+

猜你喜欢

转载自blog.csdn.net/cold___play/article/details/104200772