今日头条在消息服务平台和容灾体系建设方面的实践与思考

mPass 企业租户微服务开发平台

业务背景

今日头条的服务大量使用微服务,容器数目巨大,业务线繁多, Topic 的数量也非常多。另外,使用的语言比较繁杂,包括 Python,Go, C++, Java, JS 等,对于基础组件的接入,维护 SDK 的成本很高。

引入 RocketMQ 之前采用的消息队列是 NSQ 和 kafka , NSQ 是纯内存的消息队列,缺少消息的持久性,不落盘直接写到 Golang 的 channel 里,在并发量高的时候 CPU 利用率非常高,其优点是可以无限水平扩展,另外,由于不需要保证消息的有序性,集群单点故障对可用性基本没有影响,所以具有非常高的可用性。我们也用到了 Kafka ,它的主要问题是在业务线和 Topic 繁多,其写入性能会出现明显的下降,拆分集群又会增加额外的运维负担。并且在高负载下,其故障恢复时间比较长。所以,针对当时的状况和业务场景的需求,我们进行了一些调研,期望选择一款新的 MQ 来比较好的解决目前的困境,最终选择了 RocketMQ

为什么选择 RocketMQ

这是一个经过阿里巴巴多年双11验证过的、可以支持亿级并发的开源消息队列,是值得信任的。其次关注一下他的特性。 RocketMQ 具有高可靠性、数据持久性,和 Kafka 一样是先写 PageCache ,再落盘,并且数据有多副本;并且它的存储模型是所有的 Topic 都写到同一个 Commitlog 里,是一个append only 操作,在海量 Topic 下也能将磁盘的性能发挥到极致,并且保持稳定的写入时延。然后就是他的性能,经过我们的 benchmark ,采用一主两从的结构,单机 qps 可以达到 14w , latency 保持在 2ms 以内。对比之前的 NSQ 和 Kafka , Kafka 的吞吐非常高,但是在多 Topic 下, Kafka 的 PCT99 毛刺会非常多,而且平均值非常长,不适合在线业务场景。另外 NSQ 的消息首先经过 Golang 的 channel ,这是非常消耗 CPU 的,在单机 5~6w 的时候 CPU 利用率达到 50~60% ,高负载下的写延迟不稳定。另外 RocketMQ 对在线业务特性支持是非常丰富的,支持 retry , 支持并发消费,死信队列,延时消息,基于时间戳的消息回溯,另外消息体支持消息头,这个是非常有用的,可以直接支持实现消息链路追踪,不然就需要把追踪信息写到 message 的 body 里;还支持事务的消息。综合以上特性最终选择了 RocketMQ 。

RocketMQ 在头条的落地实践

下面简单介绍下,今日头条的部署结构,如图所示:

由于生产者种类繁多,我们倾向于保持客户端简单,因为推动 SDK 升级是一个很沉重的负担,所以我们通过提供一个 Proxy 层,来保持生产端的轻量。 Proxy 层是由一个标准的 gRpc 框架实现,也可以用 thrift ,当然任何 RPC 都框架都可以实现。

Producer 的 Proxy 相对比较简单,虽然在 Producer 这边也集成了很多比如路由管理、监控等其他功能, SDK 只需实现发消息的请求,所以 SDK 的非常轻量、改动非常少,在迭代过程中也不需要一个个推业务去升级 SDK 。 SDK 通过服务发现去找到一个 Proxy 实例,然后建立连接发送消息, Proxy 的工作是根据 RPC 请求的消息转发到对应的 Broker 集群上。 Consumer Proxy 实现的是 pull 和二次 reblance 的逻辑,这个后面会讲到,相当于把 Consumer 的 pull 透传给 Brokerset , Proxy 这边会有一个消息的 cache ,一定程度上降低对 broker page cache 的污染。这个架构和滴滴的 MQ 架构有点相似,他们也是之前做了一个 Proxy ,用 thrift 做 RPC ,这对后端的扩容、运维、减少 SDK 的逻辑上来说都是很有必要的。

在容器以及微服务场景下为什么要做这个 Porxy ?

有以下几点原因:
1、 SDK 会非常简单轻量。

