消息队列之RabbitMQ

消息队列之RabbitMQ:

一、准备知识:
把数据放到消息队列叫做生产者
从消息队列里取数据叫做消费者
消息队列主要实现的功能:
a. 异步:批量数据的异步处理
b. 解耦:串行任务的并行化
我的理解:将原先直接关联的系统,间接关联化,引入第三方(消息队列)作为中间系统。如下图所示:
消息队列之RabbitMQ_第1张图片
c. 销锋:高负载任务的负载均衡
消息队列有低延迟和高吞吐量的特性

二、RabbitMQ:

  1. RabbitMQ的工作模型:
    消息队列之RabbitMQ_第2张图片
    整个过程:生产者连接(通过Channel)队列服务器将消息投递到交换机上,然后路由到一个或多个队列上,消费者连接(通过Channel)消息队列服务器,然后取走消息(消息队列就会删除掉该消息)。

  2. 相关解释:
    RabbitMQ的服务器是Broker(中介),消息的发布和接受都需要连接服务器(即Boroker), 连接是TCP的长连接,消耗性能,于是引入了Channel(虚拟连接或消息通道),因此不需要再去创建和释放TCP长连接,而是在保持TCP长连接里面创建和释放Channel
    Channel 除了是消息通道,还是重要的Java编程接口
    队列:本质是一个独立运行的进程,有自己的数据库来保存数据。消息是存在队列上的,取消息也是从队列取的
    交换机:本质是地址清单,不会保存数据。为了消息的灵活投递
    如果有很多个业务系统,例如资金系统,提单系统,封装系统,如果每个系统都想有自己的RabbitMQ,那么就需要采购一个新的硬件,再安装一个新的服务器,很浪费资源。为了解决硬件资源利用率,那么就可以在一个硬件创建多个虚拟机(不同的虚拟机之间是完全隔离的)。
    注意: 发送消息都是发送到交换机上的,不会发送到队列上

  3. 交换机类型:
    直连交换机(direct exchange):使用唯一关键字绑定
    主题交换机(topic exchange):使用通配符(#、*)绑定消息队列
    #:代表至少一个单词
    *:(不多不少一个单词)
    单词:用英文的点(.)隔开的
    广播交换机(Fanout Exchange):不需要关键字

  4. Spring boot 整合RabbitMQ:
    配置类:创建交换机、创建队列、创建绑定关系
    消费者:监听类(监听队列)
    生产者:调用template发送消息(exchange),即RabbitTemplate,所有的消息都会转换成JSON发送
    服务器信息配置:约定大于配置

  5. 消息可靠性投递分析:
    a.确保消息成功发送到RabbitMQ服务器
    a1.服务端确认-事务模式:发送消息的效率低
    将channel设为事务,成功事务提交,失败事务回滚
    a2.服务端确认-确定模式
    将channel设为confirm模式,if(waitForConfirms){
    发送成功
    }
    普通确认方式(发一条确认一条,效率低)、批量确认方式(到底需要积累多少条消息,确认一下呢,如果前面99条成功,第100条不成功,这时候就就。。)、异步确认方式(编码复杂,让程序变得复杂)
    b.确保消息到达正确的路由(路由保证)
    b1. mandatory = true + ReturnListener
    b2. 指定交换机的备份交换机

  6. 确保消息在 队列的正确地存储(消息存储)
    a. 队列持久化(声明队列的时候指定的属性)
    b. 交换机持久化
    c. 消息持久化(内存-磁盘)

  7. 确保消息从队列正确的投递到消费者(消费者确认)
    默认的自动的应答autoAck():一收到消息,就自动应答,不管有没有成功
    channel.basicAck():手工应答
    channel.basicReject():单条拒绝
    channel.basicNack():批量拒绝

  8. 确保消息被正确消费:
    a. 消费者回调:
    a1 ) 发送回执[ 即定时扫描(超过时间,又没得到应答,会去重新投递)]
    a2)API(消息落库) 、
    b. 补偿机制:
    b1 ) 消息重发
    b2 ) 次数的控制
    c.消息幂等性:幂等性指的是无论操作多少次结果都一样。(服务端无法控制,只能消费者控制)
    对于每条消息,MQ内部生成一个全局唯一、与业务无关的消息ID:inner-msg-id。当MQ-server接收到消息时,先根据inner-msg-id判断消息是否重复发送,再决定是否将消息落地到DB中。这样,有了这个inner-msg-id作为去重的依据就能保证一条消息只能一次落地到DB。

  9. 思考:幂等性与并发(表单重复提交和并发增删改及校验唯一操作场景)
    先说乐观锁version 表 字段v=1
    a.并发更新操作
    先select 当前版本号1,同时更新操作update version set v=v+1 where v=1;
    第一个执行的将v更新为2,其他并发重复操作因数据库v更新为2,where 2=1更新0条记录,判断为过期无效操作
    b.删除操作可以不考虑并发,多删几次,结果都一样
    c.并发插入操作(用户重复提交):
    c1.首先在新增数据库时,没有旧值进行乐观锁的控制,需要借助redis,来自同一表单数据通过校验tokenid 防止重复提交
    c2.新增表单是,在action的add()方法中生成uuid,并保存在redis中

  10. 消息的顺序是如何控制的?
    parentId(批次号)
    seqNo

  11. 思考:消息的延时投递?
    TTL DLX DLQ
    写个定时器(用户下订单的时间不确定)
    delay 插件

    RabbitMQ延时队列:TTL+死信队列
    TTL:Time To Live(存活时间)
    DLX:死信队列(Dead Letter Exchange,又称死信交换机),当消息称为Dead message 后,可以被重新发送到另外一个交换机,这个交换机就是DLX。
    消息队列之RabbitMQ_第3张图片
    消息队列之RabbitMQ_第4张图片
    死信消息:对列长度达到上限;消费者拒绝消息

  12. 消息积压
    消费者宕机积压
    消费者能力不足积压
    发送者流量太大
    解决方案:
    上线更多消费者;
    将消息先批量取出来,记录数据库里,再慢慢处理

你可能感兴趣的:(消息队列)