Fork me on GitHub
Microsoft StreamInsight 构建物联网

微软的首席StreamInsight™ 项目经理发布的一篇博客 Big Data, Hadoop and StreamInsight™,微软的大数据解决方案中包含  Microsoft StreamInsight™ 和微软的 Hadoop-based Services for Windows

Microsoft还计划推出运行于Windows Server之上Hadoop预览版,已经提供下载https://connect.microsoft.com/SQLServer/Survey/Survey.aspx?SurveyID=13697 ,并会在6月29日提供最终版本。Microsoft还将在Hadoop上集成Active Directory和System Center。Microsoft的更多的大数据计划将会在今年6月公布。

 

Microsoft SQL Server 2012 Takes on Big Data with Hadoop

原文:http://msdn.microsoft.com/zh-cn/magazine/hh852591.aspx 

 

最近关于“物联网”(IoT) 的争论有很多,而且理由都很充分。 Ericsson 的 CEO Hans Vestberg 预测到 2020 年将有 500 亿台设备连接到 Web(bit.ly/yciS7r [PDF 下载])。 目前约有 15 亿台 PC 以及不足 10 亿台电话连接到 Web — 500 亿相当于全球每个人约 7 台设备,这可以帮助您直观理解这一数字! 市场研究机构 IDC 则预测到 2015 年将有超过 160 亿台设备连接到 Internet(参见图 1)。 不可否认,也存在一些较保守的预测,但通过每个人提供的数字,我们看到 Internet 的角色正在发生巨大转变 — 从为人们提供信息和娱乐内容到为支持设备的新兴应用程序提供连接服务。

 
图 1 IDC 预测会出现“嵌入式 Internet”

这些庞大数字看似可信的理由是强大的驱动因素(商机和必然性)正在推动此类解决方案的快速增加。 《经济学家》(economist.com/node/17388368) 最近的一期杂志指出,“…经济大潮的向前滚动不仅仅是为了符合技术公司和雄心勃勃的政治家的利益。 这种发展趋势已获得了动力,因为真的需要此类系统。”图 2 按应用领域显示了此类解决方案的增长态势。 例如,在欧洲强制实施智能能源系统是一个必然结果。 如果我们还不能管理能源消耗,则无法构建所需的能源产生功能。 在商机方面,简单的、无所不在的自动售货机就是一个很好的示例。 在连接该设备之后,可以根据需要而不是某项并非最理想的计划来指派服务人员。 如果本地需求增加或商品接近到期日,甚至还可以动态更改价格。 可以报告停电情况以便督促立即更换易腐商品。 换句话说,连接实现了更高效的全新业务模型。

 
图 2 按行业划分的应用增长态势

通过引用连接设备的数量来描述此转变肯定很形象,但它也会让人产生一些误解 — 此种转变并不表明几十万传统嵌入式程序员将充分就业。 这些设备仅是将其他设备集成到 Internet 的所有方面(包括分析、云、Web 应用程序、PC 和移动界面等)的复杂解决方案的终结点。

因此,看待此问题的方法是当前构建基于 Web 的应用程序的每个人将需要集成设备并帮助开发新业务和新业务模型。 换句话说,即使您不是嵌入式开发者,也不在构建嵌入式设备的商店中工作,这也是一个值得您评估的非常诱人的商机。 您当前具备的 Microsoft 技能将使您能够在 IoT 方面取得成功。

对设备数据进行分析

“数据已成为新的货币”,Windows Embedded 团队的总经理 Kevin Dallas 在最近的采访中说道 (bit.ly/wb1Td8)。 与当前 Internet 应用程序相比,IoT 涉及信息生成、管理和访问。 让我们比较一下当今典型 Internet 应用与 IoT 应用的数据特征。 您或许和其他几百万人均使用多家金融机构共享的流行机制联机支付帐单。 您每月登录多次,查看一些页面并提交付款信息。 所有这些数据都是使用当您开始与系统互动时所运行的查询从传统数据库中提取的。 您下载的页面量可能很大,但交互非常少,即使它们生成的有价值的信息(付款信息、个人信息更新等)需要长时间保存也是如此。

将此应用与能源管理系统进行对比,该系统中可能有 5000 万座大楼(商业楼和住宅楼)正在提供输入。 输入由内部的多个本地终结点(例如,房子)使用发布到后端的单个聚合视图生成。 该数据包括返回给大楼的成为定价和记帐基础的即时使用信息和强制性控制。 此系统将与价值相对较低的数据进行非常频繁的交互,这些数据在您计算系统的当前状态和该终结点的趋势数据时不一定有意义。 不过,该系统需要能够立即对可能威胁其运行的情况(例如需求剧增引发网格超载和停电)做出响应。 在这种情况下,广播信息可以立即减少能源消耗。 此类系统需要连续分析传入数据并比较发展趋势,以识别指示较高停电风险的模式。

图 3 演示 IoT 应用的典型体系结构。 堆栈底部显示了各种资产或设备,它们根据不同应用领域配备了不同种类的传感器。 这些传感器通常生成应用领域快速处理和分析所需的连续数据源。 根据设备的功能,设备本身或许能够在本地执行一些处理。 这称为本地分析,并且 .NET Micro Framework 之类的工具可帮助您在设备传递数据之前执行该本地处理。 IoT 应用使用 Internet 协议传递设备数据,以便可以对数据进行全局分析。 而全局分析的结果(例如电网的整体运行状况)是管理运营的最终用户或业务决策者关注的内容。 分析还可以驱动根据传入数据中呈现的情况自动采取操作的封闭系统。 如果资产可接收来自全局分析的反馈(例如,影响行为更改或改进操作),则这些方法将非常有用。 需要连续计算推动这些过程的全局分析并尽快提供结果。 另外,分析频繁参考随传感器数据一起提供的时间和时间戳。 因此,仅将此类数据放入数据库中并对其运行定期查询不是适当的方法。 幸运的是,Microsoft StreamInsight 支持不同的方法。

 
图 3 物联网应用的典型体系结构

Microsoft StreamInsight