2、很容易对流量进行控制; Proxy 可以对生产端的流量进行控制,比如我们期望某些Broker压力比较大的时候,能够切一些流量或者说切流量到另外的机房,这种流量的调度,多环境的支持,再比如有些预发布环境、预上线环境的支持,我们 Topic 这边写入的流量可以在 Proxy 这边可以很方便的完成控制,不用修改 SDK 。

3,解决连接的问题;特别是解决 Python 的问题, Python 实现的服务如果要获得高并发度,一般是采取多进程模型,这意味着一个进程一个连接,特别是对于部署到 Docker 里的 Python 服务,它可能一个容器里启动几百个进程,如果直接连到 Broker ,这个 Broker 上的连接数可能到几十上百万,此时 CPU 软中断会非常高,导致读写的延时的明显上涨

4,通过 Proxy ,多了一个代理,在消费不需要顺序的情况下,我们可以支持更高的并发度, Consumer 的实例数可以超过 Consume Queue 的数量。

5,可以无缝的继承其他的 MQ 。中间有一层 Proxy ,后面可以更改存储引擎,这个对客户端是无感知的。

6,在 Conusmer 在升级或 Restart 的时候, Consumer 如果直接连 broker 的话, rebalance 触发比较频繁, 如果 rebalance 比较频繁,且 Topic 量比较大的时候,可能会造成消息堆积,这个业务不是太接受的;如果加一层 Proxy 的话, rebalance 只在 Proxt 和 Broker 之间进行,就不需要 Consumer 再进行一次 rebalance , Proxy 只需要维护着和自己建立连接的 Consumer 就可以了。当消费者重启或升级的时候,可以最小程度的减少 rebalance 。

以上是我们通过 Proxy 接口给 RocketMQ 带来的好处。因为多了一层,也会带来额外的 Overhead 的,如下:
1,会消耗 CPU , Proxy 那一层会做RPC协议的序列化和反序列化。
如下是 Conusme Proxy 的结构图,它带来了消费并发度的提高。由于我们的 Broker 集群是独立部署的,考虑到broker主要是消耗包括网卡、磁盘和内存资源,对于 CPU 的消耗反而不高,这里的解决方式直接进行混合部署,然后直接在新的机器上进行扩,但是 Broker 这边的 CPU 也是可以得到利用的。

2,延迟问题。经过测试,在 4Kmsg、20W Tps 下,延迟会有所增加,大概是 1ms ,从 2ms 到 3ms 左右,这个时延对于业务来说是可以接受的。

下面看下 Consumer 这边的逻辑,如下图所示,

比如上面部署了两个 Proxy , Broker,左边有 6 个 Queue ,对于顺序消息来说,左边这边 rebalance 是一个相对静态的结果, Consumer 的上下线是比较频繁的。对于顺序消息来说,左边和之前的逻辑是保持一致的, Proxy 会为每个 Consumer 实例分配到合适的数量的 Queue ;对于不关心顺序性的消息,Proxy 会把所有的消息都放到一个队列里,然后从这个队列 dispatch 到各个 Consumer ,对于乱序消息来说,理论上来说 Consumer 数量可以无限扩展的;相对于和普通 Consumer 直连的情况,Consumer 的数量如果超过了Consume Queue的数量,其中多出来的 Consumer 是没有办法分配到 Queue 的,而且在容器部署环境下,单 Consumer 不能起太多线程去支撑高并发;在容器这个环境下,比较好的方式是多实例,然后按照 CPU 的核心数,启动多个线程,比如 8C 的启动 8 个线程,因为容器是有 Quota 的,一般是 1C,2C,4C,8C 这样,这种情况下,如果线程数超过了 CPU 的核心数,其实对并发度并没有太大的意义。

接下来,分享一下做这个接入方式的时候遇到的一些问题,如下图所示:

1、消息大小的限制。

