RabbitMQ

RabbitMQ消息模型

RabbitMQ官方提供了5个不同的Demo示例,对应了不同的消息模型:

  • 简单模型:一个消费者,一个发布者
  • Work模型:多个消费者绑定到一个队列,同一条消息只会被一个消费者处理
  • 广播模型:Fanout交换机将消息路由给每一个与之绑定的队列
  • 路由模型:Direct交换机根据RoutingKey判断路由给哪个队列
  • 主题模型:Topic交换机接收的消息RoutingKey必须是多个单词,以 `**.**` 分割

导依赖

XML 复制代码
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

改配置

javascript 复制代码
spring:
  rabbitmq:
    host: 192.168.150.101 # 主机名,mq所在服务器
    port: 5672 # 端口
    virtual-host: / # 虚拟主机
    username: itcast # 用户名
    password: 123321 # 密码

Queue 简单队列模型

发布者端

java 复制代码
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Test
    public void testSimpleQueue() {
        // 队列名称
        String queueName = "simple.queue";
        // 消息
        String message = "hello, spring amqp!";
        // 发送消息
        rabbitTemplate.convertAndSend(queueName, message);
    }

消费者端

java 复制代码
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class SpringRabbitListener {
    @RabbitListener(queues = "simple.queue")//指定队列名监听
    public void listenSimpleQueueMessage(String msg) {
        System.out.println("spring 消费者接收到消息:【" + msg + "】");
    }
}

WorkQueue​​​​​​​工作队列

Work queue,工作队列模型,可以提高消息处理速度,避免队列消息堆积

发布者端

java 复制代码
@Test
public void testWorkQueue() throws InterruptedException {
    // 队列名称
    String queueName = "simple.queue";
    // 消息
    String message = "hello, message_";
    // 发送消息
    rabbitTemplate.convertAndSend(queueName, message + i);
}

消费者端

java 复制代码
@RabbitListener(queues = "simple.queue")
public void listenWorkQueue1(String msg) throws InterruptedException {
    System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now());
    Thread.sleep(20);
}

@RabbitListener(queues = "simple.queue")
public void listenWorkQueue2(String msg) throws InterruptedException {
    System.err.println("消费者2........接收到消息:【" + msg + "】" + LocalTime.now());
    Thread.sleep(200);
}

因为默认是均分的,如果一个服务器很慢,则会影响整个系统执行效率,做到能者多劳,在spring中有一个简单的配置,可以解决这个问题。我们修改consumer服务的application.yml文件,添加配置:

javascript 复制代码
spring:
  rabbitmq:
    listener:
      simple:
        prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息

发布/订阅模型

发布订阅模式与之前案例的区别就是允许将同一消息发送给多个消费者。

模型

在consumer消费者中创建一个类,声明队列和交换机:

java 复制代码
@Configuration
public class FanoutConfig {
     //声明这是一个广播类型的交换机
    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange("exchange.fanout");//广播Fanout类型交换机
    }

    //第1个队列
    @Bean
    public Queue fanoutQueue1(){
        return new Queue("fanout.queue1");
    }

    //绑定队列和交换机 
    @Bean
    public Binding bindingQueue1(Queue fanoutQueue1, FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
    }

   //第2个队列
    @Bean
    public Queue fanoutQueue2(){
        return new Queue("fanout.queue2");
    }

    //绑定队列和交换机
    @Bean
    public Binding bindingQueue2(Queue fanoutQueue2, FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
    }
}

发布者端

java 复制代码
@Test
public void testFanoutExchange() {
    // 队列名称
    String exchangeName = "exchange.fanout";
    // 消息
    String message = "hello, everyone!";
    rabbitTemplate.convertAndSend(exchangeName, "", message);
}

消费者端

java 复制代码
@RabbitListener(queues = "fanout.queue1")
public void listenFanoutQueue1(String msg) {
    System.out.println("消费者1接收到Fanout消息:【" + msg + "】");
}

@RabbitListener(queues = "fanout.queue2")
public void listenFanoutQueue2(String msg) {
    System.out.println("消费者2接收到Fanout消息:【" + msg + "】");
}

路由模型