Microsoft StreamInsight 旨在对连续到达的数据提供及时反应,而不将数据写入磁盘中以进行分析和查询。 许多 IoT 应用领域需要在从源获取数据后几乎实时地分析传入数据。 考虑我们提到的智能电网应用,它需要对剧增的电力需求快速做出反应以重新平衡电网的运营能力。 许多 IoT 应用具有相同的需求: 需要连续分析的数据处理和引人注目的延迟。 分析必须连续,因为数据源不断地生成新数据。 许多方案需要识别只能通过分析传入数据而呈现的情况并对其快速做出反应,因此它们需要低延迟分析和几乎立即提供的结果。 这些要求使在执行分析之前将数据存储在关系数据库中变得不切实际。

我们将这些应用称为事件驱动应用,而 IoT 正是此类功能发挥作用的一个方案。 StreamInsight 是一个用于构建这些高度可伸缩、低延迟的事件驱动应用的强大平台。 它是自 2008 R2 版本发布以后的 Microsoft SQL Server 的一部分。 在事件驱动处理和基于丰富表达时间的分析方面,StreamInsight 为 SQL Server 提供了补充。 使用 StreamInsight,将以生成数据的速度,而不是处理传统数据库报告的速度提供业务见解。

可供人们立即使用或使应用程序能够自动对事件做出反应的分析结果可帮助企业更及时且更好地了解其相关运营情况,甚至可以自动执行部分运营工作。 它们也可以对传感器或设备数据中出现的重要情况、商机或趋势更快地做出反应。

要编写 StreamInsight 应用程序,开发者可使用 Microsoft .NET Framework、LINQ 和 Microsoft Visual Studio 等熟悉的工具。 图 4 描述了 StreamInsight 应用程序的开发者和运行时体验并介绍了一些关键概念。

 
图 4 StreamInsight 应用程序开发和运行时

简单的 IoT 应用

让我们更深入地了解可能的 IoT 应用方案;然后我们将构建它。 在我们的端到端示例中,我们将关注一个简单方案,该方案使用运动传感器监视旋转设备,例如涡轮或风车。 这很重要,因为振动过大会导致出现紧急情况,在这种情况下,设备可能出现故障,并且如不立即停止,则会出现严重损坏。 为可靠地检测此情况,每台设备均配备多个跟踪运动的传感器。 单个传感器中的运动激增可能仅指示该传感器的数据读数不可靠,但多个传感器中同时出现异常剧烈的运动则表明出现紧急情况。 例如对于大型涡轮,您可能希望引发警报,甚至自动关闭设备。 除了持续检查此类情况外,我们还希望为操作员提供一个仪表板,它提供了设备状态的近实时视图。

若要构建此方案,我们需要满足以下要求和解决以下技术难题:

  • 设备需要捕获哪些数据?
  • 我们使用哪些传感器来测量数据?
  • 设备如何将其传感器读数传送到 Internet?
  • 我们如何将设备数据收集到一个位置以进行分析?
  • 我们如何可以连续分析传入数据并对紧急情况快速做出反应?
  • 我们如何跨多台设备及时关联传感器读数,以便可以检查全局情况?

让我们看一下满足这些要求并实现端到端方案的方式。

IoT 应用: 实现要点

下面是实现上一节中所述的 IoT 应用的一些关键步骤。 我们将首先讨论设备,再转到输出的可视化,然后转到填充仪表板的跨设备分析。

设备。为构建传感器设备,我们首先从 Netduino Plus 着手,它是运行 .NET Micro Framework、具有 128K SRAM 的受欢迎的小型开发板。 我们添加了名为 WiFly GSX Breakout 的常见爱好者 Wi-Fi 无线电,并在自定义 PCB 板上安装了实际传感器,包括三轴加速计。 我们对设备进行编程,以将传感器读数的每秒更新发送给 Web 服务,该服务充当从所有设备收集数据并进行处理的中心。

我们对 Web 服务使用 RESTful 连接 — 它只是包含逗号分隔名称-值对的 HTTP POST。 当然,您可以从支持 HTTP 的任何种类的设备执行此操作。 我们选择使用 .NET Micro Framework,以便整个应用程序(包括设备、Web 服务、StreamInsight 适配器、Silverlight 仪表板等)全部可以使用单个编程模型 (.NET) 和工具链 (Visual Studio) 进行编写。 很明显,如果您具有 .NET 技能,则无需招聘新员工或将您的 IoT 项目的一部分外包给外部嵌入式商店;您具有完全执行它的技能。 例如,设置加速计时只需几行代码即可访问 AnalogInput 类并调用 Read 方法:

  1.           this.analogInputX = new AnalogInput(pinX);
  2. this.analogInputY = new AnalogInput(pinY);
  3. this.analogInputZ = new AnalogInput(pinZ);
  4. ...
  5.           rawZ = analogInputZ.Read();
  6. rawY = analogInputY.Read();
  7. rawX = analogInputX.Read();
  8.        

在读取传感器输入并设置 HTTP 消息内容格式后,发送数据所需的一切都包括在图 5 中。

图 5 提交传感器数据

  1.           protected void submitSensorData(string uri, string payload)
  2. {
  3.   // Message format
  4.   StringBuilder sb = new StringBuilder(256);
  5.   sb.Append(
  6.     "POST /Website/Services/DataService.aspx?method=SaveDeviceData HTTP/1.1\n");
  7.   sb.Append("User-Agent: NetduinoPlus\n");
  8.   sb.Append("Host: 192.168.1.101\n");
  9.   sb.Append("Connection: Keep-Alive\n");
  10.   sb.Append("Content-Length: ");
  11.   sb.Append(payload.Length.ToString());
  12.   sb.Append("\n");
  13.   sb.Append(payload);
  14.   sb.Append("\n");
  15.   try
  16.   {
  17.     HttpResponse response = webServer.SendRequest(uri, 80, request);
  18.   }
  19.   catch
  20.   {
  21.     ...
  22.           }
  23. }
  24.        

在服务器端,我们实现方法 SaveDeviceData,设备要将其消息发布给该方法。 我们拆分消息字符串并分析 MAC 地址、时间戳和负载数据,例如来自加速计的运动读数。 我们使用所有这些信息来填充传递给 StreamInsight 以进行后续分析的 DeviceData 对象(请参见图 6)。

