关于

消息队列(MQ),是一种应用程序对应用程序的通信方法。应用程序通过写和检索出入列队的针对应用程序的数据(消息)来通信,大多应用中 可通过消息服务中间件来提升系统异步通信、扩展解耦能力
消息服务中两个重要概念:
1.消息代理(message broker)和目的地(destination)
2.当消息发送者发送消息以后,将由消息代理接管,消息代理保证消息传递到指定目的地
消息传递不是直接远程过程调用,而是通过消息队列

一、消息传递和常用协议
    1.1 JMS
    JMS(Java MessageService)是指JMS API 是最成功的异步消息传递技术之一,基于JVM消息的规范 为java应用提供统一的消息操作,包括create、send、receive
    JMS和JDBC担任差不多的角色,用户都是根据相应的接口可以和实现了JMS的服务进行通信,进行相关的操作
    JMS包含provider(实现了JMS接口的消息中间件)、 producer/publisher、consumer/subscriber、message、queue、topic等
    JMSconsumer同时支持message selector(消息选择器)通过消息选择器,consumer可以只消费那些通过了selector筛选的消息
    消息路由机制: producer -> queue(p2p)/ topic(pub/sub) -> consumer
    提供了两种消息模型,peer-2-peer(点对点)、publish-subscribe(发布订阅)模型。
    点对点模型时,消息将发送到一个队列,该队列的消息只能被一个消费者消费。
    发布订阅模型时,消息可以被多个消费者消费。发布订阅模型中,生产者和消费者完全独立,不需要感知对方的存在
    主要特性:
    面向Java平台的标准消息传递API
    queues和topics两种消息传递模型
    支持事务
    能够定义消息格式(消息头、属性和内容)
    部分都实现了JMS API,可以担任JMS provider的角色,如ActiveMQ,Redis、HornetMQ等

    1.2 AMQP
    AMQP(advanced message queuing protocol)高级消息队列协议,也是一个消息代理的规范,兼容JMS
    AMQP是一种协议是一种binary wire-level protocol(链接协议)。这是其和JMS的本质差别,AMQP不从API层进行限定,而是直接定义网络交换的数据格式,
    使得实现了AMQP的provider天然性就是跨平台的。可以使用Java的AMQP provider,AQMP可以用http来进行类比,
    不关心实现的语言,只要大家都按照相应的数据格式去发送报文请求,不同语言的client均可以和不同语言的server链接
    AMQP中 消息路由(messagerouting)和JMS存在一些差别,在AMQP中增加了Exchange和binding的角色。
    Producer将消息发送给Exchange binding决定Exchange的消息应该发送到那个queue,而consumer直接从queue中消费消息。queue和exchange的bind有consumer来决定
    消息路由机制: producer -> exchange -> binding -> queue -> consumer
    消息生产者和消息消费者无须知道对方的Queue,消息生产者将Exchange通过Route key和任意Queue绑定。消息消费者通过Route key从任意Queue中获取Exchange
    提供了五种消息模型:direct exchange、fanout exchange、topic change、headers exchange、system exchange(后四种和JMS的pub/sub模型没有太大差别,仅是在路由机制上做了更详细的划分)
    主要特性:
    独立于平台的底层消息传递协议
    跨语言和平台的互用性、是底层协议的
    有5种交换类型direct,fanout,topic,headers,system
    面向缓存的、可实现高性能
    支持事务(跨消息队列)、支持分布式事务(XA,X/OPEN,MS DTC)
    AMQP模型: 发布->分拣->队列->消费->确认回执MQTT
    Rabbitmq使用了AMQP实现

    1.3 MQTT
    面向基于Java的企业应用的JMS和面向所有其他应用需求的AMQP,MQTT专门为小设备设计的
    计算性能不高的设备不能适应AMQP上的复杂操作,它们需要一种简单而且可互用的方式进行通信。MQTT的基本要求,MQTT是物联网(IOT)生态系统中主要成分之一
    主要特性:
    面向流,内存占用低,为小型无声设备之间通过低带宽发送短消息而设计
    不支持长周期存储和转发、不支持事务

