为什么需要Orleans流?
已有多种技术可用于构建流处理系统。这些包括用于持久存储流数据的系统(例如,Event Hubs和Kafka),和流数据上表达计算操作的系统(例如,Azure Stream Analytics,Apache Storm和Apache Spark Streaming)。这些都是很棒的系统,可以帮助您构建高效的数据流处理管道。
现有系统的局限性
但是,这些系统不适用于细粒度的、自由形式的流数据的计算。上面提到的流计算系统,都允许您指定一个操作的统一数据流图,它以相同地方式应用于所有流项。当数据是一致的,且您希望对此数据表达相同的转换、过滤或聚合操作集时,这是一个强大的模型。但是在其他用例中,您需要对不同的数据项表达根本不同的操作。在其中的一些应用程序中,作为处理的一部分,您偶尔需要进行外部调用,例如调用一些任意REST API。统一的数据流处理引擎要么不支持这些场景,要么以有限和受限的方式支持它们,要么支持它们效率低下。这是因为它们本身针对大量类似的数据项进行了优化,并且通常在表现力、处理方面受到限制。Orleans 流针对的是其他场景。
动机
这一切都始于Orleans的用户,他们请求支持从grain方法调用返回一系列的条目。可以想象,这只是冰山一角。他们实际上需要的远不止这些。
Orleans 流的一个典型场景是,当您拥有每个用户的流,并且您希望在单个用户的上下文中,为每个用户执行不同的处理。我们可能有数百万计的用户,但其中一些用户对天气感兴趣,可以订阅特定位置的天气警报,而有些用户对体育赛事感兴趣;有人正在跟踪特定航班的状态。处理这些事件需要不同的逻辑,但您不希望运行两个独立的流处理实例。有些用户仅对特定库存感兴趣,并且只有在某些外部条件适用时,这些条件可能不一定是流数据的一部分(因此需要在运行时作为处理的一部分进行动态检查)。
用户始终在改变他们的兴趣,因此他们对特定事件流的订阅,会动态地来了又去了,因此,流拓扑会动态地、快速地变化。最重要的是,每个用户的处理逻辑,也会根据用户状态和外部事件,进行动态地进化和改变。外部事件可以修改特定用户的处理逻辑。例如,在游戏作弊检测系统中,当发现一种新的作弊方式时,需要用新规则更新处理逻辑,以检测这种新的违规行为。当然,这需要在不中断正在进行的处理流程的情况下完成。批量数据流处理引擎的构建不支持此类场景。
毫无疑问,一个这样的系统,必须在许多联网的机器上运行,而不是在单个节点上运行。因此,处理逻辑必须以可伸缩和弹性的方式,部署在服务器集群中。
新的要求
我们确定了流处理系统的4个基本要求,使其能够针对上述场景。
- 灵活的流处理逻辑
- 支持高动态拓扑
- 细粒度的流粒度
- 部署
灵活的流处理逻辑
我们希望系统支持表达流处理逻辑的不同方式。我们上面提到的现有系统,要求开发人员编写一个声明性的数据流计算图,通常是按照函数式编程风格编写的。这限制了处理逻辑的表现力和灵活性。Orleans流对处理逻辑的表达方式漠不关心。它可以表示为一个数据流(例如,使用在.NET中的Reactive Extensions(Rx));表示为一个功能程序;表示为一个声明性的查询;或者在一个一般的命令逻辑中。逻辑可以是有状态的或无状态的,可能有、也可能没有副作用,并且可以触发外部动作。所有的权力都交给了开发者。
支持动态拓扑
我们希望系统允许动态地进化拓扑。我们上面提到的现有系统,通常仅限于静态拓扑,它在部署时固定且不能在运行时进化。在下面的数据流表达式示例中,在您需要更改它之前,一切都很好且很简单。
Stream.GroupBy(x=> x.key).Extract(x=>x.field).Select(x=>x+2).AverageWindow(x, 5sec).Where(x=>x > 0.8) *
更改Where
过滤器中的阈值条件,添加额外的Select
语句,或在数据流图中添加另一个分支并生成新的输出流。在现有系统中,如果不拆除整个拓扑,并从头开始重新启动数据流,这是不可能的。实际上,那些现有的系统将检查现有的计算,并能够从最新的检查点重新启动。尽管如此,这种重新启动对于实时产生结果的在线服务来说是破坏性的,并且代价高昂。当我们考虑大量这样的表达式,以类似的方式被执行但参数(每用户,每个设备等)不同,并且参数不断变化时,这种重新启动变得特别地不切实际。
我们希望系统,允许在运行时通过向计算图中添加新的链接或节点,或通过更改计算节点内的处理逻辑,来进化流处理图。
细粒度的流粒度
在现有系统中,最小的抽象单元通常是整个流(拓扑)。但是,我们的许多目标场景,都要求拓扑中的单个节点/链路本身就是一个逻辑实体。这样,每个实体都可以独立管理。例如,在包括多个链路的大的流拓扑中,不同的链路可以具有不同的特性,并且可以在不同的物理传输上实现。一些链接可以通过TCP套接字,而其他链接可以通过可靠的队列。不同的链接可以有不同的交付保证。不同的节点可以具有不同的检查点策略,并且它们的处理逻辑可以用不同的模型或甚至不同的语言来表达。这种灵活性在现有系统中通常是不可能实现的。
抽象和灵活性参数的单位,类似于SoA(面向服务的体系结构)与Actors的比较。Actor系统允许更大的灵活性,因为每个系统本质上都是一个独立管理的“极小服务”。同样,我们希望系统允许这样的细粒度控制。
部署
当然,我们的系统应具有“良好的分布式系统”的所有属性。包括:
- 可伸缩性 - 支持大量流和计算元素。
- 弹性 - 允许根据负载添加/删除资源,来增长/收缩。
- 可靠性 - 对故障有弹性
- 效率 - 有效地利用潜在的资源
- 响应性 - 启用接近实时性的方案。
这些是我们为构建Orleans流而考虑的要求。
说明:Orleans目前不直接支持像上面的示例那样编写声明性数据流表达式。当前的Orleans流API是更低级别的构建块,如这里的描述。提供声明性数据流表达式是我们未来的目标。