图 6 填充 DeviceData 对象

  1.           private int SaveDeviceData()
  2. {
  3. ...
  4.           List<string> data = record.Split(',').ToList();
  5.   DeviceData deviceData = new DeviceData();
  6.   deviceData.MAC = NormalizeMAC(data[0].Trim());
  7.   deviceData.DateTime = DateTime.UtcNow;
  8. ...
  9.           deviceData.Motion = Convert.ToDecimal(data[2].Substring(data[2].IndexOf(":") + 1));
  10. ...
  11.           // Communicate each new device data record to StreamInsight          
  12.   DeviceDataStreaming streaming = (DeviceDataStreaming)
  13.     HttpContext.Current.Application[Global.StreamingIdentifier];
  14.     streaming.TrackDeviceData(deviceData);
  15. ...
  16.           }
  17.        

仪表板。现在我们要构建允许设备操作员查看设备上传感器的当前状态的仪表板。 为便于演示,我们将仅关注一台设备。 图 7 显示了一个此类仪表板的示例。 让我们从左侧开始,查看传感器数据的不同视图。

 
图 7 用于设备监视的仪表板

移动平均数视图: 左下角的数据网格显示设备的传感器读数,其中包括光线、温度和运动值以及设备 ID 和时间戳。 正如您可以从时间戳中看到的,这些值每秒更新一次。 但仪表板不显示原始传感器值,而是显示 10 秒内传感器数据的移动平均数。 这意味着会使用最近 10 秒内数据的平均数每秒更新一次值。 使用移动平均数是一种常见的简单技术,可防止出现使用低成本传感器时偶尔出现的异常值和不良数据。

趋势线视图: 在右下角,仪表板显示传感器的趋势线。 趋势线视图的走势由左侧数据网格中显示的移动平均数决定。

警报视图: 右上角的视图显示警报的数据网格。 如果检测到临界情况,则会引发显示时间和其他信息(例如严重性和状态)的警报。

分析。现在让我们了解幕后操作并讨论处理传入传感器数据并计算仪表板可视化的结果的分析。 我们使用 StreamInsight 执行分析。 以下类表示设备数据,其中包括 MAC 地址、时间戳和传感器值:

  1.           public class DeviceData
  2. {
  3.   public string MAC { get; set; }
  4.   public DateTime DateTime { get; set; }
  5.   public decimal?
  6.           Light { get; set; }
  7.   public decimal?
  8.           Temperature { get; set; }
  9.   public decimal?
  10.           Motion { get; set; }
  11. }
  12.        

此类定义单个事件的形状,但我们想要开始讨论许多事件。 为此,我们为 StreamInsight 定义了 Observable 数据源。 这仅是实现 System.IObservable 接口的数据源:

  1.           public class DeviceDataObservable : IObservable<DeviceData>
  2.   {
  3.     ...
  4.           }
  5.        

在定义 .NET Framework 序列(例如 Enumerable 或类似的 Observable)后,即可开始编写对这些集合的 StreamInsight 查询。 让我们快速了解一下其中某些关键查询。 第一个查询获取 Observable 作为输入并生成 StreamInsight 点事件流,以使用设备数据中的“DateTime”字段作为 StreamInsight 事件的时间戳。 在下一个 LINQ 语句中,我们获取此流作为输入,并按 MAC 地址对数据进行分组。 对于每个组,我们然后应用窗口大小为 10 秒的跳跃窗口(基于时间的一部分事件),并让窗口每秒重新计算一次。 在每个窗口中,我们计算温度、光线和运动的平均数。 这为我们提供了每秒重新计算一次的每台设备的移动平均数。 图 8 显示了用于返回 StreamInsight 事件流形式的结果的函数实现此过程的代码。

图 8 获取移动平均数

  1.           public static CepStream<AverageSensorValues> GroupedAverages(
  2.               Application application,
  3.               DeviceDataObservable source)
  4.   {
  5.     var q1 = from e1 in source.ToPointStream(application,
  6.       e => PointEvent.CreateInsert(
  7.         new DateTimeOffset(
  8.           e.DateTime.ToUniversalTime()),e),
  9.       AdvanceTimeSettings.StrictlyIncreasingStartTime,
  10.       "Device Data Input Stream")
  11.              select e1;
  12.     var q2 = from e2 in q1
  13.              group e2 by e2.MAC into groups
  14.              from w in groups.HoppingWindow(
  15.                TimeSpan.FromSeconds(10),
  16.                TimeSpan.FromSeconds(1))
  17.              select new AverageSensorValues
  18.              {
  19.                DeviceId = groups.Key,
  20.                Timestamp = null,
  21.                AvgTemperature = w.Avg(t => t.Temperature),
  22.                AvgLight = w.Avg(t => t.Light),
  23.                AvgMotion = w.Avg(t => t.Motion)
  24.              };
  25.     return q2;
  26.   }
  27.        

这是考虑实现警报查询的最佳位置。 请记住,当有多个运动传感器的读数同时高于运动阈值时,将触发警报。 只需对刚计算的分组平均数使用几个 StreamInsight LINQ 语句便可处理此问题。 通过将警报阈值的更改表示为名为 AlarmThresholdSignal 的事件流,第一个查询 q3 应用了一个极佳的技巧。 此查询将阈值与来自前一个查询的平均数流联接,然后仅筛选高于阈值的事件:

  1.           var q3 = from sensor in GroupedAverages(application, source)
  2.          from refdata in AlarmThresholdSignal(application, alarmsthresholds)
  3.          where (sensor.AvgMotion !=
  4.            null && (double) sensor.AvgMotion > refdata.Threshold)
  5.          select new
  6.          {
  7.            AlarmDevice = sensor.DeviceId,
  8.            AlarmInfo = "This is a test alarm for a single device",
  9.          };
  10.        

下一个查询使用 StreamInsight 快照窗口来识别事件状态更改的时间点。 如果从前一个筛选查询产生了一个新事件,则这是新快照,并且该快照操作生成一个新窗口,其中包含与触发快照窗口的事件一致或重叠的所有事件。 下面的代码对创建快照窗口时高于警报阈值的事件进行计数:

  1.           var alarmcount = from win in q3.SnapshotWindow()
  2.                  select new
  3.                  {
  4.                    AlarmCount = win.Count()
  5.                  };
  6.        

