构架中的消息队列
本文由tom原创,转载请注明原文链接:https://work-jlsun.github.io//2016/11/19/MessageQueueInArch.html
1 消息队列
我们经常会看到业界五花八门的开源组件,消息队列也是,你眼中的消息队列有哪些? RabbitMq、ZeroMQ、Redis、Kafka? 我们可以拿这些消息队列组件来干啥?
乱花渐欲迷人眼,五花八门的东西,容易使得我们被表面所蒙蔽,透过这些表面的东西来看这些玲琅满目的消息队列。其实“消息队列”就如同这个名词所说的,分为两大核心 即“消息”+“队列”。其中消息在很大程度上就是数据,队列在很大程度上就是缓存或者泛义上的存储。
在计算机系统里头,从底层硬件到软件层的设计,到处是消息(数据)的传递。从网卡、磁盘、内存到CPU 缓存、寄存器、TLB等。到软件层面,程序执行体之间的同步和通信很多也是通过消息队列达到目的,如经典的生产者消费者模型,信号量的同步,通过管道、共享内存、socket进行通信。
在分布式系统中,系统之前交换消息的方式无非是通过同步RPC或者消息队列的方式。
与普通的同步RPC通信模式相比,基于消息队列的通信模式,从关注同步处理到关注异步通知。
2 构架中的消息队列
往往一个产品的开发设计到诸多功能,系统会非常庞大,而庞大的问题解决方法往往需要将其分化为一个个小的问题域,怎么划分组合这些诸多的功能向上支持整个产品,就是系统的构架。在构架设计里头很关键的一个元素或者说一样工具就是消息队列,消息队列可以帮助我们搞定很多事情。
- 业务解耦
当一个系统输入需要涉及到后端诸多系统的参与,有些系统会影响系统的主流程,这些系统必须立即参与。而有些系统是分支流程,不需要立即参与,可以通过异步消息的方式来将系统输入事件通知到其他非主流程分支系统。
所谓业务解耦,就是利用消息队列提供的异步消息存储通知功能,使得这些分支流程的业务逻辑不用整合在主业务逻辑里头,从而完成解耦。
举个例子:比如在登录系统中用户的登录操作除了涉及用户的登录验证之外,往往需要将用户登录的相关信息(比如登录时间、登录环境等)提供给其他系统,比如审计系统和风控系统,这些盘路系统比较适合使用消息队列进行业务解耦。
- 最终一致性
最终一致性指的是两个系统的状态保持一致,要么都成功,要么都失败。当然有个时间限制,理论上越快越好,但实际上在各种异常的情况下,可能会有一定延迟达到最终一致状态,但最后两个系统的状态是一样的。
比如对象存储服务中,我们除了提供PUT(上传对象)、GET(下载对象)这样的基本功能之外,还需要提供桶内对象的LIST(列举对象)功能,详见S3-HowTo-Dealwith-listObject。对于PUT、GET这类核心请求,为了使系统拥有更高的扩展性,其数据库(这里我们暂称数据库A)sharding方案我们选择Hash方式。而对于LIST,其功能特点要求数据库sharding(这里我们暂称数据库B)方案必须使用Range 方式(详见ListObject)。
所以这两块功能其实就涉及到依赖同一个用户输入来更新不同的数据库。使用数据库的两阶段提交方案,其代价是很大的。可以选择优先保障,PUT的核心业务逻辑,LIST列取功能保证其最终能够实现一致。在用户PUT上传对象的时候优先更新数据库A,然后将消息扔到消息队列异步更新数据库B。
- 广播
消息队列的基本功能之一是进行广播,让所有相关联的服务能够全部得到消息。
举个示例,在我们对象存储里头,用户桶的相关属性我们会在业务逻辑服务器上进行缓存。但是用户有时候会对桶的属性进行更新,比如添加referer防盗链、CORS跨域设置、自定义访问域名等等。如何将这些修改消息快速同步到所有业务逻辑服务器上,这个时候使用消息队列的广播特性就可以做到,比如使用Redis、RabbitMq等等。在我们的时间总,一般来通过广播的方式来将修改的通知及时发送给其他组件,实际数据的获取还是可以依赖统一个数据库存储进行拉取,并且配合周期性的全量同比比,来减小对消息队列在持久性和可靠性方面的要求,比如可以直接简单得部署一对KeepAlive的Redis就能够解决。
- 错峰流控
上下游对于事情的处理能力是不同的。比如,Web前端每秒承受上千万的请求,并不是什么神奇的事情,只需要加多一点机器,再搭建一些LVS负载均衡设备和Nginx等即可。但数据库的处理能力却十分有限,即使使用SSD加分库分表,单机的处理能力仍然在万级。由于成本的考虑,我们不能奢求数据库的机器数量追上前端。
典型的场景就是秒杀,如果前端不进行限流,蜂拥而上的请求可以直接把后端业务服务器或者数据库搞挂,此时将用户请求使用消息队列进行入队(比如RabbitMq),后端系统根据自身的处理能力从队列中拉取消息进行处理是不错的选择。具体可参考本文秒杀排队系统设计理念。
- 数据总线
此外,消息队列还可以充当数据总线的作用,系统中产生的各种事件,比如Nginx Access 日志(用户的访问事件),业务的整个事件流,比如订单、销售、发货、退货。此类系统中最典型的是就是Apache Kafka,如今如火如荼的大数据系统中,Kafka成为事实上的标准。如下图所示
具体可参见infoqapache-kafka-stream-data。
在我们对象存储系统中,利用kafka实现用户访问的存储转发,实现诸如流量、带宽计费,LogTrace(内部访问流程跟踪系统),请求响应时间实时分析监控等等。这些具体内容这边不作展开,后续可以专门写一篇文章进行分享。
4 参考文献
- https://zhuanlan.zhihu.com/p/21649950
- http://www.infoq.com/cn/news/2015/03/apache-kafka-stream-data
- http://www.infoq.com/cn/articles/yhd-11-11-queuing-system-design
This article used CC-BY-SA-4.0 license, please follow it.