摘要: 这些话,是乔布斯给世间留下的真正伟大礼物 阅读全文
posted @ 2013-10-07 19:48 so... 阅读(186) 评论(0) 推荐(0) 编辑

Flink/Spark 如何实现动态更新作业配置

由于实时场景对可用性十分敏感,实时作业通常需要避免频繁重启,因此动态加载作业配置(变量)是实时计算里十分常见的需求,比如通常复杂事件处理 (CEP) 的规则或者在线机器学习的模型。尽管常见,实现起来却并没有那么简单,其中最难点在于如何确保节点状态在变更期间的一致性。目前来说一般有两种实现方式:

  • 轮询拉取方式,即作业算子定时检测在外部系统的配置是否有变更,若有则同步配置。
  • 控制流方式,即作业除了用于计算的一个或多个普通数据流以外,还有提供一个用于改变作业算子状态的元数据流,也就是控制流。

轮询拉取方式基于 pull 模式,一般实现是用户在 Stateful 算子(比如 RichMap)里实现后台线程定时从外部系统同步变量。这种方式对于一般作业或许足够,但存在两个缺点分别限制了作业的实时性和准确性的进一步提高:首先,轮询总是有一定的延迟,因此变量的变更不能第一时间生效;其次,这种方式依赖于节点本地时间来进行校准。如果在同一时间有的节点已经检测到变更并更新状态,而有的节点还没有检测到或者还未更新,就会造成短时间内的不一致。

控制流方式基于 push 模式,变更的检测和节点更新的一致性都由计算框架负责,从用户视角看只需要定义如何更新算子状态并负责将控制事件丢入控制流,后续工作计算框架会自动处理。控制流不同于其他普通数据流的地方在于控制流是以广播形式流动的,否则在有 Keyby 或者 rebalance 等提高并行度分流的算子的情况下就无法将控制事件传达给所有的算子。

以目前最流行的两个实时计算框架 Spark Streaming 和 Flink 来说,前者是以类似轮询的方式来实现实时作业的更新,而后者则是基于控制流的方式。

Spark Streaming Broadcast Variable

Spark Streaming 为用户提供了 Broadcast Varialbe,可以用于节点算子状态的初始化和后续更新。Broacast Variable 是一组只读的变量,它在作业初始化时由 Spark Driver 生成并广播到每个 Executor 节点,随后该节点的 Task 可以复用同一份变量。

Broadcast Variable 的设计初衷是为了避免大文件,比如 NLP 常用的分词词典,随序列化后的作业对象一起分发,造成重复分发的网络资源浪费和启动时间延长。这类文件的更新频率是相对低的,扮演的角色类似于只读缓存,通过设置 TTL 来定时更新,缓存过期之后 Executor 节点会重新向 Driver 请求最新的变量。

Broadcast Variable 并不是从设计理念上就支持低延迟的作业状态更新,因此用户想出了不少 Hack 的方法,其中最为常见的方式是:一方面在 Driver 实现后台线程不断更新 Broadcast Variavle,另一方面在作业运行时通过显式地删除 Broadcast Variable 来迫使 Executor 重新从 Driver 拉取最新的 Broadcast Variable。这个过程会发生在两个 micro batch 计算之间,以确保每个 micro batch 计算过程中状态是一致的。

比起用户在算子内访问外部系统实现更新变量,这种方式的优点在于一致性更有保证。因为 Broadcast Variable 是统一由 Driver 更新并推到 Executor 的,这就保证不同节点的更新时间是一致的。然而相对地,缺点是会给 Driver 带来比较大的负担,因为需要不断分发全量的 Broadcast Variable (试想下一个巨大的 Map,每次只会更新少数 Entry,却要整个 Map 重新分发)。在 Spark 2.0 版本以后,Broadcast Variable 的分发已经从 Driver 单点改为基于 BitTorrent 的 P2P 分发,这一定程度上缓解了随着集群规模提升 Driver 分发变量的压力,但我个人对这种方式能支持到多大规模的部署还是持怀疑态度。另外一点是重新分发 Broadcast Variable 需要阻塞作业进行,这也会使作业的吞吐量和延迟受到比较大的影响。

Broadcast Stream 是 Flink 1.5.0 发布的新特性,基于控制流的方式实现了实时作业的状态更新。Broadcast Stream 的创建方式与普通数据流相同,例如从 Kafka Topic 读取,特别之处在于它承载的是控制事件流,会以广播形式将数据发给下游算子的每个实例。Broadcast Stream 需要在作业拓扑的某个节点和普通数据流 (Main Stream) join 到一起。

Control Stream Topo

该节点的算子需要同时处理普通数据流和控制流:一方面它需要读取控制流以更新本地状态 (Broadcast State),另外一方面需要读取 Main Stream 并根据 Broadcast State 来进行数据转换。由于每个算子实例读到的控制流都是相同的,它们生成的 Broadcast State 也是相同的,从而达到通过控制消息来更新所有算子实例的效果。