因为这里有一层 RPC ,在 RPC 请求过程中会有单次请求大小的限制;另外一方面是 RocketMQ 的 producer 里会有一个 MaxMessageSize 方法去控制消息不能超过这个大小; Broker 里也有一个参数,是 Broker 启动的配置,这个需要Broker重启,不然修改也不生效, Broker 里面有一个 DefaultAppendMessage 配置,是在启动的时候传进去对的参数,如果仅 NameServer 在线变更是不生效的,而且超过这个大小会报错。因为现在 RocketMQ 默认是 4M 的消息,如果将 RocketMQ 作为日志总线,可能消息体大小不是太够, Procuer 和 Broker 是都需要做变更的。

2、多连接的问题。

如果看 RocketMQ 源码会发现,多个 Producer 是共享一个底层的 MQ Client 实例的,因为一个 socket 连接吞吐是有限的,所以只会和Broker建立一个socket连接。另外,我们也有 socket 与 socket 之间是隔离的,可以通过 Producer 的 setIntanceName() ,当与 DefaultI Instance 的 name 不一样时会新启动一个 Client 的,其实就是一个新的 socket 连接,对于有隔离需求的、连接池需求得等,这个参数是有用的,在 4.5.0 上新加了一个接口是指定构造的实例数量。

3、超时设置。

因为多了一层 RPC ,那一层是有一个超时设置的,这个会有点不一样,因为我们的 RPC 请求里会带上超时设置的,客户端到 Proxy 有一个 RTT ,然后 Producer 到 Broker 的发送消息也是有一个请求响应延时,需要给 SDK 一个正确的超时语义。

4、如何选择一个合适的 reblance 算法,我们遇到这个问题是在双机房同城容灾的背景下,会有一边 Topic 的 MessageQueue 没有写入。

这种情况下, RocketMQ 自己默认的是按照平均分配算法进行分配的,比如有 10 个 Queue , 3 个 Proxy 情况, 1、2、3 是对应 Proxy1,4、5、6 是对应 Proxy2,7、8、9、10 是对应 Proxy3 ,如果在双机房同城容灾部署情况下,一般有一半 Message Queue 是没有写入的,会有一大部分 Consumer 是启动了,但是分配到的 Message Queue 是没有消息写入的。然后另外一个诉求是因为有跨机房的流量,所以他其实直接复用开源出来的 Consumer 的实现里就有根据 MachineRoom 去做 reblance ,会就近分配你的 MessageQueue 。

5、在 Proxy 这边需要做一个缓存,特别是拉消息的缓存。

特别提醒一下, Proxy 拉消息都是通过 Slave 去拉,不需要使用 Master 去拉, Master 的 IO 比较重;还有 Buffer 的管理,我们是遇到过这种问题的,如果只考虑 Message 数量的话,会导致 OOM ,所以要注意消息 size 的设置,

6、端到端压缩。

因为 RocketMQ 在消息超过 4k 的时候, Producer 会进行压缩。如果不在客户端做压缩,这还是涉及到 RPC 的问题, RPC 一般来说, Byte 类型,就是 Byte 数组类型它是不会进行压缩的,只是会进行一些常规的编码,所以消息体需要在客户端做压缩。如果放在 Proxy 这边做, Proxy 压力会比较大,所以不如放在客户端去承载这个压缩。

头条的容灾系统建设

前面大致介绍了我们这边大致如何接入 RocketMQ ,如何实现这么一套 Proxy ,以及在实现这套 Proxy 过程中遇到的一些问题。下面看一下灾难恢复的方案,设计之初也参考了一些潜在相关方案。

第一种方案:扩展集群,扩展集群的方案就像下图所示。

这是 master 和 slave 跨机房去部署的方式。因为我们有一层 proxy ,所以可以很方便的去做流量的调度,让消息只在一个主机房进行消息写入,不需要一个类似中控功能的实体存在。

第二种方案:类似 MySQL 和 Redis 的架构模式,即单主模式,只有一个地方式写入的,如下图所示。数据是通过 Mysql Matser/Slave 方式同步到另一个机房。这样 RocketMQ 会启动一个类似 Kafka 的 Mirror maker 类进行消息复制,这样会多一倍的冗余,实际上数据还会存在一些不一致的问题。

