zoukankan      html  css  js  c++  java
  • 20.Spring-Boot中RabbitMQ的使用之概念原理理解(重要)

    RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。 AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。AMQP从一开始就设计成为开放标准,以解决众多的消息队列需求和拓扑结构问题。

    Rabbit,兔子的意思,毕竟兔子的行动非常迅速的动物而且繁殖起来也是非常疯狂的,把它用于分布式软件命名在合适不过了。

    在今天,RabbitMQ并不是开放消息通信的唯一选择。像ActiveMQ,ZeroMQ和Apache Qpid都提供了不同的开源消息队列方案。为什么要选择RabbitMQ:
       除了Qpid之外,RabbitMQ是唯一实现了AMQP标准的代理服务器。

       正式由于Erlang,RabbitMQ集群不可意思的简单。

      每个人的经历不同,但我们发现RabbitMQ比竞争对手更可靠,更能放置崩溃。

    大致流程:

                                                  从生产者到消费者的消息流

      (1)对于生产者

    生产者(producer)创建消息,然后发布给(发送)到代理服务器(RabbitMQ)。什么是消息呢?消息包含两个部分:有效负载(payload)和标签(label)。有效负载就是想要传输的数据。它可以是任何内容,JSON数据XML数据或者TXT数据。RabbitMQ不会在意这些。标签就更有趣,它描述了有效负载,并且RabbitMQ用它来决定谁将获取消息的拷贝。不同于TCP协议得是,当你明确指定发送方和接收方时,AMQP只是用标签表述

    这条消息,然后会把消息交给Rabbit,Rabbit会根据标签把消息发送给感兴趣的接收方。这种通信方式是一种“发后即忘”的单向方式。

    (2)对于消费者

    对于消费者,它连接到代理服务器上,订阅到队列(queue)上,把消息队列想象成一个具有名字的邮箱。每当消息到达特定的邮箱时,RabbitMQ会将其发送给其中一个订阅的/监听的消费者。当消费者接收到消息时,它只得到消息的一部分:有效负载。在消息的路由过程中,消息的标签并没有随有效负载一同传递。RabbitMQ甚至不会告诉你谁是生产者/发送消息。就好比你拿起信件,却发现所有的信封都是空白的。想要知道这条消息是否是你某个朋友zhangsan发来的唯一方式就是在信里签了名。同时,如果需要明白知道谁生产的AMQP消息的话,就要看生产者是否把消息的发送方放入有效负载中。

    (3)总结:

    整个过程很简单:生产者创建消息,消费者收到消息。你的应用程序可以使生产者,也可以是消费者,向其他应用程序发送消息。或者作为一个消费者,接收消息。不过在此之前它必须建立一条信道(channel)。

    信道

    首先要连接到Rabbit,才能消费或者发布消息。你在应用程序和Rabbit代理服务器之间创建一条TCP连接。
    一旦TCP连接打开,应用程序就可以创建一条AMQP信道。信道是建立在”真实的“TCP连接内的虚拟连接。

    AMQP命令都是通过信道发送出去的。每条信道都会被指派一个唯一ID(AMQP库被帮你记住)。不论是发布消息、订阅队列或者接受消息。这些动作都是通过信道完成的。

    为什么需要信道?为什么不直接通过TCP连接发送AMQP命令。重要原因在于对操作系统来说建立和销毁TCP会话是非常昂贵的开销。

    假设你只进行TCP连接,那么每个线程都需要自行连接到Rabbit。也就是说高峰期有每秒成百上千条连接。这不仅造成TCP连接的巨大浪费,而且操作系统每秒也就只能建立很少数量的连接。

    一条TCP连接上创建多条信道是没有限制。把它想象成一束光纤就可以了。每条电缆中的光纤都可以传输(就像一条信道)。一条电缆中有许多光纤束,允许所有连接的线程通过多条光纤同时传输和接收。TCP连接就像电缆,而AMQP信道就像一条条独立光纤束。

                                       

    队列

    从概念上来讲:AMQP消息路由必须有三部分:交换器,队列和绑定。生产者把消息发布到交换器上;消息最终到达队列,并被消费者接收;绑定决定了消息如何从路由器路由到特定的队列。

                                        

                                                      AMQP栈:交换器,绑定,队列

    队列就是如同具有名字的邮箱。消息达到队列中等待消费者。消费者通过以下两种方式从特定的队列中接收消息:
    (1)通过订阅模式。这样做会将信道置为接收模式,直到取消队列的订阅为止。订阅了消息后,消费者在消费(或者拒绝)最近接收的那条消息后,就能从队列中自动接收下一条消息。
    (2)也可从队列中获取单条消息不持续订阅。

    如果至少有一个消费者订阅了队列的话,消息就会立即发送给这些订阅的消费者。但是如果消息到达了无人订阅队列呢?在这种情况下,消息会在队列中等待。一旦有消费者订阅到该队列,那么队列上的消息就会发送给消费者。

    当有多个消费者订阅了同一个队列时,消息时如何分发的。
    当Rabbit队列拥有多个消费者时,队列接收到消息将以循环的方式发送给消费者。每个消息只会发送给一个订阅的消费者。假设有 send_queue队列,消费者zhangsan和lisi订阅到send_queue队列。当消息达到send_queue队列时,消息投递方式如下:
     【1】消息Message_A到达队列send_queue。
     【2】RabbitMQ 把消息Message_A发送给zhangsan
     【3】zhangsan确认收到消息Message_A
     【4】RabbitMQ把消息Message_A中send_queue删除
     【5】消息Message_B到达队列send_queue。
     【6】RabbitMQ 把消息Message_B发送给lisi
     【7】lisi确认收到消息Message_B
     【8】RabbitMQ把消息Message_B中send_queue删除

     从上面过程可知,消费者对消息进行了确认。消费者接收到每一条消息都必须进行确认。如果消费者收到一条消息,然后确认之前 从Rabbit断开连接(或者从队列上取消订阅),RabbitMQ会认为消息没有分发,然后重新分发给下一个订阅者。如果你的应用程序崩溃, 这样做可以确保消息会被发送给另外一个消费者进行处理。

    总结

    队列是AMQP消息的通信的基础模块

    • 为消息提供处所,消息在此等待消费。
    • 对于负载均衡来说,队里是绝佳方案。只需附加一堆消费者,并让RabbitMQ以循环的方式均匀的分配发来的消息。
    • 队列时Rabbit中消息的最后的终点。

    交换器和绑定

          前面已经介绍了消费者如何从队列中获取消息。现在的问题是,消息如何到达队列。那就需要AMQP的交换器和绑定了。当你想将消息投递到队列时,你通过把消息发送给交换器来完成。然后,根据确定的规则,RabbitMQ将会决定消息该投递到那个队列。这些规则就被称为路由键(routing key).队列通过路由建绑定到交换器。当你把消息发送给代理服务器时,消息将拥有一个路由键,即便是空RabbitMQ也会将其绑定使用的路由键进行匹配。如果匹配的话,那么消息将会投递到该队列。

          服务器会根据路由键将消息从交换器路由到队列,但是它是如何处理投递到多个队列的情况的。协议中定义的不同类型交换器发挥了作用。一共有四种:direct,fanout,topic,headers.

    • direct交换器

         如果路由键匹配的话,消息就被投递到对应的队列。即”先匹配, 再投送”. 即在绑定时设定一个 routing_key, 消息的routing_key 匹配时, 才会被交换器投送到绑定的队列中去.

    direct交换器消息流

    服务器必须实现direct类型交换器,包含一个空白字符串名称的默认交换器。当声明一个队列时,他会自动化绑定到默认的交换器,并以队列名作为路由键。(这也是默认的交换器)

    • fanout交换器

          这种类型交换器将收到的消息广播到绑定的队列上。消息通信模式很简单:当你发送一条消息到fanout交换器,它会把消息投递给所有附加在此交换上的队列。举例来说:一个web应用可能需要在用户上传新图片时,用户相册必须清除缓存,同时用户应该得到相应的积分奖励。可以将两个队列绑定到图片上传服务器上。一个用于清除缓存,一个用于增加积分。从这种场景你可以了解到,使用交换器、绑定和队列比直接指定队列发送消息要有优势。

    fanout交换器消息流

    • topic交换器

          topic交换器转发消息主要是根据通配符。 在这种交换器下,队列和交换器的绑定会定义一种路由模式,那么,通配符就要在这种路由模式和路由键之间匹配后交换机才能转发消息。
           在这种交换机模式下:
    路由键必须是一串字符,用句号(.) 隔开,比如说 agreements.us,或者 agreements.eu.stockholm 等。

    路由模式必须包含一个 星号(*),主要用于匹配路由键指定位置的一个单词,比如说,一个路由模式是这样子:agreements..b.*,那么就只能匹配路由键是这样子的:第一个单词是 agreements,第四个单词是 b。

     井号(#)就表示相当于一个或者多个单词,例如一个匹配模式是agreements.eu.berlin.#,那么,以agreements.eu.berlin开头的路由键都是可以的。

    具体代码发送的时候还是一样,第一个参数表示交换机,第二个参数表示routing key,第三个参数即消息。如下:
    rabbitTemplate.convertAndSend("testTopicExchange","key1.a.c.key2", " this is  RabbitMQ!");

    topic 和 direct 类似, 只是匹配上支持了”模式”, 在”点分”的 routing_key 形式中, 可以使用两个通配符:

    *表示一个词.

    #表示零个或多个词.

    topice交换器消息流

    • headers交换器

          headers 也是根据规则匹配, 相较于 direct 和 topic 固定地使用 routing_key , headers 则是一个自定义匹配规则的类型. 在队列与交换器绑定时, 会设定一组键值对规则, 消息中也包括一组键值对( headers 属性), 当这些键值对有一对, 或全部匹配时, 消息被投送到对应队列.

     微信公众号

     

     

  • 相关阅读:
    【强烈推荐】 超漂亮的仿腾讯弹出层效果(兼容主流浏览器<转>;
    必须掌握的八个【cmd 命令行】
    grep命令的用法
    sed 命令的用法1
    Paste命令的用法
    uniq命令的用法
    join命令的用法
    Sort命令的用法
    cut命令的用法
    tr命令学习
  • 原文地址:https://www.cnblogs.com/niugang0920/p/12196531.html
Copyright © 2011-2022 走看看