文档章节

UCloud 分布式消息队列 UMQ 设计与架构演进

OSC源创君
 OSC源创君
发布于 2016/12/07 00:56
字数 2457
阅读 2628
收藏 67

肖丁,基础架构研发部工程师,负责 UCloud 公共组件、监控、消息服务等系统,专注于高可用高性能框架、中间件应用。2014年加入 UCloud,先后参与 UCloud 监控平台改造、内部 DNS 集群化、DB 服务集群化、golang 网络通信框架、通用 agent 等项目。

一、背景介绍

消息队列是企业架构演进过程中不可或缺的部分,其天生的灵活性、可扩展性、消息处理能力可以方便地帮助企业服务解耦,处理服务峰值压力。但构建与维护消息队列是很耗费时间与人力的事情。消息队列集群在使用和维护过程中,经常会遇到主节点性能瓶颈、容量规划、网络分区等问题,处理不当则可能导致消息丢失,进而影响服务。

1、消息队列应用场景

通常,我们将消息功能分为两大类:主动消费和被动消费。

图中是两个被动消费的场景,即消费者首先要和消息队列产生一个长连接。当推送者向消息队列推送消息的时候,第一种场景是指要轮询推送给消费者,这是最常见的轮询负载均衡的方式;第二种是以广播的形式,如一个消息副本需要传递给多个子模块进行消费。如果没有消息队列的应用有些消息从 A 模块传递到 B 模块在传递到 C 模块,实现一个整体的链入的处理。

2、模块解耦

紧耦合系统:服务 A 依赖服务B的稳定性,当服务 B 异常或者日常升级时,服务 A 的请求将被丢弃。

松耦合系统:接入 UMQ 后,服务 A 的消息由 UMQ 缓存,当服务 B 异常时,服务 A 的消息不会丢失,待服务 B 恢复之后再做处理。

3、消息缓冲

无 UMQ 场景下,当服务请求量增加时,服务器压力与请求量为线性关系,且通常的做法是做服务的扩容,造成资源的浪费。

接入 UMQ 场景后,服务请求量的大幅波动不会影响服务的负载能力,帮助服务平滑处理所有请求。如,当大量消息同时涌进消息队列时,可能出现后台服务无法及时处理所有消息,导致回复等情况,这时后端子系统会根据根据用户的消费能力和消费水平批量筛选部分消息。

4、异步通知

无 UMQ 场景下,一次请求需要同步等待后端处理,得到响应后再执行其他任务,可能会出现延迟等待的情况;或者轮询后端任务结果,造成多余的请求。

接入 UMQ 后实现异步通知,用户不需要同步等待,也不需要额外的轮询开销,通过异步的队列通知,获取结果集。它更适合后端研发人员使用,是优化后端系统的一种方式。如图所示,左边的同步模式和轮询模式,它们都需要客户端发送请求后等待服务端的返回,或客户端发送请求后要不断地轮询,这会对后端系统造成负载压力。异步通知的方式是基于两个消息队列,一个进行消费推送,一个进行 response 回执,这有利于实现后端系统负载的减轻。

二、项目构建过程

设计消息队列之初,我们没有考虑要自己”造轮子”,而是使用了一些现有的开源方案,如,RabbitMQ。但在搭建过程中遇到不少问题:整体的集群需要通过一套自有的共识算法来选出一个 master 节点做所有消息的一次性算法。但网络波动之后,我们发现整个消息队列的集群会出现脑裂的现象,如,不同的队列会被分配到不同的节点中,导致节点数据没法融合在一起,这对消息队列的设计产生了很大的困扰。未解决此类问题,在 RabbitMQ 脑裂的策略中我们必须舍弃一部分数据,再将它们合并到一个集群中等。

随后,我们也利用了 Golang 的高并发和 Channel 的一些特性来实现分布式的消息队列。

在我们决定造轮子时,我们会考虑哪种方式能最快实现消息队列,然后我们确定了两种模式,单体模式和微服务模式,如下所示,我对它们的优缺点进行了分析:

架构 v1.0 如图所示:

其中,Proxy 做揭露层的负载均衡,Broker 实现队列的核心逻辑,Manager 管理语言数据。第一版设计我们只用了运营访问的方式。因为我们已经使用了 Broker 的集群做队列的管理,所以整个队列集群需要有一套自适应的规则来进行管理所有节点。因此,在 Broker 和 Manager 节点内我们使用了一次性哈希算法,把不同的队列分配到不同的队列节点上处理。

当然,v1.0上也出现了一些问题,如:我们无从知道队列会落在那个队列节点上等。于是,根据这些问题,我们做了 v2.0 的改进:

此次改进引入了一个 ULB,它能快速实现后端服务的调度,请求的快速分配等。Controller 可人工控制 Proxy 下发队列信息到相应节点。底层的分布式存储也是 Redis 和 MySQL 的服务,这减轻了底层容量负担。这个架构目前也存在一些问题,如,Controller 是一个单点,当它失效时,如果没有及时回复,队列集群会不可控。其次,因为我们使用的是储备的 Broker,虽然它有自己的资源限定,但还是会对整体资源造成浪费。

在 v3.0 的时候我们最终确定了架构版本,我们重新用回原来的 Zookeeper 做 Broker 队列节点的信息上报和映射关系的处理。而 Proxy 和 Manager 是利用 Zookeeper 的 watch 特性获取队列关系的变化和队列节点的心跳变化,它能秒及地改变下发的策略。

在 Manager 中,我们把所有队列的控制规则和 Controller 进行合并,这样,当主 Manager 失效时,子 Manager 同样能注册成功。

而在 Bocker 方面,则用回之前的设计思路,即采用单点的方式,这样,当某个 Broker 失效时,Manager 会在感知到之后会将整个节点剔除,然后重新分配一个节点。

三、技术实践

1、Broker 模块的实现

因为整个内容是用 Golang 制作,所以 Broker 相应的具备了 Golang 的一些特性,如图所示:

举例说明:上层模块(Proxy)将所有消息请求的透传至 Brocker,Brocker 在协议层做解析,然后将请求放置业务逻辑中,业务逻辑再选择对应的方法,交由 go routine 进行处理,随后生成的新队列节点判断是否接受消息,然后通过 go channel 层层传递至 Subscriber。最后推送的消息则需要利用 Brocker 和 Proxy 之间的连接池,它能最大限度提升性能。此外,当 TCP pool 开始服务时,Proxy 会向 Broker 主动申请产生连接。

2、整个系统的容灾和恢复

  • 节点探测
  • 连接恢复
  • 跨机房容灾

节点探测

Proxy 与 Brocker 节点的探测:因为我们无法直接从连接层感知连接是否出现异常,所以需要在 receive 和 send 的过程中感知,随后再进行连接恢复的处理。

连接恢复

因 Proxy 会主动向 Brocker 发起 TCP 连接,所以每个连接都会通过 go routine 执行 receive 操作。当 receive 出现异常,Proxy 能感知并退出 go routine,之后重新建立连接放入 go routine。反之,当 Brocker 需要探测 Proxy 异常时,它会先从 TCP 连接池选出一项连接进行消息推送,如果推送失败,会反向删除原连接池的连接并重新选择连接,这两个过程相辅相成。

跨机房容灾

跨机房容灾是将不同用户分到不同的 set 内来做区分,它只能保证部分 set 内的用户不受影响。当整个数据中心出现问题时,图中左边整条链路都将失效。

四、经验和心得

在 UMQ 的设计和架构的演进过程中,我们虽然遇到了许多具有挑战性的问题,但最终还是克服了困难,并成功利用 UCloud IaaS 服务搭建了微服务架构,保证了服务的高可用,同时利用 golang 完成了 UMQ 核心模块的实现,实现了高性能。希望这些经验对你有帮助。

© 著作权归作者所有

OSC源创君

OSC源创君

粉丝 5052
博文 60
码字总数 129391
作品 0
深圳
私信 提问
加载中

评论(3)

yhtmxl
yhtmxl
ucloud基因太差,没上过战场,模拟并发测试有个屌用,相比aliyun中间件,aliyun中间件至少都是经过天猫海量访问检验过的
51clocker
51clocker

