时序数据流通过 Kafka 队列时可能产生的乱序缘由和解决方法

Kafka 做为一个流行的消息队列,以分布式高性能,高可靠性等特色已经在多种场景下普遍使用。在工业互联网、物联网时序数据存储的解决方案中也有大量用到。java

但在实际部署过程当中,可能会由于配置缘由致使通过 Kafka 的数据在接收方产生乱序,给后续处理环节带来排序等工做,形成没必要要的处理开销,下降系统的处理性能和额外排序的工做。git

其实能够经过合理的规划设计 Kafka 的配置和方法来避免消息在经过 Kafka 后乱序的产生,只须要遵循如下原则便可:对于须要确保顺序的一条消息流,发送到同一个 partition 上去github

Kafka 能够在一个 topic 下设置多个 partition 来实现分布式和负载均衡,由同一 consumer group 下的不一样 consumer 去消费;这样的机制可以支持多线程分布式的处理,带来高性能,但也带来了同一消息流走了不一样路径的可能性,若是没有针对性的规划,从架构上就没法保证消息的顺序。以下图所示,对于同一个 topic 的一条消息流,写入不一样的 partition,就会产生多条路径。web

为了确保一条消息流的数据可以严格按照时间顺序被消费,则必须遵循一条路径的原则,这样才能实现 FIFO(First In First Out)。

根据 Kafka 的文档描述,把哪条记录发到哪一个 partition,是由 producer 负责:算法

Producers数据库

Producers publish data to the topics of their choice. The producer is responsible for choosing which record to assign to which partition within the topic. This can be done in a round-robin fashion simply to balance load or it can be done according to some semantic partition function (say based on some key in the record). More on the use of partitioning in a second!缓存

可见,Kafka 已考虑到了确保消息顺序的需求,提供了接口来实现根据指定的 key 值发送到同一 partition 的方法。 能够看看 Kafka 相关源码:多线程

1class DefaultPartitioner(props: VerifiableProperties = null) extends Partitioner {
2  private val random = new java.util.Random
3  def partition(key: Any, numPartitions: Int): Int = {
4    Utils.abs(key.hashCode) % numPartitions
5  }
6}
复制代码

从源码上来看,Kafka 支持经过 Key 的 hash 值对 partition 的数量求余来实现基于 Key 的分配 partition 方法。所以咱们只要对不一样时序消息流,找到他们不一样的 key,而且这个 key 是不会发生变化的,那么就能在发送到 Kafka 的时候,确保每一条消息流发送到同一个 partition,走惟一的路径。所以咱们能够经过指定 Key 的方式,来实现这种严格的时序关系。架构

具体实现方法

在 TDengine 的应用场景下,咱们一般会把某一类设备(超级表)划分为一个 topic。对于每一个设备,会单独建表,一个设备产生的数据,会只放到一张表里。对于设备产生的原始数据,就须要在这个数据中找一个可以表明这个数据的 ID,并且不会发生变化的字段,做为 Key 值,在发送给 Kafka 时,带上这个 Key 值。这样就能确保该设备的全部数据流通过 Kafka 时,走惟一的路径。这个 ID 或 key 每每是设备具备惟一性的设备编码,这个编码不只能够做为 Kafka 的 Key, 也能够做为 TDengine 里的表名。app

具体实现很是简单,在 producer 发送数据时,选择一个 key,经过 KeyedMessage 方法生成消息,而后 send。以 Java 为例,其余语言能够从 Kafka 文档中找到相同功能的接口:

1 producer.send(new KeyedMessage<String, String>(topic,key,record))
复制代码

这个接口,可让使用者很是方便无需增长代码的状况下来实现指定每一个消息流绑定一个 partition 的结果。用户也能够经过本身实现一个 partition 的算法,来实现更精准的 partition 分配控制。具体实现能够参考"Kafka 指定 partition 生产,消费"中的介绍。

关于 TDengine

TDengine是涛思数据拥有自主知识产权的高性能、可伸缩、高可靠、零管理的物联网大数据平台软件,能够将数据库、缓存、消息队列、流式计算等功能彻底融合在一块儿。因为针对物联网大数据特色作了各类优化,TDengine的数据插入、查询的性能比通用的大数据平台好10倍以上,存储空间也大为节省,采用SQL接口,与第三方软件能无缝集成,大幅简化了物联网平台的系统架构,大幅减小了研发和运维的复杂度与成本。TDengine可普遍运用于物联网、车联网、工业大数据等领域。2019年7月12日,TDengine开源,在GitHub全球趋势排行榜上连续几天排名第一。

目前在GitHub上,TDengine的Star数已超10,000,GitHub地址:github.com/taosdata/TD… ,欢迎来GitHub上Star咱们!

相关文章
相关标签/搜索