服务异步通讯 Rabbit-MQ

目录

1.初始MQ

1.1同步通讯

​编辑

1.2异步通讯

1.3MQ常见框架

2.RabbitMQ快速入门

2.1RabbitMQ安装和介绍

​编辑

2.2常见的消息模型

3.SpringAMQP

3.1 Basic Queue 简单队列模型

3.2 Work Queue 工作队列模型

4.发布订阅模式

4.1 发布,订阅模型-Fanout

​编辑4.2 发布,订阅模型-Direct

​编辑4.3 发布,订阅模型-Topic

4.4 消息转换器


1.初始MQ

1.1同步通讯

简单来说:

同步通讯:就是打电话,直接互通,一方做出反应另一方立刻会知道并回应。

同步通讯的缺点:电话同一时间只能同一人(别的进程无法进行)

异步通讯:是发微信,可以当时回也可以晚点或者不回,不具有时效性。但是可以多线操作。

 

1.耦合度高。每次加入新的需求,都要修改原来的代码。

2.性能下降。调用者需要等待服务提供者响应,如果调用链过长则响应时间等于每次调用时间之和

3.浪费资源。调用链中的每个服务在等待相应过程中,不能释放请求的资源,高并发场景下会极度浪费系统资源。

4.级联失败。如果服务提供者出现问题,所有的调用者都会出现问题,如多米诺骨牌一样,迅速导致整个微服务群故障。

1.2异步通讯

优势一,

        异步调用常见实现就是事件驱动模式。(白话就是中间找个代理 ,broker。服务者订阅borker说大哥有事得喊我,叫做订阅事件。当有订单过来会同时通知所有得订阅者。所以支付服务跟broker说完就没事了回头可以跟用户说大哥我干完了,不需要等待后面的调用者回应 这就做到了解耦)。有新的事件出现后只需要订阅borker即可,取消得业务取消订阅即可。

优势二,

本来需要相加得时间50+10+150*3

通过异步通讯后50+10即可 提升性能,吞吐量提高。

          优势三,

        中间都通过borker代理了 ,自然也就没有依赖了。随意也不会担心级联失败。

 区别于同步调用的有点:

流量削峰:

        对于大型的高并发的如秒杀这样的场景下可以控制。压力给到broker(缓存)按自己能力处理。

缺点:

        针对于以上的四个有点可以看出。对于borker的依赖相当之高。并且需要borker极强的功能。调用链相对比没那么清晰,定位问题难以排查。

1.3MQ常见框架

        MQ(MessageQueue),中文是消息队列,字面来看就是存放消息的队列。也就是事件驱动构架中的Broker。

 对于吞吐量极度依赖的公司会使用kafka ,大部分中小型公司都是用RabbitMQ。

2.RabbitMQ快速入门

2.1RabbitMQ安装和介绍

        通过docker安装即可,这里不做赘述。

运行的命令,

 docker run \
    -e RABBITMQ_DEFAULT_USER=123 \   #账号
    -e RABBITMQ_DEFAULT_PASS=123 \   #密码
    --name mq -p 15672:15672 \       #MQ管理平台的端口
    -p 5672:5672 \                   #做消息通讯的端口
    -d rabbitmq:3-management         #后台运行和名称

        发行者(publisher) 发送消息虚拟主机(VirtualHost)虚拟主机内(交换机exchange路由消息到队列queue,队列暂存消息)消费者(consumer)从队列获取消息而后处理消息。

        每个用户都有自己的VirtualHost,每个VirtualHost(虚拟主机)都是相互隔离。

2.2常见的消息模型

        

红色代表queue(消息) 紫色代表exchange(交换机)

支付———broker————订单(等同于上面); 

3.SpringAMQP

 

 最主要的就是 p标准或者是规范(Protocol)。

listener监听(接收)

发送的 RabbitTenmplate (rabbit模板)

RabbitAdmin 用于自动声明队列,交换和绑定

3.1 Basic Queue 简单队列模型

发送

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

 编写publisher

spring:
  rabbitmq:
    host: 192.168.130.143
    port: 5672
    virtual-host: /
    username: 123
    password: 123
@SpringBootTest
@RunWith(SpringRunner.class)
@EnableRabbit
public class SpringAmqpTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testSendMessage2SimpleQueue(){
        String queueName = "simple.queue";
        String message = "hello spring amqp!";
        rabbitTemplate.convertAndSend(queueName,message);
    }
}

 

接收 

3.2 Work Queue 工作队列模型

工作队列可以看作是经典队列的升级,队列是有一定容量的,当一个consumer能力不够时可以在创建一个consumer分担压力。

