Spring Boot 2.x实战93 - 事件驱动2 - RabbitMQ

2.RabbitMQ

这节演示RabbitMQ支持的AMQP协议,理解AMQP需要了解下面三个概念:

当发布者发送的消息和消息的routing key到RabbitMQ中间件时:

  • exchange:基于消息的routing key,将消息路由到一个或多个的queue
  • bindingbinding是设置queue绑定到exchange的连接;
  • queue:消息队列

exchange主要有下面的类型:

  • Default:将消息路由到名称为routing key的queue,所有的queue都会自动绑定到default exchange;
  • Direct:将消息路由到binding key与消息的routing key一致的queue;
  • Fanout:将消息路由到所有绑定的queue,不考虑binding key和routing key;
  • Topic:将消息路由到binding key匹配routing key的一个或多个queue,匹配可包含通配符;
  • Headers:类似于topic,但路由基于消息头而不是routing key。

2.1 安装RabbitMQ

使用docker compose安装RabbitMQ。

stack.yml

version: '3.1'

services:
  rabbitmq:
    image: rabbitmq:management
    restart: always
    ports:
      - "5672:5672" # 连接端口
      - "15672:15672" # 管理控制台端口
    environment:
      RABBITMQ_DEFAULT_USER: wisely
      RABBITMQ_DEFAULT_PASS: zzzzzz

执行命令:

$ docker-compose -f stack.yml up -d

2.2 新建应用

新建应用,信息如下:

Group:top.wisely

Artifact:learning-amqp

Dependencies:Spring for RabbitMQLombok

build.gradle文件中的依赖如下:

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-amqp'
  compileOnly 'org.projectlombok:lombok'
   annotationProcessor 'org.projectlombok:lombok'
	//...
}

2.3 Spring Boot的自动配置

Spring Boot的自动配置有:

  • RabbitAutoConfiguration:
    • 配置连接RabbitMQ的rabbitConnectionFactory的Bean;
    • 配置用来操作消息的rabbitTemplate的Bean;
    • 通过@EnableRabbit注解开启RabbitMQ的支持。

通过RabbitProperties使用spring.rabbitmq.*对RabbitMQ进行配置。

2.4 示例

  • 连接RabbitMQ

    spring:
      rabbitmq:
        host: localhost
        port: 5672
        username: wisely
        password: zzzzzz
    
  • 消息定义

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class MessageEvent implements Serializable {
        private String id;
        private String name;
    }
    
  • 配置

    @SpringBootApplication
    @EnableScheduling //1
    public class LearningAmqpApplication {
    
       public static void main(String[] args) {
          SpringApplication.run(LearningAmqpApplication.class, args);
       }
    
       @Bean
       Queue myDest(){ //2 
          return new Queue("my-dest");
       }
    
       @Bean
       Queue confirmDest(){ //3
          return new Queue("confirm-dest");
       }
    }
    
    1. 开启计划任务的支持;
    2. 新建用于接受消息的queue:my-dest
    3. 新建用于确认消息的queue:confirm-dest
  • 发送消息

    @Component
    public class MessageProducer {
    
        RabbitTemplate rabbitTemplate;
    
        public MessageProducer(RabbitTemplate rabbitTemplate) {
            this.rabbitTemplate = rabbitTemplate;
        }
    
        @Scheduled(fixedRate = 2000)
        public void send(){
            rabbitTemplate.convertAndSend("my-dest",
                    new MessageEvent(UUID.randomUUID().toString(), "wyf"));
        }
    }
    
    1. 使用@Scheduled每隔两秒钟发送一次消息;
    2. 通过RabbitTemplateconvertAndSend(String routingKey, final Object object),此处的第一个参数是routing Key my-dest,此时的exchange是一个default exchange;此时消息会发到名称为my-dest的queue。
  • 接受消息并应答

    @Component
    public class MessageConsumer {
    
        @RabbitListener(queuesToDeclare = @Queue(name = "my-direct"))
        @SendTo("confirm-dest")
        public String consume(MessageEvent event){
            System.out.println("在consume方法中处理事件" + event);
            return "接收到了:" + event.toString();
        }
    }
    
    1. 使用@RabbitListener指定从队列获取消息,使用queuesToDeclare可以在RabbitMQ上声明queue;

      我们也可以通过自己声明Queue,使用@RabbitListenerqueues属性来指定queue:

      @Bean
      Queue myDest(){
         return new Queue("my-dest");
      }
      
      @RabbitListener(queues = "my-dest")
      @SendTo("confirm-dest")
      public String consume(MessageEvent event){
          System.out.println("在consume方法中处理事件" + event);
          return "接收到了:" + event.toString();
      }
      
    2. 当处理完成后可通过@SendTo向指定routing key触发另外的处理;这里的confirm-dest同样为routing key;若既需要指定exchange又需要指定routing key,可以使用@SendTo("some-exchange/confirm-dest")

  • 处理应答

    @Component
    public class ConfirmConsumer {
        @RabbitListener(queuesToDeclare = {@Queue(name = "confirm-dest")})
        public void confirmReceived(String confirm){
            System.out.println(confirm);
        }
    }
    
  • 启动应用

在这里插入图片描述

2.5 其它类型exchange

