首页>JAVA>正文

详细讲述消息队列(MQ)-上海Java培训技术干货

时间:2018-07-06 16:24:36   来源:上海尚学堂   阅读:
消息队列(MQ)是分布式系统中重要的组件,在很多生产环境如商品抢购等需要控制并发量的场景下都需要用到。现将其在这篇文章中做详细的讲述。

一、消息队列(MQ)概述

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

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

消息队列主要解决了应用耦合、异步处理、流量削锋等问题。

当前使用较多的消息队列有RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMq等,而部分数据库如Redis、Mysql以及phxsql也可实现消息队列的功能。
 

二、消息队列(MQ)应用场景

  1. 异步处理:例如短信通知、终端状态推送、App推送、用户注册等
  2. 数据同步:业务数据推送同步
  3. 重试补偿:记账失败重试
  4. 系统解耦:通讯上下行、终端异常监控、分布式事件中心
  5. 流量消峰:秒杀场景下的下单处理
  6. 发布订阅:HSF的服务状态变化通知、分布式事件中心
  7. 高并发缓冲:日志服务、监控上报


三、消息队列的特性

业务无关,一个具有普适性质的消息队列组件不需要考虑上层的业务模型,只做好消息的分发就可以了,上层业务的不同模块反而需要依赖消息队列所定义的规范进行通信。

FIFO,先投递先到达的保证是一个消息队列和一个buffer的本质区别。

容灾,对于普适的消息队列组件来说,节点的动态增删和消息的持久化,都是支持其容灾能力的重要基本特性。当然,这个特性对于游戏服务器中大部分应用中的消息队列来说不是必须的,这个也是跟应用情景有关的,很多时候没有这种持久化的需求。

性能,这个不必多说了,消息队列的吞吐量上去了,整个系统的内部通信效率也会有提高。

 

四、为什么需要消息队列?

当系统中出现“生产“和“消费“的速度或稳定性等因素不一致的时候,就需要消息队列,作为抽象层,弥合双方的差异。“ 消息 ”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。消息被发送到队列中,“ 消息队列 ”是在消息的传输过程中保存消息的容器 。

举几个例子

1)业务系统触发短信发送申请,但短信发送模块速度跟不上,需要将来不及处理的消息暂存一下,缓冲压力。就可以把短信发送申请丢到消息队列,直接返回用户成功,短信发送模块再可以慢慢去消息队列中取消息进行处理。

2)调远程系统下订单成本较高,且因为网络等因素,不稳定,攒一批一起发送。

3)任务处理类的系统,先把用户发起的任务请求接收过来存到消息队列中,然后后端开启多个应用程序从队列中取任务进行处理。
 

五、使用消息队列有什么好处?

5.1、提高系统响应速度

使用了消息队列,生产者一方,把消息往队列里一扔,就可以立马返回,响应用户了。无需等待处理结果。

处理结果可以让用户稍后自己来取,如医院取化验单。也可以让生产者订阅(如:留下手机号码或让生产者实现listener接口、加入监听队列),有结果了通知。获得约定将结果放在某处,无需通知。

5.2、提高系统稳定性

考虑电商系统下订单,发送数据给生产系统的情况。电商系统和生产系统之间的网络有可能掉线,生产系统可能会因维护等原因暂停服务。如果不使用消息队列,电商系统数据发布出去,顾客无法下单,影响业务开展。两个系统间不应该如此紧密耦合。应该通过消息队列解耦。同时让系统更健壮、稳定。

5.3、异步化、解耦、消除峰值

以上三点其实可以用一个例子来解释——设想有一款MMO游戏,没有人肉写的缓存层或者ORM,所有逻辑节点都直连MySQL,逻辑节点内除了要关注场景、战斗、交互等复杂逻辑以外,还要有个拼SQL语句的模块,想想简直是蛋疼。先考虑一下这样设计的弊端所在:

1. 逻辑节点与Db的交互会有大量IO,即使把与Db交互的模块耦合在逻辑节点内,其实现对你来说是黑盒,如果内部是同步实现的,那就直接卡你游戏主逻辑,就因为一次存盘操作,玩家们都掉线了,服务器也可以关掉了。