最后一步检查计数是否显示有多台设备将发出警报指示:

  1.           var filteralarms = from f in alarmcount
  2.                    where f.AlarmCount >= 2
  3.                    select new AlarmEvent
  4.                    {
  5.                      AlarmTime = null,
  6.                      AlarmInfo = "Now we have an alarm across multiple devices",
  7.                      AlarmKind = 0,
  8.                      AlarmSeverity = 10,
  9.                      AlarmStatus = 1
  10.                    };
  11.        

现在,我们只需将包含平均传感器值和警报的输出流从 StreamInsight 传送到 UI。

使输出流传送到 UI

使用在服务器端生成结果流的 StreamInsight,我们需要一种方法来将这些流传送给使用者。 使用者可能不在服务器进程中运行,并可能使用轻型 Web 应用程序来可视化结果。 如果您使用 Silverlight,则双工协议很方便,因为它支持从服务器到客户端的连续的基于推送的传送。 HTML5 Web 套接字也是引人注目的替代方法。 无论如何,您都希望轻松地在服务器端添加新分析并能够轻松地将它们与 UI 连接,而无需拆分 UI 和承载 StreamInsight 的进程之间的客户端-服务器接口。 如果 UI 和服务器之间的负载适中,则您可以将服务器端的结果序列化为 XML 并在客户端对其进行反序列化。 这样,您只需关注线路上和您的客户端-服务器接口中的 XML,以及指示要反序列化的类型的附加 Cookie。 下面是几段关键代码。

第一个代码段是 Windows Communication Foundation 协定,它用于传送 XML 序列化字符串形式的事件数据以及指示类型的 GUID:

  1.           [ServiceContract]
  2. public interface IDuplexClient
  3. {
  4.   [OperationContract(IsOneWay = true)]
  5.   void Receive(string eventData, Guid guid);
  6. }
  7.        

现在,我们可以使用数据协定为结果事件结构添加批注以使其可序列化,如图 9 所示。

图 9 为事件结构添加批注

  1.           [DataContract]
  2. public class AverageSensorValues : BaseEvent
  3. {
  4.   [DataMember]
  5.   public new static Guid TypeGuid =
  6.     Guid.Parse("{F67ECF8B-489F-418F-A01A-43B606C623AC}");
  7.   public override Guid GetTypeGuid() { return TypeGuid; }
  8.   [DataMember]
  9.   public string DeviceId { get; set; }
  10.   [DataMember]
  11.   public DateTime?
  12.           Timestamp { get; set; }
  13.   [DataMember]
  14.   public decimal?
  15.           AvgLight { get; set; }
  16.   [DataMember]
  17.   public decimal?
  18.           AvgTemperature { get; set; }
  19.   [DataMember]
  20.   public decimal?
  21.           AvgMotion { get; set; }
  22. }
  23.        

现在,我们可以轻松地序列化服务器端的结果事件并将其传送到客户端,如图 10 所示。

图 10 从服务器发送结果事件

  1.           static public void CallClient<T>(T eventData) where T : BaseEvent
  2.   {
  3.     if (null != client)
  4.     {
  5.       var xmlSerializer = new XmlSerializer(typeof(T));
  6.       var stringBuilder = new StringBuilder();
  7.       var stringWriter = new StringWriter(stringBuilder);
  8.       xmlSerializer.Serialize(stringWriter, eventData);
  9.       client.Receive(stringBuilder.ToString(), eventData.GetTypeGuid());
  10.     }
  11.   }
  12.        

在客户端上,我们反序列化双工服务的回调方法中的事件,然后根据接收到的事件的类型将其分支到不同的方法中,如图 11 所示。

图 11 在客户端上接收和反序列化事件

  1.           void proxy_ReceiveReceived(object sender, ReceiveReceivedEventArgs e)
  2. {
  3.   if (e.Error == null)
  4.   {
  5.     if (AverageSensorValues.TypeGuid == e.guid)
  6.     {
  7.       ProcessAverageSensorValues(Deserialize<AverageSensorValues>(e.eventData));
  8.     }
  9.     else if (AlarmEvent.TypeGuid == e.guid)
  10.     {
  11.       ProcessAlarms(Deserialize<AlarmEvent>(e.eventData));
  12.     }
  13.     else
  14.     {
  15.       ProcessUnknown();
  16.     }
  17.   }
  18. }
  19.        

使用这些查询并传送到相应的 Web 应用程序,您现在可以选取几台设备并摇动它们,直到一些设备读数高于警报阈值。 然后,UI 将生成这些红色警报之一,如图 12 所示。

 
图 12 包含警报的设备仪表板

因为新数据会不断进入几乎实时的仪表板,所以 ObservableCollections 对更新 UI 极其有用。 如果您使数据网格和趋势线基于这些 Observable 集合,则无需担心代码中的更新部分。 这些集合将在后台为您自动执行此操作。

前景

在此实现中,设备与常规 Web 服务通信,该服务可以运行在连接到 Internet 的普通 PC 上。 但云计算是一个吸引人的替代方法;您不一定需要为自己的 Web 服务器拥有硬件并运行软件。 云中的服务可以充当为您的应用程序收集所有设备数据的中心。 这还使您能够在设备数量增加或部署针对设备数据的其他分析时,非常轻松且灵活地扩展您的处理能力。 Microsoft 计划将 StreamInsight 功能作为 Windows Azure 中的一项服务(StreamInsight 项目代码名称“Austin”)提供。 通过提供预定义的通信终结点和协议,Austin 将使您能够轻松地将设备连接到 Microsoft 云中丰富的分析处理功能。 如果您将 IoT 应用程序部署到 Windows Azure 中,则将自动获得灵活扩展和即付即用等云好处,以便管理设备连接和对设备数据执行丰富的分析。

另一个重大转变涉及最近进行的 W3C 标准化工作。 IoT 应用程序的最重要计划是 HTML5 和 Web 套接字。 HTML5 为丰富的 Web 应用程序(例如我们实现的仪表板)提供平台。 而 WebSocket 又简化了浏览器和 Web 服务器之间基于 TCP 的全双工通信,尤其是针对连续处理传感器数据时所要求的结果传送的推送模型。

