随着诸如Apache Flink,Apache Spark,Apache Storm之类的开源框架以及诸如Google Dataflow之类的云框架的增多,创建实时数据处理作业变得非常容易。这些API定义明确,并且诸如Map-Reduce之类的标准概念在所有框架中都遵循几乎相似的语义。
但是,直到今天,实时数据处理领域的开发人员都在为该领域的某些特性而苦苦挣扎。因此,他们在不知不觉中创建了一条路径,该路径导致了应用程序中相当常见的错误。
让我们看一下在设计实时应用程序时可能需要克服的一些陷阱。
活动时间
源生成数据的时间戳称为“ 事件时间”,而应用程序处理数据的时间戳称为“ 处理时间”。在实时数据流应用程序中,最常见的陷阱是无法区分这些时间戳。
让我们详细说明一下。
由于诸如代理中的GC较高或太多数据导致背压之类的多个问题,数据队列易出现延迟。我将事件表示为(E,P),其中E是事件时间戳(HH:MM:SS格式),P是处理时间戳。在理想世界中,E == P,但这在任何地方都不会发生。
假设我们收到以下数据
('05:00:00','05:00:02'),('05:00:01','05:00:03'),('05:00:01','05:00: 03'),('05:00:01','05:00:05'),
('05:00:02','05:00:05'),('05:00:02',' 05:00:05')
现在,我们假设有一个程序可以计算每秒接收到的事件数。根据事件时间,程序返回
[05:00:00,05:00:01)= 1
[05:00:01,05:00:02)= 3
[05:00:02,05:00:03)= 2
但是,基于处理时间,输出为
[5时○○分00秒,5点00分01秒)= 0
[5点00分01秒,5点00分02秒)= 0
[5点00分02秒,5时00分03秒)= 1
[05:00: 03,05:00:04)= 2
[05:00:04,05:00:05)= 0
[05:00:05,05:00:06)= 3
如您所见,这两个都是完全不同的结果。
数据流中异常的延迟
大多数实时数据应用程序使用来自分布式队列的数据,例如Apache Kafka,RabbitMQ,Pub / Sub等。队列中的数据由其他服务生成,例如消费者应用程序的点击流或数据库的日志。
问题队列容易受到延迟的影响。即使在几十毫秒内,生成的事件也可能到达您的工作中,或者在最坏的情况下可能会花费一个多小时(极高的背压)。由于以下原因,数据可能会延迟:
- kafka上的高负载
- 生产者在其服务器中缓冲数据
- 由于应用程序中的背压,消耗速度慢
假设数据将永远不会延迟是一个巨大陷阱。开发人员应始终具有测量数据延迟的工具。例如,在Kafka,您应该检查偏移量滞后。
您还应该监视作业中的背压以及延迟(即事件时间与处理时间之间的差)。没有这些将导致数据意外丢失,例如10分钟。时间窗口似乎没有数据,并且窗口显示10分钟。之后,其期望值将是预期值的两倍。
Joins
在批处理数据处理系统中,将两个数据集合并起来比较简单。在流处理世界中,情况变得有些麻烦。
//数据集的格式为(时间戳,键,值)
//数据组1 (05:00:
00,A,值A),
(05:00: 01,B,值B),(05:00: 04,C,值C),(05:00:04,D,值D)
//数据流2
(05:00:00,A,值A'),(05:00:02,B,值B' ),
(05:00:00,C,值C')
现在,我们将两个数据流都放在它们的Key上。为简单起见,我们将进行内部联接。
Key A — 值A和值A'都同时到达。因此,我们可以轻松地将它们组合为一个函数并发出输出
Key B — 值B比值B`早1秒。因此,我们需要在数据流1上等待至少1秒钟,才能使连接正常工作。因此,您需要考虑以下内容-
- 那一秒钟的数据将存储在哪里?
- 如果1秒不是固定的延迟,并且在最坏的情况下不规则地增加到10分钟怎么办?
Key C —值C比值C'晚4秒钟到达。这与以前相同,但是现在您在数据流1和2中都具有不规则的延迟,并且没有固定的模式将其值设为1。
Key D —值D到达,但是没有观察到值D'。考虑以下-
- 您要等多久才能获得价值D`?
- 如果值D`可以从至少5秒到接近1小时的任何时间出现,该怎么办?
- 如果这是一个外部联接,而您必须决定何时单独发出值D,该怎么办?
- 如果在前一种情况下,在发出值D 1分钟后到达值D`,该怎么办?
以上所有问题的答案将取决于您的用例。重要的是要考虑所有这些问题,而不是忽略流系统的复杂性。
一定要注意 不要回避这些问题
配置
在标准微服务中,配置位于作业内部或数据库中。您可以在数据流应用程序中执行相同的操作。但是,在继续使用此方法之前,您需要考虑以下事项。
您将多久访问一次配置?
如果需要为每个事件访问配置,并且事件数量很多(超过一百万RPM),那么您也可以尝试其他方法。一种是将配置存储在作业状态中。这可以使用状态处理在Flink和Spark中完成。可以使用文件读取器或Kafka中的其他流以状态填充该配置。
在流处理世界中,针对每个事件进行数据库调用可能会使您的应用程序变慢并导致背压。选择是使用快速数据库,还是通过在应用程序内部存储状态来消除网络调用。
您的配置有多大?
如果配置很大,则仅当配置可以拆分到多个服务器时才应使用应用程序内状态,例如,一个配置为每个用户保留一些阈值。可以基于用户ID密钥将这样的配置拆分到多台计算机上。这有助于减少每台服务器的存储量。
如果无法在节点之间拆分配置,请首选数据库。否则,所有数据将需要路由到包含配置的单个服务器,然后再次重新分发。唯一包含配置的服务器充当该方案的瓶颈。
设计实时数据流应用程序似乎很容易,但是开发人员会犯很多上述错误,特别是如果它们来自微服务领域。
重要的部分是了解数据流的基础知识以及如何处理单个流,然后转到处理多个联接,实时配置更新等的复杂应用程序。
更多实时数据分析相关博文与科技资讯,欢迎关注 “实时流式计算”