两年前,腾讯在线教育部就在探索如何实现架构转型。在梳理过腾讯课堂初始技术架构的痛点后,规划出架构演进的三个重点方向:微服务、中间件、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:表示自动重置到 分区 的最小 offsetlatest:默认为 latest,表示自动重置到分区的最大 offsetnone:不自动进行 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 把分配给它的分区 分配给其他消费者。参数调优只能最大程度保证服务的可用,并不能保证服务的百分百可用。客户端需要具有捕获生产,消费等行为异常的行为。当出现异常时,能够告警,以便人工处理。这样才能最大的保证业务的高可用。

CKafka的其他优势


CKafka除了作为消息管道帮助业务实现数据解耦、流量削峰外,还可以在其他场景有所作为。

1. 日志分析系统

CKafka 结合大数据套件 EMR,可构建完整的日志分析系统。首先通过部署在客户端的 agent 进行日志采集,并将数据聚合到消息队列 CKafka,之后通过后端的大数据套件如 Spark 等进行数据的多次计算消费,并且对原始日志进行清理,落盘存储或进行图形化展示。

2. 流数据处理平台

CKafka 结合流计算 SCS , 可用于实时/离线数据处理及异常检测,满足不同场景需要:

  1. 对实时数据进行分析和展示,并做异常检测,快速定位系统问题。

  2. 消费历史数据进行落盘存储和离线分析,对数据进行二次加工,生成趋势报表等。

结语


腾讯云CKafka作为高性能、高吞吐量的消息中间件,为千万师生有序稳定的线上课堂提供了性能支撑,有效的解决了数据的实时性和可靠性问题。特别是在业务故障时,可实现快速扩缩容,并以其全面的容错机制和故障处理机制为用户提供解决方案。在2020年突如其来的疫情期间,CKafka将与腾讯课堂一起努力,为莘莘学子们百万级的课堂互动消息做好技术支撑,为构建良好的线上课堂体验贡献一份力量。