连接的设备开创了一个令人兴奋的新应用领域,并且 Microsoft 现在提供用于构建这些 IoT 应用程序的工具。 我们在这里介绍了如何在设备级别通过熟悉的接口利用您的 .NET Framework 技能,以及如何通过 Web 服务为 StreamInsight 的强大分析功能提供数据。 立即开始使用连接设备构建您的 IoT 应用程序!

Torsten Grabs 是 Microsoft SQL Server 部门的首席项目经理。 他具有 10 余年 Microsoft SQL Server 产品的使用经验,并获得瑞士苏黎世的瑞士联邦理工学院的计算机科学博士学位。

Colin Miller 在 PC 软件领域工作了 25 年(其中有 15 年效力于 Microsoft),研究方向涉及数据库、桌面发布、消费类产品、Word、Internet Explorer、Passport (LiveID) 及联机服务。 他是 .NET Micro Framework 的产品部经理。

衷心感谢以下技术专家对本文的审阅: Rafael Fernandez Moctezuma 和 Lorenzo Tessiore

原文地址:http://msdn.microsoft.com/zh-cn/magazine/hh205648.aspx 
下载代码示例

生产线的产量下降后,将容易出现用户媒体流跳过这些流程,或者您的一个产品成为了“必需产品”的情况。 真正的窍门是在这些情况发生时进行识别,或根据以往趋势对其做出预测。

成功预测这些情况需要使用近乎实时的方法。 在对相关数据进行提取、转换并加载到 SQL Server Analysis Services (SSAS) 等传统商业智能 (BI) 解决方案中时,情况早已发生改变。 同样,一些系统依靠请求-响应模式来从事务性数据存储(如 SQL Server Reporting Services 或 SSRS、报告)中请求已更新的数据,像这样的系统总是在接近请求-轮询间隔结束时运行陈旧数据。 轮询间隔通常是固定的,因此即使突然发生有趣的活动,消耗系统也不会知道,直到进入下一个间隔。 相反,消耗系统应该在满足趣味条件时连续收到通知。

在检测新兴趋势时,时间间隔至关重要 - 在过去的五分钟内,一个特定项目发生了 100 次购买,显而易见,这比过去五个月间的持续购买更能指示新兴趋势。 SSAS 和 SSRS 等传统系统需要开发人员通过事务性存储中多维数据集或时间戳列中的单独维度来自行跟踪数据的及时性。 理论上,用于识别新兴情况的工具可能具有时间内置的概念,并能提供使用该工具所需的丰富 API。

最后,对未来的准确指示来源于对过去的分析。 实际上,这就是传统 BI 的所有功能 - 对大量的历史数据进行汇总和分析,从而识别趋势。 遗憾的是,与更多的事务性系统相比,在使用这些系统时需要不同的工具和查询语言。 成功识别新兴情况需要实现过去数据和当前数据的无缝关联。 只有当对这两种数据使用相同的工具和查询语言时,才可能实现这种紧密集成。

对于生产线监视等特定情况,可通过存在的针对性极强的自定义工具来执行这些功能,但是这些工具通常比较昂贵且用途并不广泛。

为了防止生产线产量下降或确保您的产品定价合适,关键在于具有足够的响应能力,能够根据情况的更改而进行识别与调整。 若要轻松快速地识别这些情况,历史查询和实时查询应使用相同的开发人员友好的工具集和查询语言,系统应该以近乎实时的方式来处理大量的数据(大约为每秒成百上千个事件),同时引擎应该足够灵活,能够处理跨越多个问题域的情况。

幸运的是,存在这样的工具。 它称为 Microsoft StreamInsight。

StreamInsight 体系结构概述

StreamInsight 是一种复杂事件处理引擎,它每秒能够处理成百上千的事件,且延迟极低。 它可以由任何进程(如 Windows 服务)托管,也可以直接嵌入任何应用程序。 StreamInsight 具有简单的适配器模型,用于输入和输出数据,并且实时数据和历史数据的查询像任何其他来自任何 Microsoft .NET Framework 语言的程序集一样使用获取的相同 LINQ 语法。 其作为 SQL Server 2008 R2 的一部分授予许可。

StreamInsight 的高级体系结构非常简单:通过输入适配器从各种源收集事件。 这些事件均通过查询进行分析和转换,并且查询结果通过输出适配器分发给其他系统和人。 图 1 显示了这一简单结构。

图 1 Microsoft StreamInsight 高级体系结构

就像面向服务的体系结构关注消息,而数据库系统关注行一样,StreamInsight 等复杂事件处理系统按照事件进行组织。 事件是简单的数据段以及与该数据相关的时间 - 与一天中特定时间的传感器读数或股票行情价格相似。 事件所携带的数据称为它的负载。

StreamInsight 支持三种类型的事件。 点事件是即时且不持续的事件。 间隔事件是其负载与特定时间段相关的事件。 边缘事件与间隔事件相似,但当边缘事件到达时,其持续时间未知。 而系统设置了开始时间,且事件实际上具有无限持续时间,直到另一个边缘事件到达才会为这一事件设置结束时间。 例如,速度计读数可能为点事件,因为它不断更改,但是超市的牛奶价格可能为边缘事件,因为其关联时间较长。 当牛奶的零售价格更改时(比如,由于分销商定价发生更改),新价格的持续时间未知,因此,与间隔事件相比,边缘事件要更为合适。 稍后,当分销商再次更新其定价时,新的边缘事件将覆盖先前定价更改的持续时间,而另一个边缘事件将设置新的价格以便继续。

StreamInsight 中的输入适配器和输出适配器是适配器设计模式的抽象示例。 StreamInsight 引擎在其自有的事件表示上运行,但是这些事件的实际来源可能有较大差异,范围从专有接口到硬件传感器到由企业的应用程序生成的状态消息。 输入适配器将源事件转换为引擎能够理解的事件流。

