【翻译】StreamInsight:使用时间导入同步慢行引用流和快行数据流

原文链接:http://blogs.msdn.com/b/masimms/archive/2010/09/27/streaminsight-synchronizing-slow-moving-reference-streams-with-fast-moving-data-streams-time-import.aspx


使用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
posted @ 2011-08-14 17:24  StreamInsight  阅读(388)  评论(0编辑  收藏  举报