导语 | 疫情期间,为了保障国内学子的正常学习进度,腾讯课堂积极响应国家“停工不停学”的号召,紧急上线疫情期间专用的“老师极速版”,使广大师生足不出户,即可快速便捷的完成线上开课。面对线上课堂百万量级的互动消息,如何保证消息的实时性和准确性无疑是一个技术挑战。那么如何解决问题呢?接下来,就和小编一起来看看腾讯云中间件 CKafka 如何为腾讯课堂百万级消息提供技术支撑。
两年前,腾讯在线教育部就在探索如何实现架构转型。在梳理过腾讯课堂初始技术架构的痛点后,规划出架构演进的三个重点方向:微服务、中间件、DevOps。
尤其在消息中间件的选取上,从自研 Hippo 消息队列切换到云 CKafka。这主要归于以下几点原因:
实现技术栈的统一,降低组件适配成本。
使用符合开源标准的组件,便于系统切换优秀的开源组件。
CKafka具备高性能、高可用性和高可靠性的特点:免除复杂的参数配置,提供专业的性能调优;磁盘高可靠,即使服务器坏盘50%也不影响业务;多副本备份,更有多可用区容灾方案可选,零感知服务迁移。
CKafka提供安全的数据保障:提供鉴权与授权机制、主子账号等功能,为企业数据做好安全防护。
在刚刚过去的 2019 年,腾讯在线教育部已全面实现了业务上云,不仅提升了团队研发效率,还实现了快速交付。同时,CKafka 在消息流处理上的高性能特点得以实践验证。
而现在,疫情当前,面对全国千万师生同时在线的线上课堂,互动消息猛增至百万级别,无疑对在线教育平台的稳定性提出更高要求。
为了保证线上课堂广大师生的稳定互动,CKafka 作为腾讯课堂的底层消息支撑,在消息的实时性和可靠性上提供了更优化的技术方案。
腾讯课堂在线课程页面
CKafka 在腾讯课堂的实践
Ckafka 在腾讯课堂系统架构中的应用是非常典型的场景,即消息总线和业务解耦。使用了 GB 带宽、TB 存储规格的实例。
我们先来看一下 CKafka 作为消息总线在腾讯课堂的架构中所处的位置,如下图:
从架构图可知,CKafka 处于消息管道的中心位置。同时接收多个消息源数据,等待下游组件的订阅消费。
并利用其自身高分布式、高吞吐、高可靠的特性,实现流量削峰和业务解耦。腾讯课堂中的聊天消息、签到、举手、献花、答题卡等功能都使用了该能力。
在线课堂的业务场景不允许出现如消息延迟、数据丢失等情况,否则就会立刻被在线课堂的师生们感知业务的不稳定,造成不良的用户体验。
我们来假设一个场景:老师在课堂发布一个问题,学生们举手回答问题,如果老师发出的消息出现延时或丢失的情况,学生们就不能收到消息,无法及时给老师反馈问题答案,线上课堂的互动效果就会很差,严重影响课堂教学质量。
如何避免上述问题呢?我们从 CKafka 保障消息的实时性和可靠性两方面进行阐述。
1. 消息的实时性
Apache Kafka 架构上设计的底层数据读写和存储是以分区为最小单位进行的。首先来看一下 Kafka Topic 的生产消费模型。
如上图所示,生产者将数据写入到分布在集群内不同节点的不同分区上,一个或多个消费者从多台 Borker 的分区上消费订阅数据。
从这个模型可知,如果数据的读写都集中在单个分区上,则 Topic 的的所有压力都会集中在该分区上,从而落到单台 Broker 上面。
假设单台机器能承受的流量是 300MB,则此时以腾讯教育的 GB/s 的流量规模,则会出现消息处理过慢,会导致消息延时。
那怎么办呢?此时就应该提升分区的数量,提高数据处理的并行度,从而将整个 topic 的压力均分到多台机器上。
这时就会有一个疑问,Topic 需要多少分区合适呢?是不是越多的分区越好呢?
(1)影响分区数量的因素
从生产者的角度来看,数据向不同的分区写入是完全并行的;从消费者的角度来看,并发数完全取决于分区的数量(如果 consumer 数量大于 分区 数量,则必有 consumer 闲置)。
因此选取合适的分区对于发挥 CKafka 实例的性能十分重要。
Topic 的分区数量是由多种因素决定的,一般可以根据以下几个因素综合考虑:
生产者的峰值带宽
假设单机单partiton的生产消费吞吐各自最高为300,峰值生产带宽是900MB,则单纯生产至少需要3个Partiton。
消费者的峰值带宽
有人可能会觉得消费的峰值带宽应该等于生产的峰值带宽。这样是不对的。生产者只会生产一份数据,但是可以有N个消费者消费同一份数据,则此时消费带宽=N*生产带宽。
另外如果是离线计算,可能会在某一时刻,消费历史所有数据,此时消费带宽可能会远远高于生产带宽。此时如果Topic只设计3个分区就有问题了。假设消费峰值带宽是生产带宽的2倍。则此时至少需要6个分区。
消费者的处理能力
假设创建了6个分区。此时6个分区最多只会有6个消费者,每个消费者最多每秒可以从Kafka Server拉到300MB的数据。
但是每个消费者因为还需处理业务逻辑的关系,只能消费100MB的数据,这样就会容易导致出现消费堆积的情况。
为了增大消费能力,则需要多加入消费者。因为Kafka的consumer group机制里同一个消费组里同一个分区只能被一个消费者消费。所以,就应该增大分区的数量。
为满足如上需求,此时至少需要18个分区,18个消费者,才能满足消费需求。
在上面的Case中,分区数的设计也需要存在一定的冗余,因为很多情况下,性能是无法达到最优的。
所以,分区数量需要综合考虑多个因素,可以适当的多一点分区数量,以提高实例的性能。但也不能太大,太大也会导致一系列的其他问题。
(2)选取合适的分区数量
考虑到上面提到的实际因素,是否有一个相对简单的判断方法来设计分区数量呢?
在理想情况下,可以通过如下公式来判断分区的数目:
Num = max( T/PT , T/CT ) = T / min( PT , CT )
其中,Num 代表分区数量,T 代表目标吞吐量,PT 代表生产者写入单个 分区 的最大吞吐,CT 代表消费者从单个分区消费的最大吞吐。则分区数量应该等于 T/PT 和 T/CT 中的较大值。
在实际情况中,生产者写入分区的最大吞吐 PT 的影响因素和批处理的规模、压缩算法、确认机制、副本数等有关。
消费者从单个分区消费的最大吞吐 CT 的影响因素和业务逻辑有关,需要在不同场景下实测得出。
通常建议分区的数量一定要大于等于消费者的数量来实现最大并发。如果消费者数量为 5,则分区的数目也应该 ≥ 5 的。
但需要注意的是:过多的分区会导致生产吞吐的降低和选举耗时的增加,因此也不建议过多分区。
提供如下信息供参考:
单个分区是可以实现消息的顺序写入的。
单个分区只能被同消费者组的单个消费者进程消费。
单个消费者进程可同时消费多个分区,即分区限制了消费端的并发能力。
分区越多,当Leader节点失效后,其他分区重新进行Leader选举的耗时就会越长。
分区的数量是可以动态增加的,只能增加不能减少。但增加会出现消息 rebalance 的情况。
在上述方法的基础上,我们还综合考虑了腾讯课堂的生产消费峰值带宽、消费的行为特征和单个消费者的消费能力等因素,为其设计了合理的分区数量,以满足其对消息实时性的要求。
2. 消息的可靠性
消息的可靠性从不同的角度看是不一样的。
从 Apache Kafka 自身角度看来,消息的可靠性是消息的可靠存储。从业务的角度来看,消息的可靠性是指消息传输、存储、消费的可靠性。
从服务提供商来看,我们希望消息的可靠性是站在客户这一边的,即可靠的传输,存储,消费。
CKafka 在做好可靠性存储的基础上,还从配置调优、异常告警等方面尽量做到消息的可靠传输和消费。
(1)关于副本
在介绍下面的方案前,我们先聊聊一下副本。为什么要有副本的存在呢?
在分布式的场景下,数据损坏和机械故障是被认为常见事件。
数据副本是指在不同节点上持久化同一份数据,当某一个节点上存储的数据丢失时,可以从副本上读取该数据,这是解决分布式系统数据丢失问题最为有效的方法。
那么我们来思考下:有多少个副本的数据才是安全的?
理论上 2 个副本就可以大概率范围的保证数据安全,但是当两个副本都损坏时,数据也会丢失,此时就需要更多的副本,或者需要副本跨可用区、跨地域分布。
当然更多的副本就意味着要存储更多的数据,需要更高的成本投入。所以用户需要在冗余和安全之间权衡出一种平衡。这也是腾讯云上创建 topic 需要用户指定副本数量的原因,如下图:
(2)服务端的可靠性
假设 TopicA 有 3 个分区,每个分区有三个副本。来看一下如下的 Topic 分区分布示意图。
如图所示,三个分区和三个副本均匀的分布在三个 Broker 中,每台 Broker 分布了一个分区的 Leader 分区。
从上一节关于副本的描述可知,除非所有的 Broker 在同一时间挂掉,否则即使同时挂掉 2 台 Borker,服务也可以正常运行。
而在我们当前的运营架构中,三台 broker 同时挂掉的概率微乎其微,当然如果真的出现这种情况,那就是整个机房挂掉了。
为了避免整个机房挂掉的情况,腾讯云 Ckafka 也可以配置跨机房容灾和跨可用区容灾,来保证数据的可靠性。
我们可以通过参数配置来尽可能的保证可靠性传输和消费,用告警来做兜底策略,让研发感知介入处理。下面来看一下生产和消费端的参数调优。
(3)客户端参数调优
生产的可靠传输,主要来看一下如下三个配置: ack、retries。
ack
Kafka producer 的 ack 有 3 种机制,分别说明如下:
-1:Broker 在 leader 收到数据并同步给所有 ISR 中的 follower 后,才应答给 Producer 继续发送下一条(批)消息。这种配置提供了最高的数据可靠性,只要有一个已同步的副本存活就不会有消息丢失。
0:生产者不等待来自 broker 同步完成的确认,继续发送下一条(批)消息。这种配置生产性能最高,但数据可靠性最低(当服务器故障时可能会有数据丢失) 。
1:生产者在 leader 已成功收到的数据并得到确认后再发送下一条(批)消息。这种配置是在生产吞吐和数据可靠性之间的权衡(如果leader已死但是尚未复制,则消息可能丢失)
用户不显式配置时,默认值为1。如果是需要可靠性要求高的,建议设置为-1。设置为-1会影响吞吐的性能。
retries
请求发生错误时重试次数,建议将该值设置为大于0,失败重试最大程度保证消息不丢失。
消费的稳定,看一下以下配置,主要避免重复消费和频繁的消费组Rebalance:
auto.offset.reset
表示当Broker端没有offset(如第一次消费或 offset超过7天过期)时如何初始化 offset。earliest:表示自动重置到 分区 的最小 offset
latest:默认为 latest,表示自动重置到分区的最大 offset
none:不自动进行 offset 重置,抛出 OffsetOutOfRangeException 异常
默认值为latest。当设置为earliest的时候,需要注意的是:当offset失效后,就会从现存的最早的数据开始消费的情况,可能会出现数据重复消费的情况。
session.timeout.ms
使用 Kafka 消费分组机制时,消费者超时的时间。当 Broker 在该时间内没有收到消费者的心跳时,就会认为该消费者发生故障,Broker 发起重新 Rebalance 过程。
目前该值的在 Broker 的配置必须在group.min.session.timeout.ms=6000和group.max.session.timeout.ms=300000 之间。
heartbeat.interval.ms
使用 Kafka 消费分组机制时,消费者发送心跳的间隔。这个值必须小于 session.timeout.ms,一般小于它的三分之一。
max.poll.interval.ms
使用 Kafka 消费分组机制时,再次调用 poll 允许的最大间隔。如果在该时间内没有再次调用 poll,则认为该消费者已经失败,Broker 会重新发起 Rebalance 把分配给它的分区 分配给其他消费者。
参数调优只能最大程度保证服务的可用,并不能保证服务的百分百可用。
客户端需要具有捕获生产,消费等行为异常的行为。当出现异常时,能够告警,以便人工处理。这样才能最大的保证业务的高可用。
本文转载自云加社区公众号。
原文链接:https://mp.weixin.qq.com/s/nmDUiyTMwI3ntNNHgAaJOw
评论