2. 那么我们改进一下,针对1的情况,可以把这个模块做到一个线程里挂在逻辑节点上。这样其实逻辑节点跟这个Db前端模块的交互就会基于一个比较原始的消息队列。但是这样还有一个坏处,那就是这两种任务一种是计算密集的(玩家的逻辑处理)、一种是IO密集的(只负责写入读取MySQL),搞到一个节点中,扩展起来会非常麻烦,而且耦合度太高。比如说现在发现场景放单节点上有瓶颈,要按场景分节点,那么这种挂在上面的数据模块怎么跟其他场景的交互呢?

3. 峰值的问题。在分布式系统中,一次分布式事务关联的是多个节点,其中每一个节点出现问题都会成为整个事务处理流程中的瓶颈。如果逻辑节点与数据库之间没有一个起到缓冲作用的节点,那就是每次操作都要访问数据库,对于MMO来说,一个玩家上线load几百K数据,一个服10万个玩家上线已经足够搞垮一个mysql节点了。如果直接搞垮还是比较好的结果,至少是前面的玩家确实登录上去了并且可以正常游戏,后面的玩家登录不上。但是很可惜,十年前开始流行的C10K说法就是在讲:并发量上来之后,会造成chain reaction,大量的并发不会直接挂掉你的mysql节点,但是会拖慢速度,降低吞吐量,一个玩家的请求由于处理时间太长,导致玩家放弃重试,但是对于后端来说,对该玩家之前的处理过程消耗的资源就全部浪费了,陷入恶性循环。

所以,这种情景下,一个介于逻辑节点和db节点之间的缓存节点就是理所当然的事情了。这个缓存节点其实很多时候也可以看作是一个更复杂的消息队列节点。(以上部分选自:http://www.ywnds.com/?p=5791)


六、消息队列的实际应用

我们使用了两种消息队列组件:

RabbitMQ:高可用、高可靠消息应用场景,例如记账失败重试、通知服务,消息不允许丢

Kafka:高性能消息应用场景,例如日志、监控,消息允许丢失。

在此之上,我们封装了消息应用中心、日志服务等核心组件和服务。那么,消息应用中心和日志都用到了消息队列什么技术? 干货来了…
 

6.1.     消息应用中心

消息应用中心(任务中心)使用了消息队列的异步处理、数据同步、重试补偿、系统解耦、流量消峰等特性。其中:

消息应用中心(任务中心),支持RabbitMQ和Kafka两种消息通道,支持在任务元数据层面设置

任务:就是一个包含了任务执行上下文的消息,同时代表了异步处理

任务发送者(ITaskSender)发送任务:消息的生产者将任务消息发送的消息队列

任务类型:消息队列名称,例如:HaKeepAcco***Queue,充电补偿记账队列

消息队列:任务的临时存储

任务中心:任务集中处理,消息消费者

任务处理完成:消息Ack确认

任务的多级重试:多个重试消息队列,HaSysTaskStore2Queue
 

6.2.     日志组件

日志组件,使用了消息队列的高并发缓冲和发布订阅特性。其中:

日志组件使用Kafka作为消息通道,因为Kafka的性能好,吞吐量大, 可以容忍偶尔的消息数据丢失

日志组件使用发布订阅的消息模型

日志组件包含日志服务SDK和日志HSF服务,二者都是消息的生产者Producer

日志类型:消息的Topic主题

日志处理器:消息的消费者、Topic的订阅、日志数据处理(Hbase\ES\其他)
 

6.3.     RPC服务状态变化通知

RPC服务状态变化通知,使用了消息队列的发布订阅特性。其中:

RPC服务状态变化通知,使用了RabbitMQ消息队列技术

使用发布订阅的消息模型

Topic:RPCServiceState

RPCService.Proxy:RPC服务状态变化消息的订阅者

RPC服务注册、发布:消息的生产者,发送RPC服务状态变化消息。


上海尚学堂Java培训收集整理编辑,更多Java技术文章请点击:上海Java培训技术文章 ,上海尚学堂Java分布式+大数据连续班即将开班,联系客服小姐姐获取试听名额或者获取相关学习资料。

分享:0

电话咨询

客服热线服务时间

周一至周五 9:00-21:00

周六至周日 9:00-18:00

咨询电话

021-67690939
15201841284

微信扫一扫