【翻译】StreamInsight:使用时间导入同步慢行引用流和快行数据流
使用StreamInsight的常见任务之一是使用引用流集成来自相对静态数据源(例如SQL Server表)的元数据或引用数据。集成引用流的难点在于它需要和生存期(liveliness)打交道,也就是说:
在这个图上,我们可以看到两个流(数据流和引用流)连接在一起创建出了一个连接流,如果使用LINQ语法来表示上述内容的话,可以这么写:
CepStream<SensorReading> dataStream = ...; CepStream<SensorMetadata> metadataStream = ...; var joinedQuery = from e1 in dataStream join e2 in metadataStream on e1.SensorId equals e2.SensorId select e1;
从上图中,我们可以看到输出仅有两个事件。这是StreamInsight引擎根据应用程序时间(由CTI更新)产生的输出结果。在这个例子中,StreamInsight引擎做了如下工作:
- 数据流时间为T5
- 引用流事件为T0
- 由于这两个流连接在一起,因此只有接收到这个时间段内的所有事件之后才能够产生输出。由于引用流落后于数据流,因此只能按照引用流的速度输出事件
通常来说,上述行为毫无疑问是正确的。但是有时我们想要实现的和StreamInsight引擎实现的并不一致——例如引用流缓缓变化,而我们不希望一直等它。也就是说我们希望结果的输出速度同数据流一致。那么该怎么做呢?
将数据流中的CTI概念引入到引用流中:
可以使用CepStream<>.Create()重载方法来完成这项操作。下面代码显示了以某个csv文件作为样例数据源,同时将另一个csv文件作为样例引用流的过程。整个项目可以从这里下载。
//////////////////////////////////////////////////////////////////// // 创建一个时间导入设置,指定流将导入dataStream中的CTI设置 // var timeImportSettings = new AdvanceTimeSettings(null, new AdvanceTimeImportSettings("dataStream"), AdvanceTimePolicy.Adjust); //////////////////////////////////////////////////////////////////// // 从refStream.csv文件中创建引用数据流;使用timeImportSettings中定义的dataStream中的CTI设置 // CepStream<SensorMetadata> metadataStream = CepStream<SensorMetadata> .Create("refStream", typeof(TextFileReaderFactory), new TextFileReaderConfig() { CtiFrequency = 1, CultureName = CultureInfo.CurrentCulture.Name, Delimiter = ',', InputFileName = "refStream.csv" }, EventShape.Point, timeImportSettings);
注意上述语法假定"dataStream"流存在。现在如果我们连接数据流和引用流,就可以看到一个稳定的输出流了。尽管如此,如果我们简单看一下元数据流的原始输出:
var rawData = metadataStream.ToQuery(cepApp, "MetadataStream", "", typeof(TracerFactory), traceConfig, EventShape.Interval, StreamEventOrder.FullyOrdered);
会得到以下错误:
Error in query: Microsoft.ComplexEventProcessing.ManagementException: Advance time import stream 'dataStream' does not exist. ---> Microsoft.ComplexEventProcessing.Compiler.CompilerException: Advance time import stream 'dataStream' does not exist. |
咦?导入流不存在是什么意思?我已经定义了啊!其实原因是导入流还没有与另一个流在物理上真正连接。解决这个问题,你需要在绑定输出适配器之前连接两个流。
//////////////////////////////////////////////////////////////////// // 创建两个流的连接,并将结果绑定到控制台 // var joinedQuery = from e1 in dataStream join e2 in metadataStream on e1.SensorId equals e2.SensorId select e1; var query = joinedQuery.ToQuery(cepApp, "JoinedOutput", "", typeof(TracerFactory), traceConfig, EventShape.Interval, StreamEventOrder.FullyOrdered);
好了,现在让我们再看看输出:
REF,Interval from 06/25/2009 00:00:00 +00:00 to 06/25/2009 00:00:00 +00:00:,MySensor_1001,1001,14 REF: CTI at 06/25/2009 00:00:00 +00:00 REF: CTI at 06/25/2009 00:00:09 +00:00 REF: CTI at 12/31/9999 23:59:59 +00:00 |
咦?输出去哪了?注意到引用数据只是点事件序列,如果我们想要真正地把它用作引用流,我们需要将点事件系列转变为边缘事件。可以使用AlterEventDuration和Clip操作符完成以上工作:
// 将引用流中的点事件转换为边缘事件 var edgeEvents = from e in metadataStream .AlterEventDuration(e => TimeSpan.MaxValue) .ClipEventDuration(metadataStream, (e1, e2) => (e1.SensorId == e2.SensorId)) select e;
这段代码做了如下工作:
- 将点事件持续时间延伸为无穷时间
- 修剪出任何到达的具有相同传感器ID的点事件。例如,给定值(1001, SensorId_1001),如果稍后到达的另外一个事件的值为(1001, MySensor),初始事件将会被裁剪掉,而新值变为MySensor。
把所有东东放在一起如下:
// 将引用流中的点事件转换为边缘事件 var edgeEvents = from e in metadataStream .AlterEventDuration(e => TimeSpan.MaxValue) .ClipEventDuration(metadataStream, (e1, e2) => (e1.SensorId == e2.SensorId)) select e; //////////////////////////////////////////////////////////////////// // 创建两个流的连接,并将结果绑定到控制台 // var joinedQuery = from e1 in dataStream join e2 in edgeEvents on e1.SensorId equals e2.SensorId select new { SensorId = e1.SensorId, Name = e2.Name, Value = e1.Value };
最终的结果如下:
REF,Interval,12:00:00.000,12:00:00.000,,MySensor_1001,1001,14 REF: CTI at 06/25/2009 00:00:00 +00:00 REF,Interval,12:00:01.000,12:00:01.000,,MySensor_1001,1001,4 REF,Interval,12:00:02.000,12:00:02.000,,MySensor_1001,1001,77 REF,Interval,12:00:03.000,12:00:03.000,,MySensor_1001,1001,44 REF,Interval,12:00:04.000,12:00:04.000,,MySensor_1001,1001,22 REF,Interval,12:00:05.000,12:00:05.000,,MySensor_1001,1001,51 REF,Interval,12:00:06.000,12:00:06.000,,MySensor_1001,1001,46 REF,Interval,12:00:07.000,12:00:07.000,,MySensor_1001,1001,71 REF,Interval,12:00:08.000,12:00:08.000,,MySensor_1001,1001,37 REF,Interval,12:00:09.000,12:00:09.000,,MySensor_1001,1001,45 REF: CTI at 12/31/9999 23:59:59 +00:00 |