二、消息投递模式
      主要有:PTP模型、Pub/Sub模型、Partition模型
      PTP模型和Pub/Sub模型在JMS(Java Message Service)规范中有定义,ActiveMQ就实现了JMS规范, Rocketmq,kafka采用了Partition模型
      点对点模型:队列中的每一条消息,由一个消费者进行消费,消费之后就会从队列中移除。 ActiveMQ消息发送的时候可能是有序的,但是在消费的时候,就变成无序
      发布/订阅模型:生产者将消息发布到一个主题(Topic)中,订阅了该Topic的所有下游消费者,都可以接收到这条消息
      Partition模型:topic -> Partition段 每个Partition只会存储部分数据,Paritition会在同一个消费者组下的消费者中进行分配,每个消费者只消费分配给自己的Paritition
          PTP下在Partition模型中,只需保证创建的Topic只有一个Partition 最终也只会分配其中一个消费者
          Pub/Sub下只需要为每个消费者设置成不同的消费者组,当消费者的数量大于Partition数量时,这些多出来的消费者将无法消费到消息
      Rocketmq,支持单个partition的并行消费,在对单个消费者内,同时启动多个线程,来消费这个Partition中的数据

三、消息的推拉
    消费者有两种方式从消息中间件获取消息
    推模式push:消息中间件主动将消息推送给消费者
    拉模式pull:消费者主动从消息中间件拉取消息
    推模式将消息提前推送给消费者,消费者必须设置一个缓冲区缓存这些消息。好处很明显,消费者总是有一堆在内存中待处理的消息,所以效率高。
              缺点是可能导致数据积压在客户端
    拉模式在消费者需要时才去消息中间件拉取消息,这段网络开销会明显增加消息延迟,降低系统吞吐量。
    长轮询Pull的好处就是可以减少无效请求,保证消息的实时性,又不会造成客户端积压


四、中间件的组成
    基础组件
      Broker:消息队列服务器实体
      Producer/Publisher:消息的生产者
      Consumer:消息的消费者
      Topic:发布订阅模式下的消息主题
      Queue:PTP模式下,特定生产者向特定queue发送消息,消费者订阅特定的queue完成指定消息的接收
      Message:由消息头和消息体组成
    Rabbitmq
      Connection: 网络连接
      Exchange:交换器 用来接收消息将这些消息路由给服务器中的队列 (有4种里类型:direct(默认)、fanout、topic、headers) 不同类型的Exchange转发消息策略都有区别
      Binding :绑定 用于消息队列和交换器 之间的关联 (一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则)
      Channle:复用一条TCP连接 多路复用连接中的一条独立的双向数据流通道,不管发布消息 订阅队列 接收消息 这些动作都是通过信道完成,减少建立和销毁TCP开销

五、AMQP中的Exchange消息路由
    AMQP中消息的路由过程和Java开发者熟悉的JMS存在一些差别,AMQP增加了Exchange和Binding的角色,生产者把消息发送到Exchange上
    Exchange分发消息时根据类型的不同分发策略也有所区别,有四种类型:direct、fanout、topic、headers
    direct:
      消息中的路由键(routing key)如果和Binding中的bingding key一致,交换器就将消息发到对应的队列中, 路由键与队列名完全匹配
    fanout :
      fanout交换器不处理路由键,只是简单地将队列绑定到交换器上去, 每个发到fanout类型交换器的消息都会分到所有绑定的队列上去,
      每个发送到交换器的消息都会被转发到该交换器绑定的所有队列上
    topic:
      匹配分配消息的路由键属性, 2个通配符:符号“#”和符号“*”。#匹配0个或多个单词,*匹配一个单词

六、消息中间件的优势
    1.系统解耦:系统与系统间的消息传递没有直接调用关系, 扩展系统不用改动接口。降低工程间的依赖度
    2.异步通信:一套逻辑 同时请求N个系统协调 以完成整个业务。通过并行调用业务 提高系统响应时间
    3.过载保护:高峰值访问系统是系统业务无法正常运行,先存队列中再慢慢处理 缓冲服务压力,流量削峰

    缺点
    1.降低高可用:系统引入的外部依赖越多,越容易挂掉,本来你就是A系统调用BCD三个系统的接口就好了,现在又加入一个mq,万一mq挂掉了,整个系统也就崩溃了
    2.增加系统的复杂程度: 重复消费问题、消息丢失问题、消息传递的顺序性问题
    3.一致性问题: 一套业务流程 ABC系统中 其中一个系统失败 数据不一致问题