publisher: 

 @Test
    public void testSendMessage2WorkQueue() throws InterruptedException {
        String queueName = "simple.queue";
        String message = "hello work amqp!";
        for(int i = 1;i<=50;i++){
            rabbitTemplate.convertAndSend(queueName,message+i);
            Thread.sleep(20);
        }

    }

consumer *2:

@RabbitListener(queues = "simple.queue")
    public void listenWorkQueue1(String msg) throws InterruptedException {
        System.out.println("消费者接收到的simple.queque的消息:"+msg+ LocalTime.now());
        Thread.sleep(20);
    }

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

 当前模式下属于消息预取模式 如50条消息两个consumer平均预取25条,但是consumer2的处理能力慢所以1执行完后要等2处理完自己的,所以在yml中添加以下条件。

 设置预取1当一个consumer处理完一个会告诉queue,然后queue在分配一个。

设置2就是两个。也就是consumer会容纳两个然后回复完,在分配。

可以达到能者多劳。

4.发布订阅模式

在之前的队列模式中(simple,或work队列)一个queue只会给一个consumer。

不能满足多消费者同时处理一个消息(如 支付发同时发个订单,仓促,短信等)。

增加了exchenge(交换功能):只能消息路由不负责存储。 

4.1 发布,订阅模型-Fanout

@Configuration
public class FanoutConfig {
    //声明交换机
    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange("itbug.fanout");
    }
    //声明队列
    @Bean
    public Queue fanoutQueue1(){
        return new Queue("fanout.queue1");
    }
    //绑定交换机和队列
    @Bean
    public Binding fanoutBinding1(Queue fanoutQueue1,FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
    }

笨方法,首先声明配置类@Configuration搭载@Bean

通过交换机和声明队列 binding exchange和queue。

消费者:

 @RabbitListener(queues = "fanout.queue1")
    public void listenFanoutQueue1(String msg){
        System.out.println("消费者1接收到的simple.queque的消息:"+msg);
    }

    @RabbitListener(queues = "fanout.queue2")
    public void listenFanoutQueue2(String msg){
        System.out.println("消费者2接收到的simple.queque的消息:"+msg);
    }
  @Test
    public void testSendMemmageFanoutQueue(){
        String exchangeName = "itbug.fanout";
        String message  = "hello fanout exchange";
        rabbitTemplate.convertAndSend(exchangeName,"",message);
    }

“”引号之间留给Direct(路由模型)


4.2 发布,订阅模型-Direct

路由模型相对于广播模型(fanout)功能更宽泛。

Direct相对于Fanout模式发送的消息更多些。在queue中提供相对的“暗号”,匹配后可对接。

 @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "direct.queue1"),
            exchange = @Exchange(name = "itbug.direct",type = ExchangeTypes.DIRECT),
            key = {"red","blue"}
    ))
    public void listenDirectQueue1(String msg){
        System.out.println("消费者1接收到的simple.queque的消息:"+msg);
    }

通过@RabbitListener 绑定队列 交换机和暗号(绑定key)

    @Test
    public void testSendMessageDirectQueue(){
        String exchangeName = "itbug.direct";
        String message = "hello direct exchange";
        rabbitTemplate.convertAndSend(exchangeName,"blue",message);
    }

通过这里绑定key “blue” 则可以选择通过key把消息发送给谁。

也可以通过共有的key ”red“ 把消息同时发给共有 “red” 的consumer。


4.3 发布,订阅模型-Topic

 在路由模式上功能更丰富一些。通过 . 的形式写入过个单词.#/*的模式,收到某一字段的信息。

listener:

    /**
     * Topic1
     * @param msg
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "topic.queue1"),
            exchange = @Exchange(name = "itbug.topic",type = ExchangeTypes.TOPIC),
            key = "#.news"
    ))
    public void listenTopicQueue1(String msg){
        System.out.println("消费者1接收到的simple.queque的消息:"+msg);
    }

    /**
     * Topic2
     * @param msg
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "topic,queue2"),
            exchange = @Exchange(name = "itbug.topic",type = ExchangeTypes.TOPIC),
            key = "china.#"
    ))

 发送:

    @Test
    public void testSendMessagetopicQueue(){
        String exchangeName = "itbug.topic";
        String message = "2023年2月10日12点04分";
        rabbitTemplate.convertAndSend(exchangeName,"china.news",message);
    }

这样的 key(chian。news)  . china.# 和 #.key  都可以收到。

4.4 消息转换器

也就是 通过 rabbitTemplate 来发送数据 但是都是通过byte字节的形式。

 通过 jackson转换的形式json 

 

 

 原理则是用 MessageConverter 来覆盖默认的jdk的序列化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值