从0开始细说分布式系统中的“消息队列”

语前

百度百科:“消息队列”是在消息的传输过程中保存消息的容器。

一句很简单、且带着浓厚的中国老百姓质朴气息的话。虽然很直白,但就是不明白这是一坨什么东西,对吧?

首先从字面上看,分两段:消息、队列。消息很简单,队列也很简单(大家都排队买过东西)。那“消息队列”咱们就可以简单理解为:许多消息排成的队列。咱们生活中常见的,买饭排队、ATM取钱排队、超市结账排队等等,那为什么排队?因为人多啊!同样“消息队列”也是因为消息过多而排成的队列。这个队列的成员是消息。“人”在自己的队列中排队,如果轮到自己办完事,就可以离开队列,那么“消息”在队列中如果尽完自己义务也可以离开了....

 

概述

消息队列(Message Queue),是分布式系统中重要的组件,其通用的使用场景可以简单地描述为:

当不需要立即获得结果,但是并发量又需要进行控制的时候,差不多就是需要使用消息队列的时候。

简单的说就是:

非紧急事太多、来不及处理、先“登记完要办的事(消息)”排队、人先回去吧、办事部门会挨个处理的

 

场景

消息队列到底什么时候使用消息队列呢?从专业的角度上讲,比如:

  • 应用耦合:多应用间通过消息队列对同一消息进行处理,避免调用接口失败导致整个过程失败;
  • 异步处理:多应用对消息队列中同一消息进行处理,应用间并发处理消息,相比串行处理,减少处理时间;
  • 限流削峰:广泛应用于秒杀或抢购活动中,避免流量过大导致应用系统挂掉的情况;
  • 消息驱动的系统:系统分为消息队列、消息生产者、消息消费者,生产者负责产生消息,消费者(可能有多个)负责对消息进行处理;

上面的这四种情况,我分别通过描述一些咱们生活中使用的场景进行逐一描述

1、应用解耦

场景:10W人在线考试,考试时长100分钟,取前200名。

分析:正常情况下,“考生考完考试--->提交试卷--->系统改卷--->返回提交成功---->1天后公布考试结果”。由实际情况得知,考生提交试卷的时间大多集中在最后10分钟。如果系统按照业务流程进行“提交试卷--->改卷--->返回成功---->1天后查看录取结果”,这样短时间大量改卷会造成系统业务阻塞,考生体验很不好(提交试卷后“等待改卷”占用了太多时间)。但是现实情况是,改卷没有规定必须马上完成,所以,我需要优化业务处理方案。

方案:我们可以考虑对系统进行业务划分为两个应用,“考试系统”与“阅卷系统”。“考试系统”考完,把阅卷任务写入“消息队列”,“阅卷系统”去“消息队列”中领取任务,进行阅卷,一段时间后、阅卷完成,发布分数结果和录取结果进行公布。

总结:这样我们根据实际情况,在业务上进行应用分离,分离“流水线方式”的“交卷-->改卷”,把改卷稍稍后延,提高考生的“交卷速度”,又不耽误一天后公布成绩。完美!

2、异步处理

场景:用户注册的时候,获取短信/邮箱验证码。

分析:用户在客户端点击“发送验证码--->服务端发送---->用户手机收到验证码--->OK”。一般情况,验证码发送是有第三方提供的服务,第三方服务的性能有时候不稳定,如果按照串行业务模式,用户需要一直等待第三方发送成功后才能返回操作结果,这个等待的过程时长稳不稳定(因为如果多个用户同时要求发送验证码等等,而且等待过程中,用户页面得不到响应),给用户造成不好的体验。

方案:当用户点击发送验证码后,立即给用户返回“验证码已发送,请等待..”,进而立即响应用户的操作结果,同把发送短信的命令信息放入“消息队列”,然后使用多线程模式对“消息队列”进行任务处理。

总结:消息队列进行异步处理,避免了串行业务的响应等待,优化了客户对网站注册模块的体验,同时也能一定程度降低服务器压力。

3、限流削峰

场景:100件特价商品,双十一抢购。

分析:秒杀活动开始,由于瞬时访问量过大,服务器接收过大,会导致流量暴增,相关系统无法处理请求甚至崩溃。

方案:首 先、加入消息队列后, 系统可以从消息队列中取数据,相当于消息队列做了一次缓冲。其次、队列长度可以做限制,事实上,秒杀时,后入队列的用户无法秒杀到商品,这些请求可以直接被抛弃,返回活动已结束或商品已售完信息;

总结:此种解决方案仍是大多数“抢购活动”使用的主要技术手段。

4、消息驱动

场景:考试系统(开始)--->批阅试卷系统(批阅)--->推荐强化练习(结束)

分析:正常情况下,这是一个串行的业务模式,三个独立的系统,在本模式下,如果其中一个模块出错,就会导致整个响应过程进行卡壳,无法继续进行下去。

方案:这三个子系统间由消息队列连接起来,前一个阶段的处理结果放入队列中,后一个阶段从队列中获取消息继续处理。如下图:

总结:首先避免了直接调用下一个系统导致当前系统失败;其次、每个子系统对于消息的处理方式���以更为灵活,可以选择收到消息时就处理,可以选择定时处理,也可以划分时间段按不同处理速度处理;

 

常用消息队列

 

尾语

消息队列功能很独特、也很诱人,但是,咱们在使用的时候还是要根据自己的实际情况、实际业务来选用。不要为了使用“技术”而使用“技术”,到最后搞的项目四不像。比如,消息订阅的分发模式,第一种很好模仿,无非就是一个list(不考虑持久化的情况),第二种发布订阅模式也可以使用redis的订阅机制来做。

 

各类资料:

RabbitMQ的安装与简介

RabbitMQ中、消息、信道、交换机、队列等概念

RabbitMQ的六种(只讲到5种)工作模式

RabbitMQ的使用示例(.net) 

RabbitMQ的使用示例(java) 

 RabbitMQ的ACK机制:如果设置了回执模式,当消费者宕机而没有回执,消息队列就会重发本消息。(注意:ACK没有超时不超时的问题,仅仅只有消费者宕机才会重发消息)

RabbitMQ消息应答ACK与消息持久化

Rabbitmq的两种模式下(事务消息/发布确认)(注意,这两种方式在处理分布式事务的时候,特别合适)

Kafka的简介(术语解释、以及常用消息队列的简单介绍)

从架构上分析RabbitMQ和Kafka(很精确,解释了为什么kafka和rabbitmq适用的场景不一样。)

confluent-kafka-dotnet类库以及.net/core示例【同步发送(发送成功,会阻塞代码,没有返回值)、异步发送(还可以适用await等待“发送完成”拿到返回结果,也可以使用ContinueWith设置回调函数处理返回的结果)】

添加评论

Loading