2.5.1 Direct Exchange
  • 发送消息

    @Component
    public class MessageProducer {
    
        RabbitTemplate rabbitTemplate;
    
        public MessageProducer(RabbitTemplate rabbitTemplate) {
            this.rabbitTemplate = rabbitTemplate;
        }
    
        @Scheduled(fixedRate = 2000)
        public void sendDirect(){
            rabbitTemplate.convertAndSend("direct-exchange", "some-key",
                    new MessageEvent(UUID.randomUUID().toString(), "wyf"));
        }
    }
    

    第一个参数direct-exchange为exchange名称,some-key为routing key。

  • 接受消息:

    @Component
    public class MessageConsumer {
    
        @RabbitListener(bindings = @QueueBinding(
                exchange = @Exchange(name = "direct-exchange", type = ExchangeTypes.DIRECT),
                value = @Queue(name = "direct-queue"),
                key = "some-key"
        ))
        public void consumeDirect(MessageEvent event){
            System.out.println("在consumeDirect方法中处理事件" + event);
        }
    
        @RabbitListener(bindings = @QueueBinding(
                exchange = @Exchange(name = "direct-exchange", type = ExchangeTypes.DIRECT),
                value = @Queue(name = "direct-queue2"),
                key = "some-key"
        ))
        public void consumeDirect2(MessageEvent event){
            System.out.println("在consumeDirect2方法中处理事件" + event);
        }
    }
    

    我们可以使用@RabbitListenerbindings属性,通过@QueueBinding来声明binding,我们在看看@QueueBinding的属性:

    • exchange:通过@Exchange声明名称为direct-exchange的exchange,默认类型是Direct可省略;
    • value:通过@Queue声明名称为direct-queue的queue;
    • key:此处为binding key。

    上面的例子中我们使用了两个binding,分别绑定了exchange和不同的两个queue,它们的binding key都和routing key相同,两个queue都能收到消息。启动应用:

在这里插入图片描述

当然,我们也可以自己声明binding、exchange、queue。

	@Bean
	Queue directQueue(){
		return new Queue("direct-queue");
	}

	@Bean
	DirectExchange directExchange(){
		return new DirectExchange("direct-exchange");
	}

	@Bean
	Binding directBinding(){
		return BindingBuilder.bind(directQueue()).to(directExchange()).with("some-key");
	}
@RabbitListener(queues = "direct-queue")
public void consumeDirect(MessageEvent event){
    System.out.println("在consumeDirect方法中处理事件" + event);
}
2.5.2 Topic Exchange
  • 发送消息

    @Component
    public class MessageProducer {
    
        RabbitTemplate rabbitTemplate;
    
        public MessageProducer(RabbitTemplate rabbitTemplate) {
            this.rabbitTemplate = rabbitTemplate;
        }
    
        @Scheduled(fixedRate = 2000)
        public void sendTopic(){
            rabbitTemplate.convertAndSend("topic-exchange", "some.key.topic",
                    new MessageEvent(UUID.randomUUID().toString(), "wyf"));
        }
    }
    

    topic-exchange为exchange的名称;some.key为routing key,发送给topic exchange的routing key不能是任意的,要求是使用“.”隔开的词。

  • 接受消息

    @Component
    public class MessageConsumer {
        @RabbitListener(bindings = @QueueBinding(
                exchange = @Exchange(name = "topic-exchange", type = ExchangeTypes.TOPIC),
                value = @Queue(name = "topic-queue"),
                key = "some.*.topic"
        ))
        public void consumeTopic(MessageEvent event){
            System.out.println("在consumeTopic方法中处理事件" + event);
        }
    
        @RabbitListener(bindings = @QueueBinding(
                exchange = @Exchange(name = "topic-exchange", type = ExchangeTypes.TOPIC),
                value = @Queue(name = "topic-queue2"),
                key = "#.topic"
        ))
        public void consumeTopic2(MessageEvent event){
            System.out.println("在consumeTopic方法中处理事件" + event);
        }
    }
    
    

    这里的binding key,有两个特殊的匹配字符:

    • “*”:可替代一个词
    • “#”:可替代一个活多个词

    上面两种情况都可匹配。启动应用:

在这里插入图片描述

新书推荐:

我的新书《从企业级开发到云原生微服务:Spring Boot 实战》已出版,内容涵盖了丰富Spring Boot开发的相关知识
购买地址:https://item.jd.com/12760084.html
在这里插入图片描述

主要包含目录有:

第一章 初识Spring Boot(快速领略Spring Boot的美丽)
第二章 开发必备工具(对常用开发工具进行介绍:包含IntelliJ IDEA、Gradle、Lombok、Docker等)
第三章 函数式编程
第四章 Spring 5.x基础(以Spring 5.2.x为基础)
第五章 深入Spring Boot(以Spring Boot 2.2.x为基础)
第六章 Spring Web MVC
第七章 数据访问(包含Spring Data JPA、Spring Data Elasticsearch和数据缓存)
第八章 安全控制(包含Spring Security和OAuth2)
第九章 响应式编程(包含Project Reactor、Spring WebFlux、Reactive NoSQL、R2DBC、Reactive Spring Security)
第十章 事件驱动(包含JMS、RabbitMQ、Kafka、Websocket、RSocket)
第11章 系统集成和批处理(包含Spring Integration和Spring Batch)
第12章 Spring Cloud与微服务
第13章 Kubernetes与微服务(包含Kubernetes、Helm、Jenkins、Istio)
多谢大家支持。

猜你喜欢

转载自blog.csdn.net/wiselyman/article/details/107310145
今日推荐