第三种方案:双写加双向复制的架构。这个结构太复杂不好控制,尤其是双向复制,其中消息区回环的问题比较好解决,只需针对在每个正常的业务消息,在 Header 里加一个标志字段就好,另外的 Mirror 发现有这个字段就把这条消息直接丢掉即可。这个链路上维护复杂而且存在数据冗余,其中最大问题是两边的数据不对等,在一边挂掉情况下,对于一些无法接受数据不一致的是有问题的。

此外,双写都是没有 Mirror 的方案,如下图所示。这也是我们最终选择的方案。我们对有序消息和无序消息的处理方式不太一样,针对无序消息只需就近写本机房就可以了,对于有序消息我们还是会有一个主机房,Proxy 会去 NameServer 拉取 Broker 的 Queue 信息, Producer 将有序消息路由到一个指定主机房,消费端这一侧,就是就近拉取消息。对于顺序消息我们会采取一定的调度逻辑保证均衡的分担压力获取消息,这个架构的优点是比较简单,缺点是当集群中一边挂掉时,会造成有序消息的无序,这边是通过记录消息 offset 来处理的。

此外,还有一种独立集群部署的,相当于没有上图中间的有序消息那条线,因为大多数有序消息是整体体系的,服务要部署单元化,比如某些 uid 、订单 Id 的消息或请求只会落到一边机房的,完全不用担心消息来得时候是否需要按照某些 key 去指定 MessageQueue ,因为过来的消息必定是隶属于这个机房的,也就是说中间有序消息那条线可以不用关心了,可以直接去掉。但是,这个是和整个公司部署方式以及单元化体系有关系的,对于部分业务我们是直接做到两个集群,两边的生产者、消费者、Broker 、Proxy 全部是隔离的,两边都互不发现,就是这么一套运行方式,但是这就需要业务的上下游要做到单元化的程度才可行。

mPass 企业租户微服务开发平台

喜欢阅读Spring、SpringBoot、SpringCloud等底层源码的可以关注下mPass 微服务开发平台,期待您的宝贵意见!

原文地址:https://www.cnblogs.com/lishangzhi/p/11773756.html

时间: 2024-08-30 03:06:35

今日头条在消息服务平台和容灾体系建设方面的实践与思考的相关文章

JEESZ-kafka消息服务平台实现

JEESZ的消息服务平台已经抛弃了之前的ActiveMQ,改用高吞吐量比较大的Kafka分布式消息中间件方案: JEESZ-kafka消息平台使用spring+kafka的集成方案,详情如下: 1. 使用最高版本2.1.0.RELEASE集成jar包:spring-integration-kafka 2. Zookeeper.Kafka分布式集群使用init.properties配置化方案. 3. 使用消息生产者spring-context-producer配置化方案. 4. 使用消息消费者sp

智能家居更该关注的是智能服务平台,而不是智能硬件设备

物联网IOT不仅是将一些设备连接在一起,还需要让它们能提供智能服务,以方便人们的生产.生活,提高效率,增加满意度. 物联网不是这两年才一下冒出来的,而是几十年市场技术积累改进的结果.比如工业上早就有工业自动化的说法,家庭在90年代也出现了音视频管理系统.这些都是今天我们所提的越来越红火的物联网的基石. 相比较车联网,智能家居要更复杂,难度更大.因为首先来说,汽车企业壁垒更高,往往由几个大企业控制中下游,比如通用.克莱斯勒.博世,行业标准也容易达成,而家庭电子产品,涉及的行业更多,尤其智能家居生活

【容灾】RTO和RPO

要建设容灾系统,就必须提出相应 的设计指标,以此作为衡量和选择容灾解决方案的参数. 目前,国际上通用的容灾系统的评审标准为Share 78,主要包括以下内容.  ●备份/恢复的范围  ●灾难恢复计划的状态  ●业务中心与容灾中心之间的距离  ●业务中心与容灾中心之间如何连接  ●数据是怎样在两个中心之间传送的  ●允许有多少数据丢失  ●保证更新的数据在容灾中心被更新  ●容灾中心可以开始容灾进程的能力  Share 78只是建立容灾系统的一种评审标准,在设计容灾系统时,还需要提供更加具体的设计