来自 StreamInsight 查询的结果表示特定商业知识,且能够高度专业化。 将这些结果路由至最合适的地点,这点至关重要。 输出适配器可用于将事件的内部表示转换为打印到控制台的文本、通过 Windows Communication Foundation (WCF) 发送到另一个系统以供处理的消息,甚至 Windows Presentation Foundation 应用程序中图表上的点。 有关使用文本文件、WCF 和 SQL 等的示例适配器可从 streaminsight.codeplex.com 获得。

StreamInsight Queries by Example

乍一看,StreamInsight 查询似乎与从数据库中查询行相似,但是两者之间存在重大差异。 查询数据库时,系统会构造并执行查询,同时返回结果。 如果基础数据发生更改,输出并不会因为已运行查询而受影响。 数据库查询结果表示某一时刻的快照,可以通过请求-响应模式使用。

StreamInsight 查询为现有查询。 随着新输入事件的到达,查询不断响应,并且根据需要创建新的输出事件。

本文中的查询示例来自可供下载的示例解决方案。 这些示例开始较简单,但随着查询语言新功能的引入,功能变得更加强大。 所有查询都使用同一负载类。 以下是一个简单类的定义,该类具有 Region 属性和 Value 属性:

  1.           public class EventPayload {
  2.   public string Region { get; set; }
  3.   public double Value { get; set; }
  4.  
  5.   public override string ToString() {
  6.     return string.Format("{0}\t{1:F4}", Region, Value);
  7.   }
  8. }
  9.        

示例应用程序中的查询使用一台输入适配器和一台输出适配器来进行,输入适配器可随机生成数据,输出适配器只需将各事件写入控制台。 为清晰起见,对示例应用程序中的适配器进行了简化。

若要运行每个查询,请在示例解决方案中取消注释 Program.cs 文件中的行,该示例解决方案可将查询分配给称为“template”的本地变量。

以下是一个基本查询,它通过 Value 属性来筛选事件:

  1.           var filtered =
  2.   from i in inputStream
  3.   where i.Value > 0.5
  4.   select i;
  5.        

具有使用 LINQ 经验的任何开发人员应该非常熟悉此查询。 因为 StreamInsight 使用 LINQ 作为它的查询语言,因此此查询与 LINQ to SQL 查询类似,访问数据库或对 IList 进行内存中筛选。 当事件从输入适配器到达时,其负载将受到检查,并且如果 Value 属性的值大于 0.5,事件将被传递到输出适配器,并在此将其打印到控制台。

应用程序运行时,可以看到事件不断到达输出中。 这实际上是一个推模型。 当事件到达时,StreamInsight 会计算来自输入的新输出事件,这与数据库等拉模型不同,在拉模型中,应用程序必须定期轮询数据源,以查看新数据是否已经到达。 这能与 Microsoft .NET Framework 4 中可用的 IObservable 支持完美结合,我们将在后续章节中对此进行介绍。

使用推模型代替轮询来处理连续数据是个非常好的主意,但是 StreamInsight 的真正功能体现在查询时间相关的属性上。 当事件通过输入适配器到达时,它们获得了一个时间戳。 该时间戳可能来自数据源本身(假设事件表示历史数据,且带有用于存储时间的显示列),或者可以设置为事件到达的时间。 实际上,时间是 StreamInsight 查询语言中的第一个类。

查询通常与标准数据库查询类似,标准数据库查询在尾部粘贴有时间限制符,如“每五秒”或“五秒的时间跨度上每三秒”。例如,以下是一个简单查询,它每五秒查询一次 Value 属性的平均值:

  1.           var aggregated =
  2.   from i in inputStream
  3.     .TumblingWindow(TimeSpan.FromSeconds(5),
  4.     HoppingWindowOutputPolicy.ClipToWindowEnd)
  5.   select new { Avg = i.Avg(p => p.Value)};
  6.        

数据窗口

因为时间概念是复杂事件处理系统的基础必需概念,因此应以简单的方式来使用系统中查询逻辑的时间组件,这点非常重要。 StreamInsight 使用窗口概念来表示按时间分组。 之前的查询使用翻转窗口。 应用程序运行时,查询将每五秒生成单个输出事件(窗口的大小)。 输出事件表示前五秒的平均值。 像 LINQ to SQL 或 LINQ to Object 一样,聚合方法(如 Sum 和 Average)能够将按时间分组的事件汇总为单个值,或可以使用 Select 将输出投影成不同格式。

翻转窗口只是另一种窗口类型的特例:跳跃窗口。 跳跃窗口也有大小,但是它们也具有不等于其窗口大小的跳跃大小。 这表示跳跃窗口可以互相重叠。

例如,窗口大小为五秒、跳跃大小为三秒的跳跃窗口将每三秒生成输出(跳跃大小),提供前五秒的平均值(窗口大小)。 它一次向前跳跃三秒,且持续五秒。 图 2 显示分组为翻转窗口和跳跃窗口的事件流。

图 2 翻转窗口和跳跃窗口

请注意,翻转窗口并不重叠,但是对于跳跃窗口,如果跳跃大小小于窗口大小,则可以重叠。 如果窗口重叠,事件将可能在多个窗口中结束,如同时存在于窗口 1 和窗口 2 中的第三个事件。 边缘事件(具有持续时间)也可能在窗口边缘重叠,并在多个窗口中结束,如翻转窗口中的倒数第二个事件。

另一种常见窗口类型为计数窗口。 计数窗口包含特定数量的事件,而不是某一时间点或时间段内的事件。 要查询最后三个到达的事件的平均数,可能需要使用计数窗口。 计数窗口当前的一个限制是不支持 Sum 和 Average 等内置聚合方法。 您必须创建用户定义的聚合。 下文会对这一简单流程进行介绍。

最后一种窗口类型为快照窗口。 在边缘事件的环境下,快照窗口最容易理解。 每次事件的开始或结束即表示当前窗口的完成和新窗口的开始。 图 3 显示如何将边缘事件分组为快照窗口。 请注意每个事件边界触发窗口边界的方式。 E1 开始,w1 也开始。 当 E2 开始时,w1 完成,而 w2 开始。 下个边缘是 E1 结束,使得 w2 完成,而 w3 开始。 结果为三个窗口:包含 E1 的 w1,包含 E1 和 E2 的 w2 以及包含 E3 的 w3。 事件分组为窗口后,它们会受到拉伸,从而使事件的开始与结束时间与窗口的相同。

