消息队列学习 -- 基础了解

    xiaoxiao2022-07-14  168

    消息队列这个概念已经出了好几年,但在日常业务中鲜有用到。听说消息队列的应用场景有异步处理、分布式事务、日志收集、系统接口等,考虑到这些场景自己后期都会遇到,如果利用消息队列可以更好的解决问题,那学习一下也未尝不可哈。计划先做一个技术储备,分一个小的学习系列,由浅入深系统的学习一下消息队列的东西。

    还是坚持理论 + 实践的方式,坚决杜绝空谈理论。废话少说,开始我们今天的入门学习吧。

    在学习消息队列之前,我们有必要先来了解一下消息队列产生的时代背景:

    一、背景

    消息队列的出现是伴随着分布式系统的出现而出现的。

    这里需要先明确一下,分布式和集群是两个不同的概念。

    分布式:将一个业务拆分成多个子业务并部署在不同的服务器上

    集群:将一个业务部署在多台服务器上

    通常,对于一个分布式应用而言,每个子系统都是采用集群方式部署的。

    单体应用时代,各模块之间是通过进程间通信实现调用的。但在单体应用按业务拆分成独立的子系统之后,各个子系统可能是物理隔离的,不能再使用传统的进程间调用了,需要一种新的通信方式。

    二、系统通信

    对于拆分之后的多个子系统之间,常见的通信方式有以下两种:远程过程调用、消息队列。

    1、远程过程调用

     百科上对RPC的介绍是这样的:通过网络从远程计算及程序上请求服务,而不需要了解底层网络技术的协议。

    简单来说,RPC是一种协议,将网络通信封装了一下,对调用方透明;

    目前已知的RPC框架又Dubbo(阿里)、GRPC(谷歌)、Thrift(Apache);GRPC、Thrift、Dubbo的比较

    RPC支持同步/异步两种调用模式,但很多时候都是使用同步调用功能,适用于对实时性要求比较高的场景,但同时会产生延迟的;

    优点:远程调用相当于本地调用;对于调用放无需关注网络通信细节,减少了开发成本;

    缺点:系统耦合,可扩展性查;

    不足:不支持分布式事务,而且是点对点通信;

    一个好的RPC框架要能提供负载均衡、超时重试、健康检查等各项功能。

    2、消息队列

    各个子系统之间基于消息进行通信:应用中的某个子系统负责发送消息,有关心这条消息的相应字系统负责接收消息,并在接收到消息之后进行系统内的业务处理。

    消息在被发送后可以立即返回,由消息队列负责消息的传递。消息发布者只管将消息发送到消息队列而不用管谁会来取,消息使用者只管从消息队列中取数据而不用管是谁发布的(也可能是由消息队列主动推的)。

    对于可靠性要求不高的业务场景,发布者可提供消息的不可靠传输,发布之后不care消息是否被消息队列接收;

    对于可靠性要求较高的场景,需要保证消息的可靠传递,发布者可以同步或者异步等待消息发布的状态;

    同步模式下,发布者会被阻塞,较高可靠性,但吞吐量低;异步模式下,可以显著提高吞吐量;

    消息队列本质上是发布-订阅的关系:订阅者订阅某个或者某些主题,发布者发布某个主题的数据;

    也可以把整个流程看成是一个生产者消费者模型:生产者(发布者)向缓存池(消息队列)中发送消息,消费者(订阅者)从缓冲池中取数据。

    一个好的消息中间件要能提供如消息堆积、消息持久化、可靠传递、消息重复、严格有序、集群等功能。

    三、应用场景

    RPC主要应用于对消息的实时性要求比较高,而且是同步调用的场景下;

    消息队列应用场景广泛,可以用于异步处理、系统解耦、流量削峰、事务最终一致性;

    1、异步处理

    队列天然具有异步处理的能力。

    对于不需要一次性完成的业务逻辑(如用户注册成功后,向用户发送注册成功邮件以及发放优惠券以及用户支付成功之后同步物流状态信息、库存信息),可以将流程拆分:发布者将需要异步处理的逻辑以消息的形式发送到消息队列服务器,由消息队列服务器进行后续的业务逻辑处理;消息队列服务器会将接受到的消息保存到队列中,之后消费该队列,并向订阅该主题的消费者发送消息。

    如果需要保证消息的可靠传输,在消息发送失败之后(网络传输失败或者其他异常),发布者需要进行发布的重试;

    如果需要关注消息的发送状态,只需要提供一个回调接口给消息队列使用,用于告知消息发送情况;

    2、系统解耦

    解耦各个子系统之间的显示调用关系,调用者和被调用者通过消息实现,使系统易于扩展和维护;

    比如用户下单支付,初期需求是在用户支付成功之后更新库存信息、优惠券信息;现在需求变了,在用户支付成功之后还需要向用户发送消息通知、物流信息同步,后期还可能又支付之后赠送积分、送豆等需求,总之,系统的功能是逐渐扩展的。

    你可以使用传统的方式,有需求来时就增加一个模块并集成到系统内。渐渐的你就会发现,模块越来越多,也越来越难以维护。

    而如果采用消息队列呢?假如还是增加积分功能,你只需要新增加积分模块,然后订阅订单支付主题即可,无需再将模块集成到订单支付模块中。这样,各个子系统之间是独立的,系统可以无限的扩展;

    是不是很牛逼?

    3、流量削峰

    这个还是借用了异步处理的功能,将短时间内高并发的请求持久化,然后异步处理,从而削平高峰期的并发流量,改善系统性能;

    4、事务最终一致性

    通过事务表 + 队列保证事务的一致性。如A向B转账时,A扣减相应金额的钱同时将记录写入事务表,同时发布一条消息到消息队列中;B从消息队列中取出消息,更新账户的钱同时更新事务表;这个时候需要保证更新数据库和消息队列的操作在一个事务中,可以使用事件表来解决。

    另外,也可以使用RocketMQ消息中间件提供的事务消息实现分布式事务的功能,具体详情这里就先不展开,后期我们会有专门的系列了解。

    四、系统架构

    对于一个典型的消息队列,需要包括以下三部分:

    broker:消息处理中心,负责消息的接收、存储和转发producer:生产者,负责生成消息和发送消息到消息处理中心consumer:消费者,从消息中心获取消息,并进行相应处理

    五、其他

    除此之外,消息队列还要提供诸如消息堆积、消息持久化、可靠传递、消息重复、严格有序、集群等。

    1、消息堆积

    当消费者的消费速度无法跟上生产者的生产速度时,消息便会在消息队列服务器产生堆积,此时消息队列服务器需要能够处理这种情况。比如,微信消息队列设置一个阈值,当超过这个阈值的时候,新的消息不再放入消息队列;

    2、消息持久化

    消息队列服务器在接收到消息之后先将消息暂存起来,可以放到内存中,也可以持久化存储到数据库、文件系统中;当暂存到内存中时,在机器宕机时会出现数据丢失的问题。所以,如果要求消息不能丢失,一定要进行消息的持久化;

    3、可靠投递

    一个完成的消息需要经过三个阶段:

    1.生产者发送消息到消息处理中心

    2.消息处理中心将消息持久化

    3.消息中心将消息发送给消费者

    由于跨越不同的系统,中间会碰到诸如网络问题、系统宕机等各种不确定的情形。

    对于第一个阶段,生产者在发送消息之后可以提供一个异步回掉接口来获取消息发送的结果,同时可以设置超时时间;

    如果在规定的时间内没有接收到对方的回掉请求,则认为消息发送失败,重新发送消息;

    如果在消息发送之后,消息队列服务器告知出现了异常,这个时候生产者也会进行消息的重复发送;

    对于第二个阶段,消息处理中心在接收到消息之后会将消息持久化储存下来,然后发送给消费者;当消息发送失败或者发送超时时,消息的状态是待发送,定时任务会不停的轮询所有的待发送消息,最终保证消息的可靠投递;

    对于第三个阶段,消费者在出现消费失败或者或者接受消息失败时(消息处理中心发送消息时也会设置超时时间),也会进行消息的再次发送;

    4、消息重复

    为了保证消息的可靠投递,会导致消息重复的问题,此时需要保证消费者业务具有幂等性;

    5、严格有序

    消费者接受到消息的顺序与发送者发送消息的顺序是一致的。

    6、集群

    提供高可用性,避免单点故障;也可以通过集群进行负载均衡,提高吞吐量;

    再者,集群需要提供方便的水平扩展能力。

    OK,本篇文章先做一个引子,从下一个章节开始我们逐步展开。

    参考:

        消息队列与RPC

        MQ与RPC区别

    最新回复(0)