StreamInsight 服务器概念
本主题介绍如何表示和操作数据,如何将数据传入和传出 StreamInsight 服务器。它旨在让您熟悉与 Microsoft StreamInsight 中的复杂事件处理相关的基本概念。本主题从描述数据结构开始,然后描述作用于数据或处理数据的 StreamInsight 服务器组件。
流
StreamInsight 中的所有数据组织成流。每个流描述随时间变化的某个可能未结束的数据集合。例如,股票行情显示器流提供所交易的不同股票随时间变化的股价,温度传感器流提供温度传感器随时间推移报告的温度值。
考虑一个电力监控方案,该方案的目的是监控用于测量各种设备用电情况的电表集合。这些电表定期传送数据,包括用电量(瓦数除以 10)以及与读数关联的时间戳。下表显示的是来自三个电表的用电读数,假设每个电表每秒发出一个用电读数。
时间 |
电表编号 |
用电量 |
---|---|---|
2009-07-27 10:27:23 |
1 |
100 |
2009-07-27 10:27:24 |
1 |
200 |
2009-07-27 10:27:51 |
2 |
300 |
2009-07-27 10:28:52 |
2 |
100 |
2009-07-27 10:27:23 |
3 |
200 |
因为此信息可以表示成随时间变化的值,所以可以用一个流来表示数据。以此流中的数据为例,针对此流的查询可以返回在给定时期内用电量最高或最低的电表,或返回随时间变化的前 10 个用电量最高的电表列表。
事件
流中表示的基础数据打包成事件。事件是 StreamInsight 服务器处理的基本数据单位。每个事件由以下部分组成:
标题。事件标题包含元数据以及一个或多个时间戳,其中元数据用于定义事件类型,时间戳用于定义事件的时间间隔。时间戳基于应用程序,由数据源提供,并非是 StreamInsight 服务器提供的系统时间。请注意,时间戳使用 DateTimeOffset 数据类型,此类型可分辨时区,并且基于 24 小时时钟。StreamInsight 服务器将所有时间规范化为 UTC 日期时间,并验证输入中时间戳字段是否设置了 UTC 标志。
负载。一种 .NET 数据结构,它保留与事件关联的数据。负载中定义的字段是用户定义的,其类型基于 .NET 类型系统。
流中的事件(其应用程序时间戳对应于它们到达查询的顺序)被称为“有序”。如果不是这样,事件被称为“无序”到达。StreamInsight 服务器保证,无论事件是否有序到达,查询输出都一样,除非查询编写者明确指定采用另外方式。在流中,通常的事件到达模式有:
稳定速度,如来自文件或表格的记录。
间歇和随机速度,如来自零售条码扫描仪的数据。
具有突然爆发状态的间歇速度,如 Web 点击操作或气象遥测。
事件标题
事件标题定义事件的类型和事件的临时属性。
事件类型
事件类型指示事件是流中的新事件,还是正在声明流中现有事件的完整性。StreamInsight 支持两种事件类型:INSERT 和 CTI(当前时间增量)。
INSERT 事件类型将事件及其负载添加到事件流中。除了负载,INSERT 事件的标题还标识事件的开始和结束时间。下图显示一个 INSERT 事件类型的布局。
标题 |
负载 |
---|---|
事件类型 ::= INSERT 开始时间 ::= DateTimeOffset 结束时间 ::= DateTimeOffset |
字段 1 … 字段 n 作为 CLR 类型 |
CTI 事件类型是一种特殊标点事件,用来指示流中现有事件的完整性。CTI 事件结构由提供当前时间戳的单个字段组成。CTI 事件具有两个用途:
首先,它使查询能够接受和处理其应用程序时间戳与其在查询中到达顺序不符的事件。当发出 CTI 事件时,该事件向 StreamInsight 服务器指示,任何后续传入 INSERT 事件都不会修改此 CTI 时间戳之前的事件历史记录。也就是说,在发出某个 CTI 事件后,任何 INSERT 事件的开始时间都不能早于该 CTI 事件的时间戳。通过这样指示事件流的“完整性”,StreamInsight 服务器可以释放开窗运算符或其他累积了状态的聚合运算符的结果,从而确保事件流高效地通过系统。
CTI 事件的第二个作用是保持查询的滞后时间较短。频繁的 CTI 将使查询以更高的频率推送出结果。
重要提示 |
---|
如果输入流中不存在 CTI 事件,则不会从查询生成任何输出。 |
有关详细信息,请参阅将应用程序时间提前。
下图显示 CTI 事件类型的布局。
标题 |
---|
事件类型 ::= CTI 开始时间 ::= DateTimeOffset |
事件模型
事件模型基于事件的临时特征定义其形状。StreamInsight 支持三种事件模型:间隔、点和边缘。间隔事件可视作最一般的类型,而边缘和点是特殊事例。
间隔
间隔事件模型表示其负载在给定时段内有效的事件。间隔事件模型要求在事件元数据中同时提供事件的开始时间和结束时间。间隔事件只在此特定时间间隔内有效。务必要记住的是,就事件负载的有效性而言,包含事件开始时间,但是不包含结束时间。
下图显示间隔事件模型的布局。
元数据 |
负载 |
---|---|
事件类型 ::= INSERT 开始时间 ::= DateTimeOffset 结束时间 ::= DateTimeOffset |
字段 1 … 字段 n 作为 CLR 类型 |
间隔事件的示例包括:电脉冲的宽度,拍卖竞标的持续时间(有效性),股票买入价在特定时期内有效的股票行情显示器活动等。在上述电力监控示例中,可以使用下列间隔事件来表示电表事件流。
事件类型 |
开始 |
结束 |
负载(用电量) |
---|---|---|---|
INSERT |
2009-07-15 09:13:33.317 |
2009-07-15 09:14:09.270 |
100 |
INSERT |
2009-07-15 09:14:09.270 |
2009-07-15 09:14:22.255 |
200 |
INSERT |
2009-07-15 09:14:22.255 |
2009-07-15 09:15:04.987 |
100 |
点
点事件模型表示在单个时间点处发生的一个事件。点事件模型只需要事件的开始时间。StreamInsight 服务器通过向开始时间中添加时钟周期(基础时间数据类型的最小时间单位)来推断有效的结束时间,以设置事件的有效时间间隔。考虑不包括事件结束时间,点事件仅对其开始时间的单个实例有效。
下图显示点事件模型的布局。
元数据 |
负载 |
---|---|
事件类型 ::= INSERT 开始时间 ::= DateTimeOffset |
字段 1 … 字段 n 作为 CLR 类型 |
点事件示例包括电表读数、电子邮件到达、用户 Web 单击、股票行情、进入 Windows 事件日志中的条目。在上述的电力监控示例中,可以使用下列点事件来表示电表事件流。请注意,结束时间是按照在开始时间上加 1 个时钟周期 (t) 计算的。
事件类型 |
开始 |
结束 |
负载(用电量) |
---|---|---|---|
INSERT |
2009-07-15 09:13:33.317 |
2009-07-15 09:13:33.317 + t |
100 |
INSERT |
2009-07-15 09:14:09.270 |
2009-07-15 09:14:09.270 + t |
200 |
INSERT |
2009-07-15 09:14:22.255 |
2009-07-15 09:14:22.255 + t |
100 |
边缘
边缘事件模型表示其负载在给定时间间隔内有效的事件发生,但在到达 StreamInsight 服务器时只知道开始时间;因此将结束时间设置为将来的最大时间。事件的结束时间稍后才知道并进行更新。边缘事件模型包含两个属性:发生时间和边缘类型。这些属性一同定义边缘事件的开始点或结束点。
下图显示一个边缘事件模型的布局。
元数据 |
负载 |
---|---|
事件类型 ::= INSERT 边缘时间 ::= DateTimeOffset 边缘类型 ::= START | END |
字段 1 … 字段 n 作为 CLR 类型 |
边缘事件的示例包括 Windows 进程、来自 Windows 事件跟踪 (ETW) 的跟踪事件、Web 用户会话或模拟信号的量化。边缘事件的负载的有效时间间隔是 Start 事件时间戳与 End 事件时间戳之间的差值。在下图中,请注意负载值为“c”的事件,此刻它还没有已知的结束日期。
事件类型 |
边缘类型 |
开始时间 |
结束时间 |
负载 |
---|---|---|---|---|
INSERT |
Start |
t0 |
DateTimeOffset.MaxValue |
a |
INSERT |
End |
t0 |
t1 |
a |
INSERT |
Start |
t1 |
DateTimeOffset.MaxValue |
b |
INSERT |
End |
t1 |
t3 |
b |
INSERT |
Start |
t3 |
DateTimeOffset.MaxValue |
c |
…等等 |
下面演示如何基于上表中定义的开始和结束时间,使用边缘事件量化模拟信号。此类连续信号意味着,对于每个新值,必须同时提交 END 以及 START 边缘。图中介绍的边缘指从时间 t1 到 t3 的事件。
与事件模型的选择相关的性能注意事项
为您的问题选择正确的事件模型,这一点至关重要。例如,如果事件持续一段时间,而您的应用程序能够确定事件的开始和结束时间,则最好使用间隔事件对此建模。如果您并不了解事件到达后将于何时结束,则可以考虑将该事件作为点事件建模,将其生存期改为延长一段时间,然后在明确该事件的结束时间后使用“剪辑”操作修改该生存期。另一可选的方式是将这些事件作为边缘事件建模。
尽管边缘事件是非常方便的事件模型,但应注意到该类事件可能带来的几个性能问题。处理边缘事件在这些事件完全有序到达时才能实现最佳性能;完全有序是指所有开始边缘都按开始时间排序,结束边缘都按结束时间排序,组合之后的事件顺序也按时间来排序。例如,如果具有按如下顺序排列的边缘事件:
事件类型 |
边缘类型 |
开始时间 |
结束时间 |
负载 |
INSERT |
Start |
1 |
DateTimeOffset.MaxValue |
a |
INSERT |
End |
1 |
10 |
a |
INSERT |
Start |
3 |
DateTimeOffset.MaxValue |
b |
INSERT |
End |
3 |
6 |
b |
INSERT |
Start |
5 |
DateTimeOffset.MaxValue |
c |
INSERT |
End |
5 |
20 |
c |
此顺序在时间戳 (1, 10, 3, 6, 5, 20) 是无序的。但如果边缘事件是完全排序的 - 如按 (1, 3, 5, 6, 10, 20) 排序 - 这对查询处理性能的影响将会降低。启用这样的排序,随后再进行处理,是很容易实现的。将该问题拆分为两个查询。第一个查询可以是空查询,用来接收边缘事件作为输入,对这些事件进行完全排序,然后输出这些经过排序的边缘事件。第二个查询可以接收此输入,并执行主要逻辑。请注意,应该将这些查询定义为两个单独的查询,然后使用动态查询组合联接起来。有关详细信息,请参阅在运行时撰写查询。
事件负载
事件负载是一种 .NET 数据结构,其中包含与事件关联的数据。负载中的字段由用户定义,其类型基于 .NET 类型系统。负载字段支持大多数 CLR 标量和初级类型。不支持嵌套类型。
适配器
适配器转换并传送进出 StreamInsight 服务器的传入和传出事件流。StreamInsight 提供高度灵活的适配器 SDK,可帮助您为自己的域特定事件源和输出设备(接收器)生成适配器。适配器使用 C# 编程语言实现,并存储为程序集。适配器类在设计时作为模板来创建,在 StreamInsight 服务器中进行注册,在运行时作为适配器实例在服务器中进行实例化。
输入适配器
输入适配器实例从外部源(如数据库、文件、股票资讯、网络端口和传感器等)接受传入事件流。输入适配器用提供传入事件的原有格式读取传入事件,然后将此数据转换为 StreamInsight 服务器所用的事件格式。
您将创建一个输入适配器来为您的数据源处理特定事件源。如果事件源只产生单个事件类型,则可以类型化此适配器。也就是说,实现适配器来发出一种特定事件类型的事件。使用类型化适配器,适配器的所有实例都产生相同的固定负载格式,这种格式使您能够提前知道字段的数量和类型。这类事件的示例包括:股票资讯数据,特定设备发送的传感器数据等。如果您的事件源在不同情况下发出不同的类型,也就是说,事件中可能包含不同的负载格式,或者可能无法提前知道负载格式,则实现非类型化的适配器。借助于非类型化(泛型)适配器,在查询绑定时间,事件负载格式会作为配置规范的一部分提供给适配器。此类源的示例包括:包含不定数量字段的 CSV 文件,其中,存储在文件中的数据类型不得而知,直到查询实例化时为止;SQL Server 表的适配器,其中,生成的事件取决于表的架构。尤须注意,在运行时,单个适配器实例(无论是类型化还是非类型化的)都始终发出某一特定类型的事件。非类型化适配器在查询绑定时提供灵活的实现来接受事件类型的指定,而不是在实现适配器时定义事件类型。
输出适配器
您将创建一个输出适配器模板来接收 StreamInsight 服务器处理的事件,将事件转换为输出设备(接收器)所需的格式,然后将数据发送给该设备。输出适配器的设计和创建过程与输入适配器类似。类型化输出适配器针对特定的事件负载而设计,而非类型化输出适配器仅当在实例化查询时,在运行时随事件类型提供。
有关详细信息,请参阅创建输入和输出适配器。核心适配器 API 提供最大程度的灵活性,可针对任何事件源或事件接收器实现操作。此外,StreamInsight 还在更高抽象级别上支持实现 IObservable 或 IEnumerable 接口的事件源和接收器。有关详细信息,请参阅使用可观察和可枚举的事件源和事件接收器 (StreamInsight)。
处理和分析事件
使用 StreamInsight,事件处理基于所定义的逻辑组织为查询。这些查询采用潜在无限的时间敏感输入数据(记录时间或实际时间)馈送,对数据执行一些计算,并以适当的形式输出结果。
查询模板
查询模板是查询结构的基本单位,是定义业务逻辑的结构。要连续分析和处理从输入适配器提交给 StreamInsight 服务器的事件,并生成输出适配器所需的事件流,业务逻辑是必需的。例如,您可能希望评估传入的用电事件,以便了解在给定时期内,超出您设定的特定阈值的最大值或最小值。
可以编写查询模板来执行特定的工作单元,然后组合成更复杂的查询模板。查询模板采用结合了 C# 语言的 LINQ 进行编写。LINQ 是一个语言平台,它允许您采用完全集成到宿主语言中的行为,针对集表达声明性计算。这样,您可以在相同开发平台中,将事件的声明性处理与过程编程的灵活性相结合,无需考虑这两个编程范式间阻抗不匹配。
StreamInsight 服务器提供下列功能来编写表达查询和分析:
通过计算引入额外事件属性
使用案例(如单位转换)要求在接收的事件之上执行计算。在 StreamInsight 服务器中使用投影操作,可以在负载中添加额外字段,并对输入事件中的字段执行计算。有关详细信息,请参阅投影。
筛选事件
在一些使用案例(如警告通知)中,您可能希望查看是否有某个负载字段超过了您正在监控的设备部件的操作阈值。通常,只有满足特定特性的事件的子集才与这些使用案例相关。不具有这些特性的事件不需要处理,可以将其废弃。筛选操作允许您对事件负载表达布尔谓词,并废弃那些不满足谓词的事件。有关详细信息,请参阅筛选。
分组事件
考虑这样一个事件流,该事件流为您提供来自您的所有温度传感器的温度读数。如果所有事件都通过单个事件流来提供,则您可能希望基于传感器的位置或传感器 ID 将传入的事件分组。StreamInsight 服务器提供分组操作,您可以基于事件属性(如位置或 ID)来分组传入流,然后将其他操作或完整的查询片段单独应用于每个组。有关详细信息,请参阅分组和应用。
时移窗口
随时间推移分组事件是一个很有用的概念,可以实现很多方案。例如,您可能希望查看在某个固定时期内发生的故障数,并且如果故障数超过阈值,则引发一个警告。跳跃和滑动窗口允许您对事件流定义窗口来执行这种分析。有关详细信息,请参阅使用事件窗口。
聚合
假如您并不关心每个单独事件,而是希望研究聚合值,如平均、总和或计数等。StreamInsight 服务器为 sum、count、min、max 和 average 提供了内置聚合,这些聚合通常操作时间窗口。有关详细信息,请参阅聚合。
识别前 N 个候选项
当您希望标识一个事件流中根据特定指标排名最高的候选事件时,需要一种特殊的聚合操作。TopK 操作允许您根据规定的顺序,从流中的事件字段中检查这些候选事件。有关详细信息,请参阅 TopK。
匹配来自不同流的事件
一个常见的使用案例就是推断从多个流接收的事件。例如,因为事件源在其事件数据中提供了时间戳,所以您可能希望确保只将一个流中的事件与另一个流中的事件匹配(如果它们在时间上密切相关)。此外,您可能对匹配哪些事件,何时匹配这些事件还有其他约束。StreamInsight 服务器提供有力的联接操作,该操作执行两个任务:首先,如果来自两个源的事件的时间重叠,则匹配事件;其次,对负载字段执行指定的联接谓词。这种匹配的结果中同时包含来第一个和第二个事件的负载。有关详细信息,请参阅联接。
将来自不同流的事件组合成一个事件
多个数据源可能提供了您希望馈送到相同查询中的相同类型事件。StreamInsight 服务器提供的联合操作允许您将几个输入流合并到单个输出流中。有关详细信息,请参阅联合。
用户定义的扩展
StreamInsight 服务器的内置查询功能可能不能满足所有情况。为了允许域特定扩展,StreamInsight 服务器中的查询可以调用用户定义的功能,这些功能通过 .NET 程序集提供。除了用户定义函数之外,您还可以定义和实现自定义聚合或查询运算符。有关详细信息,请参阅用户定义函数和用户定义聚合和运算符。
有关详细信息,请参阅在 LINQ 中编写查询模板。有关如何编写用于 StreamInsight 的 LINQ 查询的详细指导,请参阅 A Hitchhiker’s Guide to StreamInsight Queries(StreamInsight 查询的便利指南)。
查询实例
如果将一个查询模板与特定输入和输出适配器绑定,则会在 StreamInsight 服务器中注册一个查询实例。可以在 StreamInsight 服务器中启动、停止和管理绑定的查询。数据通过输入适配器进入 StreamInsight 服务器后,可以对数据执行连续的计算。换言之,当单独的事件到达服务器后,现有查询处理这些事件,即发出输出事件来响应输入事件到达。下图演示运行时的 StreamInsight 查询和适配器。当输入适配器的实例绑定到一个查询实例时,StreamInsight 服务器使用并处理事件。然后,处理后数据被推送到与相同查询实例绑定的输出适配器实例。