图 3 快照窗口

更多复杂查询

在提供可用窗口与基本查询方法(如地点、分组依据和排序依据)的情况下,可以进行多种查询。 以下是一个查询,其将输入事件按地区分组,然后使用跳跃窗口来输出最后一分钟各个 Region 的负载 Value 的总和:

  1.           var payloadByRegion =
  2.   from i in inputStream
  3.   group i by i.Region into byRegion
  4.   from c in byRegion.HoppingWindow(
  5.     TimeSpan.FromMinutes(1),
  6.     TimeSpan.FromSeconds(2),
  7.     HoppingWindowOutputPolicy.ClipToWindowEnd)
  8.   select new {
  9.     Region = byRegion.Key,
  10.     Sum = c.Sum(p => p.Value) };
  11.        

这些窗口使用两秒的跳跃大小,因此引擎每两秒发送输出事件。

因为查询运算符是在 IQueryable 接口中定义的,因此可以撰写查询。 以下代码使用上一个查询,其按地区查找总和,并计算总和最高的地区。 快照窗口允许事件流按总和分类,因此可以使用 Take 方法获取总和最高的地区:

  1.           var highestRegion =
  2.   // Uses groupBy query
  3.   (from i in payloadByRegion.SnapshotWindow(
  4.     SnapshotWindowOutputPolicy.Clip)
  5.     from sumByRegion in i
  6.     orderby sumByRegion.Sum descending
  7.     select sumByRegion).Take(1);
  8.        

一般情况是有关快速移动事件(如传感器中的读数)到慢速移动或静态参考数据(如传感器的固定位置)流的查询。 查询使用联接来实现此目的。

StreamInsight 联接语法与任何其他 LINQ 联接相同,但有一点需要注意:当事件的持续时间重叠时,它们才会联接在一起。 如果传感器 1 在时间 t1 报告了一个值,但是有关传感器 1 位置的参考数据仅对时间 t2 到 t3 有效,那么联接将不匹配。 持续时间的联接条件并没有明确写入查询定义中;这是 StreamInsight 引擎的基本属性。 使用静态数据时,通常情况下,输入适配器实际上将数据处理为带有无限持续时间的边缘事件。 这样将能成功完成到快速移动事件流的所有联接。

通过联接来关联多个事件流是一个非常强大的概念。 装配线、石油生产设施或高容量网站通常不会因为隔离的事件而发生故障。 一个用于触发温度警报的设备部件通常不会导致生产线瘫痪;生产线瘫痪可能由于多个原因造成,如温度在某一持续时间段内过高,同时某一工具使用过多,而操作员正在换班。

如果没有联接,隔离事件将不会有这么多的商业价值。 通过对历史数据使用联接和 StreamInsight 查询,用户可以将隔离流与非常具体的监控条件相关联,然后进行实时监控。 现有查询能够查找可能导致故障的情况,并自动生成可路由至系统的输出事件,该系统知道如何使过热的设备部件脱机,而不是等到该部件造成整条生产线停产。

在零售情况中,有关某段时间按项目划分的销售量的事件可以输入到定价系统和客户订单历史记录中,从而确保每个项目具有最佳的定价,或决定在用户结账前向其推荐的项目。 由于查询易于创建、修改和撰写,因此您可以从简单的情况开始,并随时间的流逝进行优化,从而增加业务价值。

用户定义的聚合

StreamInsight 附带最常见的聚合函数,包括 Count、Sum 和 Average。 当这些函数不够时(或您需要在前文提到的计数窗口进行聚合),StreamInsight 支持用户定义的聚合函数。

要创建用户定义的聚合,其流程包括两个步骤:编写实际聚合方法,然后通过扩展方法将该方法公布到 LINQ。

进行第一步时,如果聚合与时间无关,则从 CepAggregate<TInput, TOutput> 继承,如果聚合与时间有关,则从 CepTimeSensitiveAggregate<TInput,TOutput> 继承。 这些抽象类具有单独的实现方法,称为 GenerateOutput。 图 4 显示 EveryOtherSum 聚合的实现,这种聚合将每个其他事件加起来。

图 4 EveryOtherSum 聚合

  1.           public class EveryOtherSum :
  2.   CepAggregate<double, double> {
  3.  
  4.   public override double GenerateOutput(
  5.     IEnumerable<double> payloads) {
  6.  
  7.     var sum = default(double);
  8.     var include = true;
  9.     foreach (var d in payloads) {
  10.       if (include) sum += d;
  11.       include = !include;
  12.     }
  13.     return sum;
  14.   }
  15. }
  16.        

进行第二步时,需要在 CepWindow<TPayload> 上创建扩展方法,以便可以在查询中使用您的聚合。 CepUserDefinedAggregateAttribute 适用于扩展方法,以便通知 StreamInsight 在哪里可以找到聚合的实现(在这种情况下,类是在第一步中创建的)。 在可下载的示例应用程序中,本流程两个步骤的代码均可在 EveryOtherSum.cs 文件中找到。

更多适配器信息

查询表示对适配器提供的数据进行操作的业务逻辑。 示例应用程序使用一台简单输入适配器和一台输出适配器来进行,输入适配器可生成随机数据,输出适配器可将数据写入控制台。 它们均遵循相似的模式,CodePlex 网站上提供的适配器也遵循这一模式。

StreamInsight 使用 Factory 模式来创建适配器。 给定配置类后,工厂可创建相应适配器的实例。 在示例应用程序中,输入适配器和输出适配器的配置类都非常简单。 输出适配器配置具有保存格式字符串的单个字段,可在编写输出时使用。 输入适配器配置具有填写生成随机事件之间睡眠时间的字段,也具有另一个称为 CtiFrequency 的字段。

CtiFrequency 中的 Cti 代表当前时间增量。 StreamInsight 使用 Cti 事件来帮助确保事件以正确的顺序传递。 默认情况下,StreamInsight 支持不按顺序到达的事件。 当通过查询传递事件时,引擎将自动对事件进行相应的排序。 然而,这一重新排序具有一定的限制。