目前 Flink 的 Broadcast Stream 从效果上实现了控制流的作业状态更新,不过在编程模型上有点和一般直觉不同。原因主要在于 Flink 对控制流的处理方式和普通数据流保持了一致,最为明显的一点是控制流除了改变本地 State 还可以产生 output,这很大程度上影响了 Broadcast Stream 的使用方式。Broadcast Stream 的使用方式与普通的 DataStream 差别比较大,即需要和 DataStream 连接成为 BroadcastConnectedStream 后,再通过特殊的 BroadcastProcessFunction 来处理,而 BroadcastProcessFunction 目前只支持 类似于 RichCoFlatMap 效果的操作。RichCoFlatMap 可以间接实现对 Main Stream 的 Map 转换(返回一只有一个元素的集合)和 Filter 转换(返回空集合),但无法实现 Window 类计算。这意味着如果用户希望改变 Window 算子的状态,那么需要将状态管理提前到上游的 BroadcastProcessFunction,然后再通过 BroadcastProcessFunction 的输出来将影响下游 Window 算子的行为。

总结

实时作业运行时动态加载变量可以令大大提升实时作业的灵活性和适应更多应用场景,目前无论是 Flink 还是 Spark Streaming 对动态加载变量的支持都不是特别完美。Spark Streaming 受限于 Micro Batch 的计算模型(虽然现在 2.3 版本引入 Continuous Streaming 来支持流式处理,但离成熟还需要一定时间),将作业变量作为一致性和实时性要求相对低的节点本地缓存,并不支持低延迟地、低成本地更新作业变量。Flink 将变量更新视为特殊的控制事件流,符合 Even Driven 的流式计算框架定位,目前在业界已有比较成熟的应用。不过美中不足的是编程模型的易用性上有提高空间:控制流目前只能用于和数据流的 join,这意味着下游节点无法继续访问控制流或者需要把控制流数据插入到数据流中(这种方式并不优雅),从而降低了编程模型的灵活性。个人认为最好的情况是大部分的算子都可以被拓展为具有 BroadcastOperator,就像 RichFunction 一样,它们可以接收一个数据流和一个至多个控制流,并维护对应的 BroadcastState,这样控制流的接入成本将显著下降。

参考文献

1.FLIP-17 Side Inputs for DataStream API
2.Dynamically Configured Stream Processing: How BetterCloud Built an Alerting System with Apache Flink®
3.Using Control Streams to Manage Apache Flink Applications
4.StackOverFlow - ow can I update a broadcast variable in spark streaming?