我们希望不同的消息被不同的队列消费,这时就要用到Direct类型的Exchange。

基于注解声明队列和交换机

发布者端

java 复制代码
@Test
public void testSendDirectExchange() {
    // 交换机名称
    String exchangeName = "exchange.direct";
    // 消息
    String message = "红色警报!日本乱排核废水,导致海洋生物变异,惊现哥斯拉!";
    // 发送消息
    rabbitTemplate.convertAndSend(exchangeName, "red", message);
}

消费者端

java 复制代码
@RabbitListener(bindings = @QueueBinding(
    value = @Queue(name = "direct.queue1"),
    exchange = @Exchange(name = "exchange.direct", type = ExchangeTypes.DIRECT),
    key = {"red", "blue"}//只消费带有,red或者blue的消息
))
public void listenDirectQueue1(String msg){
    System.out.println("消费者接收到direct.queue1的消息:【" + msg + "】");
}



@RabbitListener(bindings = @QueueBinding(
    value = @Queue(name = "direct.queue2"),
    exchange = @Exchange(name = "exchange.direct", type = ExchangeTypes.DIRECT),
    key = {"red", "yellow"}//只消费带有,red或者yellow的消息
))
public void listenDirectQueue2(String msg){
    System.out.println("消费者接收到direct.queue2的消息:【" + msg + "】");
}

主题模型

发布者端

java 复制代码
@Test
public void testSendTopicExchange() {
    // 交换机名称
    String exchangeName = "exchange.topic";
    // 消息
    String message = "喜报!孙悟空大战哥斯拉,胜!";
    // 发送消息
    rabbitTemplate.convertAndSend(exchangeName, "china.news", message);
}

消费者端

java 复制代码
@RabbitListener(bindings = @QueueBinding(
    value = @Queue(name = "topic.queue1"),
    exchange = @Exchange(name = "exchange.topic", type = ExchangeTypes.TOPIC),
    key = "china.#"
))
public void listenTopicQueue1(String msg){
    System.out.println("消费者接收到topic.queue1的消息:【" + msg + "】");
}

@RabbitListener(bindings = @QueueBinding(
    value = @Queue(name = "topic.queue2"),
    exchange = @Exchange(name = "exchange.topic", type = ExchangeTypes.TOPIC),
    key = "#.news"
))
public void listenTopicQueue2(String msg){
    System.out.println("消费者接收到topic.queue2的消息:【" + msg + "】");
}

消息转换器

默认实现是SimpleMessageConverter,基于JDK的ObjectOutputStream完成序列化。发送对象类型时,会乱码,如果要修改只需要定义一个MessageConverter 类型的Bean即可。推荐用JSON方式序列化,步骤如下:

XML 复制代码
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.9.10</ version >
</dependency>

在配置类声明转换器

java 复制代码
@Bean
public MessageConverter jsonMessageConverter(){
   return new Jackson2JsonMessageConverter();
}
相关推荐
羊锦磊33 分钟前
[ Mybatis 多表关联查询 ] resultMap
java·开发语言·数据库·mysql·mybatis
在未来等你3 小时前
RabbitMQ面试精讲 Day 19:网络调优与连接池管理
性能优化·消息队列·rabbitmq·高并发·连接池·面试准备·网络调优
ZeroToOneDev3 小时前
Java(泛型和JUnit)
java·开发语言·笔记
迪尔~5 小时前
Apache POI中通过WorkBook写入图片后出现导出PDF文件时在不同页重复写入该图片问题,如何在通过sheet获取绘图对象清除该图片
java·pdf·excel
现在,此刻5 小时前
leetcode 11. 盛最多水的容器 -java
java·算法·leetcode
DKPT6 小时前
Java设计模式之开闭原则介绍与说明
java·设计模式·开闭原则
hyy27952276846 小时前
企业级WEB应用服务器TOMCAT
java·前端·tomcat
布朗克1686 小时前
Spring Boot项目通过Feign调用三方接口的详细教程
java·spring boot·feign
Arva .6 小时前
Spring基于XML的自动装配
xml·java·spring
帅得不敢出门8 小时前
Android Framework定制长按电源键关机的窗口
android·java·framework