联鼎容灾备份一体机---容灾鼎

容灾鼎是将虚拟化技术.数据同步技术.数据容灾技术.高可用集群技术和高性能硬件进行无缝集成,并在出厂前完成预安装.预配置.预测试,从而简化现场部署流程,加快应用上线的速度.用户选择容灾鼎,就是选择了一体化的交付模式,用户无需分别选择软件.硬件再进行集成,只需关注自己的核心业务系统即可,高可用容灾一体化是未来的发展方向. 容灾鼎作为一款国内领先的一体化容灾核心设备,突破了传统业务系统软硬件分离式部署的模式,大大减低了系统部署的难度和操作复杂度, 极大地提高了系统整体可靠性, 使用户真正体验到"数据安

.net平台 基于 XMPP协议的即时消息服务端简单实现

.net平台 基于 XMPP协议的即时消息服务端简单实现 昨天抽空学习了一下XMPP,在网上找了好久,中文的资料太少了所以做这个简单的例子,今天才完成.公司也正在准备开发基于XMPP协议的即时通讯工具所以也算是打一个基础吧!如果你还没有了解过XMPP请先阅读附录中链接的文章,本实例是基agsXMPP上开发的,agsXMPP是C#写的支持开源XMPP协议软件,我们可以在agsXMPP上快速构建自已的即时通讯平台,我的这个例子只是修改了服务器端,因为agsXMPP本身自带的服务器端没有实现聊天功能.

今日头条架构演进之路

今天给大家分享今日头条架构演进,前面几位讲师讲了很多具体的干货,我的分享偏重基础设施及架构思路的介绍,我们想法是通过提供更好的基础设施,帮助架构做更好的迭代. 从架构的角度,技术团队应对的压力最主要来自三方面: 服务稳定性.接口的稳定性,让服务更可靠: 迭代速度.迭代速度对于大公司来讲相对没那么重要,规模比较大,生存压力相对小一点,但相对中型小型公司来讲,迭代速度是必须要保证的,时间窗也是一个决定能否成功的重要因素: 服务质量.主要关注用户满意度,它也是一个特别重要的 topic. 今日头条发展

Go -- 今日头条架构

夏绪宏,今日头条架构师,专注对高性能大规模 Web 架构,云计算.性能优化.编程语言理论等方向,PHP committer,HHVM 项目贡献者.2009 加入百度,先后从事大规模 IDC 自运维设施建设.云计算平台的架构设计.贴吧业务性能优化.百度通用 RPC 设计和优化等.2015 年加入今日头条负责基础设施,系统架构设计和优化,解决大流量高并发下的系统性能.可靠性和运维效率等方面的问题. 今天给大家分享今日头条架构演进,前面几位讲师讲了很多具体的干货,我的分享偏重基础设施及架构思路的介绍,

今日头条的核心架构解析

今日头条创立于2012年3月,到目前仅4年时间.从十几个工程师开始研发,到上百人,再到200余人.产品线由内涵段子,到今日头条,今日特卖,今日电影等产品线. 一.产品背景 今日头条是为用户提供个性化资讯客户端.下面就和大家分享一下当前今日头条的数据(据内部与公开数据综合): 5亿注册用户 2014年5月1.5亿,2015年5月3亿,2016年5月份为5亿.几乎为成倍增长. 日活4800万用户 2014年为1000万日活,2015年为3000万日活. 日均5亿PV 5亿文章浏览,视频为1亿.页面请

在刀尖舞蹈的今日头条,为什么要冒险“催熟”抖音?

最近的中国互联网充满了血腥味. 一边是出行市场的巨头与独角兽之争,一边是以抖音为代表的头条系在整个内容+社交领域的疯狂厮杀. 最近最常见的口径是,"抖音用500天挤走快手"."抖音绿了快手"."南抖音北快手"--而不管是骂还是夸,抖音都和快手牢牢挂上了钩. 的确,现在打开两款App里面内容几乎都是清一色的网红跳舞和搞笑小段子,两个平台背后也拥有非常相似的算法推荐逻辑.在经历品牌升级之后,抖音的Slogan变成了"记录美好生活"