posted @ 2019-02-11 17:55 so... 阅读(5459) 评论(0) 推荐(1) 编辑
摘要: FinalShell:http://www.hostbuf.com/ 阅读全文
posted @ 2017-10-27 09:53 so... 阅读(200) 评论(0) 推荐(0) 编辑
摘要: 1、权限过滤Lucene的基于关键字的评分机制,适用于基于相关度的过滤和排序。它是基于矢量模型,其中给文档分配一个相应的分数,分数越高相关的文档也越多。然而,应用系统有时因为用户级权限仅需返回相关文档的子集。过滤的权限问题实际上是查询时将一个布尔过滤器作用于文档的普遍问题的子问题。我们将探讨这种过滤... 阅读全文
posted @ 2015-09-07 11:38 so... 阅读(599) 评论(0) 推荐(0) 编辑
摘要: package com.lid;import java.util.Calendar;import java.util.Date;import java.util.Timer;import java.util.TimerTask;public class Test { public static... 阅读全文
posted @ 2015-09-06 10:39 so... 阅读(268) 评论(0) 推荐(0) 编辑
摘要: “Server.UrlDecode(Server.UrlEncode("北京")) == “北京””,先用UrlEncode编码然后用UrlDecode解码,这条语句永远为true吗?答案是否定的,结果可能与很多人预想的不大一样。本文主要分析这一问题出现的原理,研究下Server.UrlEncode... 阅读全文
posted @ 2015-08-28 14:55 so... 阅读(375) 评论(0) 推荐(0) 编辑
摘要: 1. 浏览器解码 浏览器根据服务器页面响应Header中的“Content-Type: text/html;charset=gb2312”解码。修改web.config中“responseEncoding=utf-8”,发现服务器页面响应Header变成了“Content-Type: tex... 阅读全文
posted @ 2015-08-28 14:53 so... 阅读(638) 评论(0) 推荐(0) 编辑
摘要: 在采用Hibernate做对象映射时,我一直都采用UUID来做主键。由于Hibernate的UUID需要占用32位的字符,所以一般都会让人感觉响效率且增加存储占用。 我在查看公司项目时发现了一种比较好的生成UUID的方法,就是将UUID数据进行Base64化。觉得比较有意义拿出来给大家分享。 阅读全文
posted @ 2015-08-25 20:04 so... 阅读(936) 评论(0) 推荐(0) 编辑
摘要: Spring启动后执行三种方法 阅读全文
posted @ 2015-06-05 10:59 so... 阅读(345) 评论(0) 推荐(0) 编辑
摘要: oracle 将一个字符串分割成多行 阅读全文
posted @ 2015-04-02 20:14 so... 阅读(3187) 评论(0) 推荐(0) 编辑
摘要: 当前java程序中 能够实现定时的 主要有 三种 方式 ,分别是: java定时器 , spring定时器 , quartz定时器. 阅读全文
posted @ 2015-03-19 14:46 so... 阅读(498) 评论(0) 推荐(0) 编辑
摘要: java当中的定时器的4种使用方式 阅读全文
posted @ 2015-03-18 11:34 so... 阅读(468) 评论(0) 推荐(0) 编辑
摘要: 创建数据库失败((Microsoft.SqlServer.Smo))执行Transact-SQL语句或批处理时发生了异常。 阅读全文
posted @ 2015-03-17 11:02 so... 阅读(6205) 评论(0) 推荐(1) 编辑
摘要: 1.active 处于此状态的会话,表示正在执行,处于活动状态。 2.killed 处于此状态的会话,表示出现了错误,正在回滚,当然,也是占用系统资源的。还有一点就是,killed的状态一般会持续较长时间,而且用windows下的工具pl/sql developer来kill掉,是不管用的,要用命令:alter system kill session 'sid,serial#' ; 3.inactive 处于此状态的会话表示不是正在执行的,比如select语句已经完成。我一开始以为,只要是inactive状态的会话,就是该杀,为什么不释放呢。其实,inactive对数据库本身没有什么影响,但是如果程序没有及时commit,那么就会造成占用过多会话 阅读全文
posted @ 2015-02-25 11:42 so... 阅读(591) 评论(0) 推荐(0) 编辑
摘要: Delegate是Dotnet1.0的时候已经存在的特性了,但由于在实际工作中一直没有机会使用Delegate这个特性,所以一直没有对它作整理。这两天,我再度翻阅了一些关于Delegate的资料,并开始正式整理这个C#中著名的特性。本文将由浅入深的谈一下Delegate这个特性。 阅读全文
posted @ 2015-02-25 11:10 so... 阅读(403) 评论(0) 推荐(0) 编辑
摘要: Weblogic 线程处理的默认时间为600s,StuckThreadMaxTime:600。在运行一些将长时间的程序时经常会由于请求时间过长,导至超时。报出more than the configured time (StuckThreadMaxTime) of "600" seconds错误。或是由于发送该请求较多,很有可能会导致weblogic的线程阻塞,严重会引起weblogic挂起现象。 阅读全文
posted @ 2015-01-21 12:54 so... 阅读(2983) 评论(0) 推荐(0) 编辑
摘要: 首先分析产生此错误是由于 WebLogic Server 超过了其“阻塞线程最长时间:”的默认值:600 秒。 考虑将 WebLogic Server 的“阻塞线程最长时间”的值从默认的 600 秒更改为更大的值,例如 1200 秒。 阅读全文
posted @ 2015-01-21 12:52 so... 阅读(10215) 评论(0) 推荐(0) 编辑
摘要: Commons - BeanUtils 提供了很多功能,其中一个很有用的是对对象集合进行排序,如Collections.sort(peoples, new BeanComparator("age")); 另外,可以使用java.util.Collections的sort方法可以对collection集合进行排序,包括多列组合排序,下面是自己实现java.util.Comparator,定制对象属性排序规则的例子: 阅读全文
posted @ 2015-01-16 15:07 so... 阅读(2062) 评论(0) 推荐(0) 编辑
摘要: SpringMVC + maven架构环境时: Error configuring application listener of class org.springframework.web.context.ContextLoaderListener解决方案 阅读全文
posted @ 2015-01-06 15:24 so... 阅读(495) 评论(0) 推荐(0) 编辑
摘要: V$SESSION是基础信息视图,用于找寻用户SID或SADDR。不过,它也有一些列会动态的变化,可用于检查用户。 阅读全文
posted @ 2015-01-05 15:14 so... 阅读(1298) 评论(0) 推荐(0) 编辑
摘要: Memcached是一个免费开源的,高性能的,具有分布式对象的缓存系统,它可以用来保存一些经常存取的对象或数据,保存的数据像一张巨大的HASH表,该表以Key-value对的方式存在内存中。 阅读全文
posted @ 2015-01-05 14:04 so... 阅读(142) 评论(0) 推荐(0) 编辑
点击右上角即可分享
微信分享提示