rabbitmq笔记

97 阅读3分钟

同步调用优缺点

优点

  • 时效性较强,可以立即得到结果

缺点

  • 耦合度高
  • 性能和吞吐能力下降
  • 有额外的资源消耗
  • 有级联失败问题

异步通信优缺点

优点

  • 耦合度低
  • 吞吐量提升
  • 故障隔离
  • 流量削峰

缺点

  • 依赖于Broker的可靠性、安全性、吞吐能力
  • 架构复杂了,业务没有明显的流程线,不好追踪管理

mq常见技术

mqtype.jpg

RabbitMQ概述

rabbitstruc.jpg

概念

  • channel:操作MQ的工具
  • exchange:路由消息到队列中
  • queue:缓存消息
  • virtual host:虚拟主机,是对queue、exchange等资源的逻辑分组

常见消息模型

MQ的官方文档中给出了5个MQ的Demo示例

  • 基本消息队列(basicqueue) basic.jpg
  • 工作消息队列(workqueue)
    • 多个消费者绑定到一个队列,同一条消息只会被一个消费者处理
    • 通过设置prefetch来控制消费者预取的消息数量。防止效率低的消费者取到太多消息,降低效率 截屏2022-12-10 11.43.11.png
  • 发布订阅(pub,sub)
    • 广播(fanout)
      • 发送给所有队列 截屏2022-12-10 11.43.23.png
    • 路由(direct/routing)
      • 发送到指定队列 截屏2022-12-10 11.43.28.png
    • 主题(topic)
      • 分类配置 截屏2022-12-10 11.43.32.png

SpringAMQP

AMOP

一种mq标准

SpringAMQP

基于AMQP协议定义的API规范,提供模板发送和接收消息

  • 包含两部分
    • spring-amqp:基础抽象
    • spring-rabbit:底层实现

Rabbit基本消息队列示例

<!--AMQP依赖,包含RabbitMQ-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    virtual-host: /
    username: zxy
    password: 123456
//生产者
@SpringBootTest
public class test {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    // 不会自动创建队列
    @Test
    public void testSimpleQueue(){
        String queueName = "simple.queue";
        String message = "hello,spring amqp";
        rabbitTemplate.convertAndSend(queueName,message);
    }
}
//消费者
@Component
public class ConsumerMessage {
    @RabbitListener(queues = "simple.queue")
    public void listener(String msg){
        System.out.println("我来消费了:"+msg);
    }
}

Rabbit工作消息队列示例

#消费者配置
spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    virtual-host: /
    username: zxy
    password: 123456
    listener:
      simple:
        prefetch: 1 #预取消息数
//消费者代码
@RabbitListener(queues = "simple.queue")
public void listenerWork1(String msg) throws InterruptedException {
    System.out.println("我来消费了1:"+msg+":"+ LocalTime.now());
//        Thread.sleep(20);
}

@RabbitListener(queues = "simple.queue")
public void listenerWork2(String msg) throws InterruptedException {
    System.out.println("我来消费了2:"+msg+":"+ LocalTime.now());
//        Thread.sleep(20);
}

RabbitMQ发布订阅(广播)

//交换机、队列绑定关系配置
@Configuration
public class FonoutConfig {
    //声明交换机
    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange("itcast.fanout");
    }
    //声明第一个队列
    @Bean
    public Queue fanoutQueue1(){
        return new Queue("fanout.queue1");
    }

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

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

    //绑定队列2和交换机
    @Bean
    public Binding bindingQueue2(){
        return BindingBuilder.bind(fanoutQueue2()).to(fanoutExchange());
    }
}
//消息生产者
@SpringBootTest
public class test {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Test
    public void sendFanout(){
        String exchange = "itcast.fanout";
        String message = "你好,...";
        rabbitTemplate.convertAndSend(exchange,"",message);
    }
}
//消息消费者
@RabbitListener(queues = "fanout.queue1")
public void listenerFanout1(String msg){
    System.out.println("我来消费了1:"+msg);
}

@RabbitListener(queues = "fanout.queue2")
public void listenerFanout2(String msg){
    System.out.println("我来消费了2:"+msg);
}

RabbitMQ发布订阅(路由)

  • 每一个Queue都与Exchange设置一个BindingKey
  • 发布者发送消息时指定消息的RoutingKey
  • Exchange将消息路由到BindingKey与消息RoutingKey一致的队列
//消费者代码
@Component
public class ConsumerMessage {
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "direct.queue1"),
            exchange = @Exchange(name = "itcast.direct",type = ExchangeTypes.DIRECT),
            key = {"red","blue"}
    ))
    public void listenerRout1(String msg) {
        System.out.println("我来消费了1:"+msg+":"+ LocalTime.now());
//        Thread.sleep(20);
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "direct.queue2"),
            exchange = @Exchange(name = "itcast.direct",type = ExchangeTypes.DIRECT),
            key = {"yellow","green"}
    ))
    public void listenerRout2(String msg) {
        System.out.println("我来消费了2:"+msg+":"+ LocalTime.now());
//        Thread.sleep(20);
    }
}
//生产者
@SpringBootTest
public class test {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Test
    public void sendFanout(){
        String exchange = "itcast.direct";
        String message = "你好,...";
        rabbitTemplate.convertAndSend(exchange,"green",message);
    }
}

RabbitMQ发布订阅(主题)

//消费者代码
@Component
public class ConsumerMessage {
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "topic.queue1"),
            exchange = @Exchange(name = "itcast.topic",type = ExchangeTypes.TOPIC),
            key = "china.#"
    ))
    public void listenerRout1(String msg) {
        System.out.println("我来消费了1:"+msg+":"+ LocalTime.now());
//        Thread.sleep(20);
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "topic.queue2"),
            exchange = @Exchange(name = "itcast.topic",type = ExchangeTypes.TOPIC),
            key = "#.news"
    ))
    public void listenerRout2(String msg) {
        System.out.println("我来消费了2:"+msg+":"+ LocalTime.now());
//        Thread.sleep(20);
    }
}
//生产者
@SpringBootTest
public class test {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Test
    public void sendFanout(){
        String exchange = "itcast.topic";
        String message = "你好,...";
        rabbitTemplate.convertAndSend(exchange,"aa.news",message);
    }
}

消息转换器

  • 利用MessageConverter实现,默认是JDK的序列化
  • 生产者和消费者必须使用相同的MessageConverter
<!-- 生产者和消费者导入相同的依赖 -->
<dependencies>
    <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-xml</artifactId>
    </dependency>
</dependencies>
// 生产者消费者创建相同的bean
@Bean
public MessageConverter messageConverter(){
    return new Jackson2JsonMessageConverter();
}
// 消费者接收参数类型与生产者保持一致
@RabbitListener(queues = "object.queue")
public void listenerRout2(Map<String,Object> map) {
    System.out.println("我来消费了2:"+map+":"+ LocalTime.now());
}