zoukankan      html  css  js  c++  java
  • RabbitMQ应用场景及原理

    一、简介

    RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queuing Protocol)的开源实现。

    AMQP :高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。 AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。 RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

    二、应用场景

    1、异步处理

    场景:发送手机验证码,邮件。 此场景可以分为两种方式实现。

    (1)串行

    这个流程,全部在主线程完成,注册->入库->发送邮件->发送短信,由于都在主线程,所以要等待每一步完成才能继续执行。由于每一步的操作时间响应时间不固定,所以主线程的请求耗时可能会非常长,如果请求过多,会导致IIS站点巨慢,排队请求,甚至宕机,严重影响用户体验。

    (2)并行

    这个做法是主线程只做耗时非常短的入库操作,发送邮件和发送短信,会开启2个异步线程,扔进去并行执行,主线程不管,继续执行后续的操作,这种处理方式要远远好过第一种处理方式,极大的增强了请求响应速度,用户体验良好。缺点是,由于异步线程里的操作都是很耗时间的操作,一个请求要开启2个线程,而一台标准配置的ECS服务器支撑的并发线程数大概在800左右,假设一个线程在10秒做完,这个单个服务器最多能支持400个请求的并发,后面的就要排队。出现这种情况,就要考虑增加服务器做负载,尴尬的增加成本。

    (3)RabbitMQ的处理方式

    这个流程是,主线程依旧处理耗时低的入库操作,然后把需要处理的消息写进消息队列中,这个写入耗时可以忽略不计,非常快,然后,独立的发邮件子系统,和独立的发短信子系统,同时订阅消息队列,进行单独处理。处理好之后,向队列发送ACK确认,消息队列整条数据删除。这个流程也是现在各大公司都在用的方式,以SOA服务化各个系统,把耗时操作,单独交给独立的业务系统,通过消息队列作为中间件,达到应用解耦的目的,并且消耗的资源很低,单台服务器能承受更大的并发请求。

    2、应用解耦

    场景:双11是购物狂节,假设中间的流程为下单=>减库存=>发货。

    (1)第一种方式,通过连续操作表,在单一系统中,通过主线程,连续操作。这种做法,只要一个环节出问题,请求直接报错,导致用户懵逼,假设在执行到减库存操作报错了,整个流程没有用事务回滚的话,还会造成数据不一致。

    (2)第二种方式,把这三个业务,拆成三个独立系统,通过JSON方式相互调用请求。这个做法,其实已经很不错了,起码独立出来,各自做各自的事情,一定程度上减小了整个系统的耦合性。但是问题是,就算是通过API形式请求,发送请求的系统一般情况下会等待被请求方的响应,如果响应错了,整个程序还是会终止,前面的业务系统假如已经做了入库操作,就必须要回滚了。很麻烦。如果说不等待被请求方响应的话,如果出错,如果还要保证数据一致性,就要做更多的操作,去补全数据,比如,下单成功,减库存失败,发货成功,这样当减库存系统修复后,就要通过订单数据,去补库存表的对应数据。相对麻烦,难处理。

    (3)第三种方式,把消息队列作为中间件,当订单系统下完单后,把数据消息写入消息队列中,库存系统和发货系统同时订阅这个消息队列,思想上和纯API系统调用类似,但是,消息队列RabbitMq本身的强大功能,会帮我们做大量的出错善后处理,还是,假设下单成功,库存失败,发货成功,当我们修复库存的时候,不需要任何管数据的不一致性,因为库存队列未被处理的消息,会直接发送到库存系统,库存系统会进行处理。实现了应用的大幅度解耦。

    3、流量削峰

    场景:秒杀活动,团购,一般会因为流量过大,导致应用挂掉,为了解决这个问题,一般在应用前端加入消息队列。 


     

     这个主要原理就是,控制队列长度,当请求来了,往队列里写入,超过队列的长度,就返回失败,给用户报一个可爱的错误页的等等。

     可以控制活动人数,超过此一定阀值的订单直接丢弃。可以缓解短时间的高流量压垮应用。

    4、日志处理

    这个场景应该都很熟悉,一个系统有大量的业务需要各种日志来保证后续的分析工作,而且实时性要求不高,用队列处理再好不过了。

    5、消息通讯

    现在上线的各大社交通讯项目中,实际上是没有用消息队列做即时通讯的。

    这个是点对点通信,消费者A和B同时订阅消息队列,又同时是制造者,就能实现点对点通信。

    群聊的做法,所有客户端同时订阅队列,又同时是发送,制造者。

    PS:

    上述大致的5种RabbitMq的应用场景,下面来介绍几个消息队列的区别:

    ActiveMq:这个应用于JAVA中间件较多。

    ZeroMq:这个是分发效率最高的队列,是其他队列的十倍以上,缺点是不能数据持久化。

    kafka:这是一种高吞吐量的发布订阅消息系统,当每秒达到10W+的分发要求时,可以用这个尝试,新浪微博就是用这个做分发。

    三、RabbitMQ特性

     - 可靠性(Reliability)
    
    RabbitMQ 使用一些机制来保证可靠性,如持久化、传输确认、发布确认。
    
     - 灵活的路由(Flexible Routing)
    
    在消息进入队列之前,通过 Exchange 来路由消息的。对于典型的路由功能,RabbitMQ 已经提供了一些内置的 Exchange 来实现。针对更复杂的路由功能,可以将多个 Exchange 绑定在一起,也通过插件机制实现自己的 Exchange 。
    
     - 消息集群(Clustering)
    
    多个 RabbitMQ 服务器可以组成一个集群,形成一个逻辑 Broker 。
    
     - 高可用(Highly Available Queues)
    
    队列可以在集群中的机器上进行镜像,使得在部分节点出问题的情况下队列仍然可用。
    
     - 多种协议(Multi-protocol)
    
    RabbitMQ 支持多种消息队列协议,比如 STOMP、MQTT 等等。
    
     - 多语言客户端(Many Clients)
    
    RabbitMQ 几乎支持所有常用语言,比如 Java、.NET、Ruby 等等。
    
     - 管理界面(Management UI)
    
    RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker 的许多方面。
    
     - 跟踪机制(Tracing)
    
    如果消息异常,RabbitMQ 提供了消息跟踪机制,使用者可以找出发生了什么。
    
     - 插件机制(Plugin System)
    
    RabbitMQ 提供了许多插件,来从多方面进行扩展,也可以编写自己的插件。

    四、基本概念

     - Message
    
    消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等。
    
     - Publisher
    
    消息的生产者,也是一个向交换器发布消息的客户端应用程序。
    
     - Exchange
    
    交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
    
     - Routing Key
    
    路由关键字,exchange根据这个关键字进行消息投递。
    
     - Binding
    
    绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。
    
     - Queue
    
    消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。
    
     - Connection
    
    网络连接,比如一个TCP连接。
    
     - Channel
    
    信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内地虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。
    
     - Consumer
    
    消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。
    
     - Virtual Host
    
    虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 / 。
    
     - Broker
    
    表示消息队列服务器实体。它提供一种传输服务,它的角色就是维护一条从生产者到消费者的路线,保证数据能按照指定的方式进行传输。

    五、Exchange类型

    Exchange分发消息时根据类型的不同分发策略有区别,目前共四种类型:direct、fanout、topic、headers 。headers 匹配 AMQP 消息的 header 而不是路由键,此外 headers 交换器和 direct 交换器完全一致,但性能差很多,目前几乎用不到了,所以直接看另外三种类型:

    • direct策略

    消息中的路由键(routing key)如果和 Binding 中的 binding key 一致, 交换器就将消息发到对应的队列中,简单来讲,就是路由键与队列名完全匹配。

    • 如果一个队列绑定到交换机要求路由键为“dog”
    • 只转发 routing key 标记为“dog”的消息,
    • 不会转发“dog.puppy”,也不会转发“dog.guard”等等
    • 它是完全匹配、单播的模式

    如图所示:

    Exchange和两个队列绑定在一起:

    • Q1的bindingkey是orange。
    • Q2的binding key是black和green。
    • 当Producer publish key是orange时, exchange会把它放到Q1上, 如果是black或green就会到Q2上, 其余的Message被丢弃。
    • fanout策略

     

     

     从上图也可以看出,这种策略,将忽略所谓的routing key,将消息分发到所有绑定的Queue上,更加类似我们理解的广播模式。

    • topic策略

    topic 交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上

    可以理解为直接策略的进阶版,直接策略是完全精确匹配,而topic则支持正则匹配,满足某类指定规则的(如以xxx开头的路由键),可以键消息分发过去

    • # 匹配0个或多个单词
    • * 匹配不多不少一个单词

    如图所示:

    Producer发送消息时需要设置routing_key,

    • Q1 的binding key 是”.orange.“
    • Q2 是 “..rabbit” 和 “lazy.#”:
    • 产生一个 test.orange.mm 消息,则会路由到Q1;而如果是 test.orange则无法路由到Q1,因为Q1的规则是三个单词,中间一个为orange,不满足这个规则的都无效。
    • 产生一个 test.qq.rabbit 或者 lazy.qq 都可以分发到Q2;即路由key为三个单词,最后一个为rabbit或者不限制单词个数,主要第一个是lazy的消息,都可以分发过来。
    • 如果产生的是一个 test.orange.rabbit消息,则Q1和Q2都可以满足。

    Exchange小结

    主要使用的消息分发策略有三个,直接,路由和扇形,简单的小结下应用场景和区别。

    a. Direct Exchange

    直接完全匹配模式,适用于精准的消息分发。

    b. Topic Exchange

    Routing Key的匹配模式,支持Routing Key的模糊匹配方式,更适用于多类消息的聚合。

    c. Fanout Exchange

    忽略Routing Key, 将消息分配给所有的Queue,广播模式,适用于消息的复用场景。

    六、ACK

    消息队列的一个重要指标,当有消费者获取了消息之后,对这个消息我应该怎么办?是直接删除还是等某个合适的机会再删除?又或者是干脆不删除,就留着了?

    在实际的应用场景中,消息正常消费之后,我们希望的是这个消息就不要了,但是消费的过程中如果出现了bug,则希望不要删除消息,等我修复这个bug后,可以把这个消息重新的投递给我。

     ack机制

    ACK机制是消费者从RabbitMQ收到消息并处理完成后,反馈给RabbitMQ,RabbitMQ收到反馈后才将此消息从队列中删除。

    如果一个消费者在处理消息出现了网络不稳定、服务器异常等现象,那么就不会有ACK反馈,RabbitMQ会认为这个消息没有正常消费,会将消息重新放入队列中。
    如果在集群的情况下,RabbitMQ会立即将这个消息推送给这个在线的其他消费者。这种机制保证了在消费者服务端故障的时候,不丢失任何消息和任务。
    消息永远不会从RabbitMQ中删除,只有当消费者正确发送ACK反馈,RabbitMQ确认收到后,消息才会从RabbitMQ服务器的数据中删除。
    消息的ACK确认机制默认是打开的。

    如果忘记了ACK,那么后果很严重。当Consumer退出时候,Message会一直重新分发。然后RabbitMQ会占用越来越多的内容,由于RabbitMQ会长时间运行,因此这个"内存泄漏"是致命的。

    七、RabbitMQ 选型和对比

    1.从社区活跃度

    按照目前网络上的资料,RabbitMQ 、activeM 、ZeroMQ 三者中,综合来看,RabbitMQ 是首选。 

    2.持久化消息比较

    ZeroMq 不支持,ActiveMq 和RabbitMq 都支持。持久化消息主要是指我们机器在不可抗力因素等情况下挂掉了,消息不会丢失的机制。

    3.综合技术实现

    可靠性、灵活的路由、集群、事务、高可用的队列、消息排序、问题追踪、可视化管理工具、插件系统等等。

    RabbitMq / Kafka 最好,ActiveMq 次之,ZeroMq 最差。当然ZeroMq 也可以做到,不过自己必须手动写代码实现,代码量不小。尤其是可靠性中的:持久性、投递确认、发布者证实和高可用性。

    4.高并发

    毋庸置疑,RabbitMQ 最高,原因是它的实现语言是天生具备高并发高可用的erlang 语言。

    5.比较关注的比较, RabbitMQ 和 Kafka

    RabbitMq 比Kafka 成熟,在可用性上,稳定性上,可靠性上,  RabbitMq  胜于  Kafka  (理论上)。

    另外,Kafka 的定位主要在日志等方面, 因为Kafka 设计的初衷就是处理日志的,可以看做是一个日志(消息)系统一个重要组件,针对性很强,所以 如果业务方面还是建议选择 RabbitMq 。

    还有就是,Kafka 的性能(吞吐量、TPS )比RabbitMq 要高出来很多。

    选型最后总结:

    如果我们系统中已经有选择  Kafka  ,或者   RabbitMq  ,并且完全可以满足现在的业务,建议就不用重复去增加和造轮子。

    可以在  Kafka  和   RabbitMq  中选择一个适合自己团队和业务的,这个才是最重要的。但是毋庸置疑现阶段,综合考虑没有第三选择。

    八、参考资源

     

  • 相关阅读:
    java中的几种对象(PO,VO,DAO,BO,POJO)
    【转】Spring boot 打成jar包问题总结
    mac 上安装lua
    Mac下更新Vim到最新版本
    刘以鬯和香港文学
    权重随机算法的java实现
    MySQL具体解释(7)-----------MySQL线程池总结(一)
    IIS PHP 配置 问题总结
    HDU 3622 Bomb Game(2-sat)
    poj 2388 Who's in the Middle
  • 原文地址:https://www.cnblogs.com/wmy666/p/12488028.html
Copyright © 2011-2022 走看看