假设事件真的能够以任意顺序到达。 那么怎么能够确定最早的事件已经到达,并因此通过查询来推送? 这不可能,因为下一个事件的时间可能比您收到最早事件的时间更早。 StreamInsight 使用 Cti 事件来通知引擎比已接收事件更早的事件将不会到达。 Cti 事件实际上提示引擎去处理已经到达的事件,随后忽略或调整任何带有早于当前时间的时间戳的事件。

示例输入适配器生成排序事件流,因此它在每个生成的事件后自动插入一个 Cti 事件,以便保持流程的进行。 如果您已编写输入适配器,而您的程序没有产生输出,则请确保您的适配器插入了 Cti,因为如果没有 Cti,引擎将一直等下去。

StreamInsight 附带了适配器的各种基本类:特型、泛型、点型、间隔型和边缘型。 特型适配器总是产生带有常见负载类型的事件 - 在示例案例中,为 RandomPayload 类。 泛型适配器适用于可产生多种事件类型的事件源,或不能提前得知行布局和内容的事物,如 CSV 文件。

示例输入适配器具有常见负载类型,可生成点事件,因此其继承自 TypedPointInputAdapter<RandomPayload>。 基本类具有两个必须实现的抽象方法:Start 和 Resume。 在示例中,Start 方法使得计时器在配置指定的间隔内触发。 计时器的 Elapsed 事件运行 ProduceEvent 方法,该方法完成适配器的主要工作。 此方法的主体遵循通用模式。

首先,适配器检查引擎自上次运行后是否已停止而现在仍在运行。 然后,调用基本类中的一种方法来创建点事件的实例,其负载已设置且事件已排列在流中。 在示例中,SetRandomEventPayload 方法可代替任何真实适配器逻辑 - 例如,读取文件、与传感器对话或查询数据库。

输入适配器工厂也非常简单。 它实现了接口 ITypedInputAdapterFactory<RandomPayloadConfig>,因为它是特性适配器的工厂。 本工厂的唯一特点在于它也实现了 ITypedDeclareAdvanceTimeProperties<RandomPayloadConfig> 接口。 此接口允许工厂处理前文所述的 Cti 插入操作。

示例应用程序的输出适配器遵循的模式与输入适配器基本相同。 包括配置类、工厂与输出适配器本身。 适配器类与输入适配器十分相似。 主要区别是适配器从队列中移除事件,而不是对其进行排队。 因为 Cti 事件与其他事件相似,它们也到达输出适配器,并很容易被忽略。

可观察量

虽然适配器模型十分简单,但还可以使用以下一种更简单的方式来将事件输入和输出引擎。 如果应用程序使用的是 StreamInsight 的内嵌部署模型,则您可以使用 IEnumerable 和 IObservable 作为引擎的输入和输出。 给定一个 IEnumerable 或 IObservable,您可以通过调用所提供的扩展方法(如 ToStream、ToPointStream、ToIntervalStream 或 ToEdgeStream)之一创建输入流。 这将创建一个看上去与输入适配器创建的事件流极为相似的事件流。

同样,给定一个查询,扩展方法(如 ToObservable/Enumerable、ToPointObservable/Enumerable、ToIntervalObservable/Enumerable 或 ToEdgeObservableEnumerable)会分别将查询输出路由至 IObservable 或 IEnumerable。 这些模式特别适用于重播保存在数据库中的历史数据。

使用 Entity Framework 或 LINQ to SQL 创建数据库查询。 使用 ToStream 扩展方法将数据库结果转换为事件流,并定义关于该事件流的 StreamInsight 查询。 最后,使用 ToEnumerable 将 StreamInsight 结果路由至方便您 foreach 并打印的位置。

部署模型和其他工具

若要使用 Observable 和 Enumerable 支持,必须在您的应用程序中嵌入 StreamInsight。 但是 StreamInsight 不支持独立模型。 在安装时,系统会询问您是否创建 Windows 服务以托管默认实例。 该服务可随后托管 StreamInsight,允许多个应用程序连接到相同的实例并共享适配器和查询。

通过共享服务器而非嵌入的服务器来进行的通信会使用 Server 类上的一种不同的静态方法。 不调用具有实例名称的 Create,而是调用 Connect,其带有指向共享实例的 EndpointAddress。 此部署策略更适用于企业情况,在此情况下,多个应用程序可能需要使用共享的查询或适配器。

在两种情况下,有时需要弄清楚为什么 StreamInsight 生成的输出不是应该生成的输出。 该产品附带名为 Event Flow Debugger 的工具,以用于此用途。 本文不介绍该工具的使用方法,但总而言之,该工具允许您连接到实例并通过查询跟踪输入和输出事件。

灵活、反应迅速的工具

灵活的部署选项、熟悉的编程模型和可轻松创建的适配器使得 StreamInsight 成为各种情况下的好选择。 从查询并在一秒内关联数以千计的传感器输入的集中式实例到在单个应用程序中监控当前事件和历史事件的嵌入式实例,StreamInsight 均采用开发人员友好的框架(如 LINQ)来实现高度自定义的解决方案。

易于创建的适配器以及用于在事件流与 IEnumerable 和 IObservable 之间进行转换的内置支持使得它能够快速找到解决方案并运行,从而增加封装了特定商业知识的查询的创建和完善工作。 在完善过程中,这些查询提供越来越多的值,使得应用程序和组织能够在发生有趣情况时进行识别并做出反应,而不错过处理的机会。

Rob Pierry 是 Captura (capturaonline.com) 的首席顾问,其中 Captura 是一家咨询公司,提供由可扩展技术支持的创新用户体验。您可以通过 rpierry+msdn@gmail.com 与他联系。

衷心感谢以下技术专家对本文的审阅:Ramkumar Krishnan、Douglas Laudenschlager 和 Roman Schindlauer

 

作者: 自由、创新、研究、探索……
出处:http://shanyou.cnblogs.com/
版权:本文版权归作者和博客园共有
转载:欢迎转载,为了保存作者的创作热情,请按要求【转载】,谢谢
要求:未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任 
posted on 2012-05-28 09:28  HackerVirus  阅读(236)  评论(0编辑  收藏  举报