七、mq消息丢失
    生产者弄丢:
    1.事务方式:生产者发送消息之前,通过channel.txSelect开启一个事务,接着发送消息。如消息没有成功被 RabbitMQ 接收到,进行事务回滚channel.txRollback,然后重新发送, 反之提交事务channel.txCommit  (生产者的吞吐量和性能都会降低很多)
    2.Confirm 机制(发送回执确认): Confirm 模式是在生产者那里设置的,就是每次写消息的时候会分配一个唯一的 ID,然后 RabbitMQ 收到之后会回传一个 ACK,告诉生产者这个消息 OK 了。如果 RabbitMQ 没有处理到这个消息,那么就回调一个 Nack 的接口,这个时候生产者就可以重发
事务机制是同步的,提交一个事务之后会阻塞在那儿。
Confirm 机制是异步的,发送一个消息之后就可以发送下一个消息然后那个消息 RabbitMQ 接收了之后会异步回调你一个接口通知你这个消息接收到了
     Rabbitmq数据丢失:
     防止rabbitmq自己弄丢了数据,须开启rabbitmq的持久化,就是消息写入之后会持久化到磁盘,哪怕是rabbitmq自己挂了,恢复之后会自动读取之前存储的数据,一般数据不会丢
     罕见的是,rabbitmq还没持久化,自己就挂了,可能导致少量数据会丢失的,但是这个概率较小
     持久化 + 生产者的confirm机制配合,只有消息被持久化到磁盘之后,才会通知生产者ack了,所以哪怕是在持久化到磁盘之前,rabbitmq挂了,数据丢了,生产者收不到ack,也是可以自己重发的
(deliveryMode设置为2)
    消费者数据丢失
    消费的时候,刚消费到,还没处理,结果进程挂了比如重启了,RabbitMQ认为你都消费了,这数据就丢了
    用RabbitMQ提供的ack机制, 如果消费端一直未收到ack响应则分配给其他消费端

六、保证幂等性(重复消费)
    出现重复消费问题 如 消费端消费了未ack 就挂掉了 mq就重复发送 当前消费端启动后重复消费。
    解决办法:
    1.生产者发送消息时,每条消息加一个全局的唯一id,然后消费时,将该id保存到redis里面。消费时先去redis里面查一下有么有,没有再消费
    2.数据库操作可以设置唯一键,防止重复数据的插入
    3.如写redis那没有问题,每次都是set,天然的幂等性

七、顺序消费
    如生产者产生了2条消息:M1、M2,要保证这两条消息的顺序
    一种简单的方式就是将M1、M2发送到同一个Server上,生产者等待M1发送成功后再发送M2,根据先达到先被消费的原则,M1会先于M2被消费,这样就保证了消息的顺序
    如果发送M1耗时大于发送M2的耗时,那么M2就仍将被先消费,仍然不能保证消息的顺序,则确定M1成功再发M2,重试机制
   总之 消息被存储的时候保持和发送的顺序的一致, 消息被消费的时候保持和存储的消息顺序一致.

八、消息一致性
    以订单创建为例:生成订单、处理订单 如订单处理成功,由于网络原因或者MQ宕机,消息没有发送出去 一方状态不变
    1. 则选择支持事务消息中间件 ,用来保证了消息生成者本地事务处理成功与消息发送成功的最终一致性问题
     事务消息的预发送机制其实与二阶段提交类似,唯一不同的是 消费端如消费不成功不会触发回滚,因为消息队列会持久化数据,通过MQ的重试机制 直至消费成功
    2.本地消息
     在执行业务操作的时候,记录一条消息数据到DB,并且消息数据的记录与业务数据的记录必须在同一个事务内完成
     在记录完成后消息数据后,后面我们就可以通过一个定时任务到DB中去轮训状态为待发送的消息,然后将消息投递给MQ

十一、高可用性
     服务器宕机不能为系统提供服务 就需要做mq的集群 rabbitmq集群有2中模式:普通集权、镜像集群
     普通集群
     多台机器上启动多个 RabbitMQ实例 类似Master-Slave模式一样 创建的Queue 只会放在一个Master RabbtiMQ 实例上,其他实例都同步那个接收消息的 RabbitMQ 元数据
     如果连接到的 slave RabbitMQ 实例未存放 Queue 数据的实例,会从master RabbitMQ 数据的实例上拉取数据,返回给客户端 (实时拉取数据开销问题和宕机问题都无法消费消息)
     镜像集群
     每次写消息到 Queue 的时候,都会自动把消息到多个实例的 Queue 里进行消息同步。这样的话任何一个机器宕机了,别的实例都可以用来提供服务,这样就做到了真正的高可用

群交流(262200309)
原文地址:https://www.cnblogs.com/webster1/p/12520611.html