引用来自“哆嗦一下呗”的评论

ucloud 的ULB 我就不说什么了, 小型公司的需求都满足不了,更别说中小型企业了, 跟阿里云比起来差很多
ucloud的ulb4 可以实现吞吐量达14M PPS(10G, 64字节线速),新建连接200k CPS以上,并发连接数达到3000万、10G线速的转发,还是很屌的。
哆嗦一下呗
ucloud 的ULB 我就不说什么了, 小型公司的需求都满足不了,更别说中小型企业了, 跟阿里云比起来差很多
【广州】OSC源创会第55期报名开始

本期图文回顾链接:https://www.oschina.net/question/2686220_2206472 转眼就到了十一月,在年末之际OSC源创会将来到羊城广州,与各位OSCer在秋末来一场浪漫的相约,我们一如既往秉承着“自...

Alaise
2016/10/18
24.5K
33
【广州】OSC源创会第55期报名开始

本期图文回顾链接:https://www.oschina.net/question/2686220_2206472 转眼就到了十一月,在年末之际OSC源创会将来到羊城广州,与各位OSCer在秋末来一场浪漫的相约,我们一如既往秉承着“自...

Alaise
2016/10/18
696
0
广州源创会前排有妹子出手,路过的男朋友速来认领

到2016年11月14日,开源中国第55期源创会【广州站】圆满结束,广州站活动详情信息请查看这里,主题PPT下载请注意文末链接。 广州站也是2016年源创会路演终站,继续着源创会一贯的火爆氛围,前...

Alaise
2016/11/14
10.1K
48
UCan下午茶 2018-杭州站《企业的“数据困惑”该何去何从?》

如今,已经进入大数据时代, 各种系统、应用、活动所产生的数据已经浩如烟海,数据已经不再仅仅是企业存储的信息,而是已经成为可以从中获取巨大商业价值的企业的战略资产,在这样的情况下,...

UCloudTech
2018/07/19
8
0
UCan下午茶 2018-杭州站《企业的“数据困惑”该何去何从?》

如今,已经进入大数据时代, 各种系统、应用、活动所产生的数据已经浩如烟海,数据已经不再仅仅是企业存储的信息,而是已经成为可以从中获取巨大商业价值的企业的战略资产,在这样的情况下,...

UCloudTech
2018/07/19
524
0

没有更多内容

加载失败,请刷新页面

加载更多

全面兼容IE6/IE7/IE8/FF的CSS HACK写法

浏览器市场的混乱,给设计师造成很大的麻烦,设计的页面兼容完这个浏览器还得兼容那个浏览器,本来ie6跟ff之间的兼容是很容易解决的。加上个ie7会麻烦点,ie8的出现就更头疼了,原来hack ie...

前端老手
17分钟前
3
0
常用快递电子面单批量打印api接口对接demo-JAVA示例

目前有三种方式对接电子面单: 1.快递公司:各家快递公司逐一对接接口 2.菜鸟:支持常用15家快递电子面单打印 3.快递鸟:仅对接一次,支持常用30多家主流快递电子面单打印 目前也是支持批量打...

程序的小猿
20分钟前
6
0
Yii 框架中rule规则必须搭配验证函数才能使用

public $store_id;public $user_id;public $page;public $limit;public $list;public $mch_list;public $cart_id;public $is_community;public $shop_id;public $cart_typ......

chenhongjiang
23分钟前
4
0
Flutter使用Rammus实现阿里云推送

前言: 最近新的Flutter项目有“阿里云推送通知”的需求,就是Flutter的App启动后检测到有新的通知,点击通知栏然后跳转到指定的页面。在这里我使用的是第三方插件Rammus来实现通知的推送,之...

EmilyWu
23分钟前
42
0
Knative 实战:三步走!基于 Knative Serverless 技术实现一个短网址服务

短网址顾名思义就是使用比较短的网址代替很长的网址。维基百科上面的解释是这样的: 短网址又称网址缩短、缩短网址、URL 缩短等,指的是一种互联网上的技术与服务,此服务可以提供一个非常短...

阿里巴巴云原生
38分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部