【CEP】调试StreamInsight官方Demo——HelloInsight

原文链接:http://my.oschina.net/SnifferApache/blog/338550

StreamInsight开发指南(https://technet.microsoft.com/zh-cn/library/ee391564(v=sql.111).aspx)在“输入和输出适配器”后面标注了“旧模型”。

适配器模型遵循如下的状态转移图:

imageimage

其体系结构如图1:

24233217_IxY6.png

{可以看出,StreamInsight主要包括三个部分:输入适配器(Input Adapter)、输出适配器(Output Adapter)以及CEP服务器。}

StreamInsight示例采用的新模型。该示例中创建和使用五个基本实体类型:源、接收器、主题、绑定和处理。其体系结构如图2:

CEP 体系结构概述

{对比图1,可以看出新旧模型的原理没变}

新模型“使用事件源和事件接收器”过程中用到可枚举和可观察的源和接收器,其中IQbservable可以理解为Observable的“远程查询”版本{by赵姐夫blog.zhaojie.me/2010/09/async-programming-and-reactive-framework.html}

HelloInsight系列样例是基于适配器模型的,包括三个项目:

HelloInsight:流中只有一个简单事件(其payload为字符串类型)便停止了,输出这个事件payload的内容;

HelloInsightObservable:事件流中payload为数字,找出流中大于某个值的事件;

HelloInsightEnumerable:在HelloInsight基础上,InputAdapter把txt文件中每行字符串“包装”为事件push到流中,OutputAdapter输出流中每个事件的payload;

1. 运行样例

手札系列的内容相对权威、深入,在这里本文会从初学者角度来提到一些点。

至于开发环境请参考上一篇(http://my.oschina.net/SnifferApache/blog/324541

Demo下载地址(http://pan.baidu.com/s/1qWqKe5Y的CEP目录

HelloInsight和HelloInsightObservable本地直接运行成功,而HelloInsightObservable运行出现如下问题:

类型“System.Reactive.Linq.IQbservable`1<T0>”在未被引用的程序集中定义。必须添加对程序集“System.Reactive.Providers, Version=1.0.10621.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”的引用。 HelloInsightObservable

按照“使用IObservable接口创建StreamInsight程序”这篇教程,第一步需要安装Reactive Extension for .Net 4,接着添加System.CoreEx和System.Reactive程序集引用。但是最新版Rx安装之后,程序集列表中没有System.CoreEx,在只导入System.Reactive .dll的情况下运行出现上面的问题。{!安装Rx的目的仅仅是提供两个dll文件,略显逗逼;}

事实上,安装StreamInsight2.1之后,Reactive会自动安装上,VS中下文program.cs本来出现的错误提示和引用中的黄色叹号立即消失了,这个项目调试正常输出;

推测五星教程以及其他教程中之所以提到的单独安装Reactive Extension for .Net 4,可能是其StreamInsight版本较旧;

BTW,安装Reactive Extension for .Net 4安装之后默认在C盘创建两个文件夹Microsoft Reactive Extensions SDK和Microsoft SDKs,VS2013添加引用栏会自动识别到这些东西,Reactive主要路径如下

路径1:C:\Program Files (x86)\Microsoft Reactive Extensions SDK\v1.0.10621\Binaries\.NETFramework\v4.0

路径2:C:\Program Files (x86)\Microsoft SDKs\Reactive Extensions\v2.0\Binaries\.NETFramework\v4.5

从下图可看出VS自动将V2.0和V1.0的dll混到一起了,所以引用的时候不要重复,而且最好同一版本(否则两个版本的方法名相同,可能会出现方法不明确的错误)。

162259_OR10_576429.png

前面提到的System.CoreEx在V2.0中已经集成到SystemCore中了,所以没有这个dll文件


2. HelloInsight

25224354_cYg5.png

该项目是基于适配器模型的。回顾前面的状态转移图,适配器就是将一种格式的数据转换成另一个格式的数据。输入适配器把传感器、web服务器和数据库等等多样数据转换成事件(Event)格式,push到事件流(Event Stream)中,输出适配器则与之进行相反的操作。

我们首先看这个项目的InputAdapters包括:

HelloInputConfig.cs    //配置类

HelloInputFactory.cs    //适配器工厂

HelloPointInput.cs    //点事件适配器

理论上,适配器工厂会根据情况把任务交给不同类型的适配器,比如Point,Interval或者Edge适配器。样例为了简单期间,只有点事件一种适配器,要不然怎么叫“麻雀虽小,五脏俱全”呢,哈哈;

适配器处理的各类信息肯定要有一定的格式才规范,我们把格式信息抽象为配置类,这便是HelloInputConfig;

具体到HelloPointInput.cs,其处理流程则和状态转移图对照起来了,其中最重要的“生产”环节

private void ProduceEvents()
        {
            var pendingEvent = CreateInsertEvent();
            pendingEvent.StartTime = DateTime.Now;
            pendingEvent.Payload = new HelloPayload
            {
                str = _config.inputString
            };

            EnqueueOperationResult result = Enqueue(ref pendingEvent);
            EnqueueCtiEvent(DateTime.Now);

            Stopped();
         }

构造Insert事件,配置其StartTime和Payload,Enqueue到StreamInsight引擎,在Enqueue一个Cti事件,遍把引擎关掉了,关掉了,关掉了。。。。。

所以呢,功能非常简单,我们再来看看输出适配器(暂时不关心引擎到底会对stream做什么事情。。),同样的,只看最核心的如何消费流中的事件

private void ConsumeEvents()
        {
            PointEvent<HelloPayload> currEvent;
            DequeueOperationResult result;
            while (true)
            {
                if (AdapterState.Running == base.AdapterState)
                {
                    result = Dequeue(out currEvent);
                    if (result == DequeueOperationResult.Empty)
                    {
                        Ready();
                        return;
                    }
                    else
                    {
                        if (currEvent.EventKind == EventKind.Insert)
                        {
                            Console.WriteLine("Output: " +
                            currEvent.Payload.str
                            );

                        }
                        ReleaseEvent(ref currEvent);
                    }
                }
                else if (AdapterState.Stopping == AdapterState)
                {
                    Stopped();
                }
                else
                {
                    return;
                }

            }

        }

大体上,只要适配器还在Running状态,就Dequeue一个事件,输出Insert类型事件的payload。适配器要不在Running状态了,那就结束了。

最后主程序要做的就是搭建Query模版,启动Query

Query query = filteredCepStream.ToQuery(application,//Guid.NewGuid().ToString()
                "HelloInsightQuery", "创建查询,并绑定到输出适配器",
                typeof(HelloOutputFactory), outputConfig, 
                EventShape.Point, StreamEventOrder.ChainOrdered);
            query.Start();

没错,就是这两句,其中application是CEP服务器上的一个应用程序,定义如下

Server server = Server.Create("Default");

Application application = server.CreateApplication("HelloInsight");

filteredCepStream为Microsoft.ComplexEventProcessing.Linq.CepStream<TPayload>类型;

这个类用到了泛型,就好比你明天有一个快递到,快递具体是什么不重要,我们关注你和快递员的沟通、协作过程。

这个强大的类型可以使用linq表达式来定义好流的处理方式(这里就一个字符串没什么好处理的),我们现在是在构建Query模版,启动之后才会生效

var filteredCepStream = from e in cepStream
                                    select e;

cepStream怎么构造呢?

var inputConfig = new HelloInputConfig
                    {
                        inputString = "Hello StreamInsight!"
                    };                
Microsoft.ComplexEventProcessing.Linq.CepStream<HelloPayload> inputStream = 
            CepStream<HelloPayload>.Create("InputStream", typeof(HelloInputFactory), inputConfig, EventShape.Point);

BTW,CepStream<TPayload>是一个牛逼的静态类,有很多静态方法可以用~~

查询启动之后,输入适配器用另外的线程执行相应的方法(ProduceEvents),主线程需要等待适配器线程执行结束。结束之后一定要记得关闭Query哦

那问题来了,我们怎么在主线程获取适配器线程的运行状态呢?

答案是使用DiagnosticView 诊断报告每,具体原理你不用管,你只要知道我们一定会成功就好了,科科

 //流队列为空的时候,适配器会停止运行
            DiagnosticView dv = query.Application.Server.GetDiagnosticView(query.Name);

            //Start()异步启动之后,主程序每隔一秒就询问一下输出适配器的状态,如果不是“Running”,就可以输出统计结果了
            while ((string)dv[DiagnosticViewProperty.QueryState] == "Running")
            {
                Thread.Sleep(1000);
                dv = query.Application.Server.GetDiagnosticView(query.Name);
            }
            // 此函数输出监视到的参数,比如总事件数、查询时间等
            RetrieveDiagnostics(query.Application.Server.GetDiagnosticView(new Uri("cep:/Server/EventManager")), Console.Out);
            
            query.Stop();

让我们看一下效果:

25224357_xeoS.png

创建Query模版,启动Query的方法除了上面提到的,还可以使用QueryTemplate来替代(上一篇提到的复杂样例TrafficJoinQuery就是使用这种方法)

var queryTemplate = application.CreateQueryTemplate("ExampleTemplate", "Description...", cepStream);
var queryBinder = new QueryBinder(queryTemplate);
queryBinder.BindProducer<HelloPayload>("input", inputAdapter, inputConfig, EventShape.Point);
queryBinder.AddConsumer<HelloPayload>("output", outputAdapter, outputConfig, EventShape.Point, StreamEventOrder.ChainOrdered);

var query = application.CreateQuery("ExampleQuery", "Description...", queryBinder);


3. HelloInsightEnumerable

和HelloInsight相比,你只要修改一下输入适配器就ok了(配置类字段改成了txt路径)

构造函数改成读完txt全部内容。。

 private HelloInputConfig _config;
        private List<string> strings;
        private IEnumerator<string> stringEnumerator;

        public HelloPointInput(HelloInputConfig config)
        {
            _config = config;

            var streamReader = new StreamReader(config.fileName);
            strings = new List<string>();
            
            while (!streamReader.EndOfStream)
            {
                strings.Add(streamReader.ReadLine());
            }
            stringEnumerator = strings.GetEnumerator();
            
            streamReader.Close();
        }

再改改ProduceEvents

 private void ProduceEvents()
        {
            while (AdapterState != AdapterState.Stopping)
            {
                if (stringEnumerator.MoveNext())
                {
                    try
                    {
                        var line = stringEnumerator.Current;
                        var pendingEvent = CreateInsertEvent();
                        pendingEvent.StartTime = DateTime.Now;
                        pendingEvent.Payload = new HelloPayload
                        {
                            str = line
                        };
                        EnqueueOperationResult result= Enqueue(ref pendingEvent);
                        if (result == EnqueueOperationResult.Full)
                        {
                            Thread.Sleep(1000);
                            //Ready();
                            return;
                        }
                        EnqueueCtiEvent(DateTime.Now);
                        //Thread.Sleep(1000);
                    }
                    catch
                    {
                        //error handling should go here
                    }
                }
                else
                {
                    break;
                }
            }
            Stopped();

         }


搞定

25230313_WWIo.png

25230328_KMeH.png

手札四无法解决海量数据读取的问题,如果某一行文本没有换行,超长,有可能塞爆string空间。还有一次性全部读取可能也不太合适。

4. HelloInsightObservable


5. 拾遗

5.1 Rx和LINQ

LINQ和Rx都是用来对集合进行操作。LINQ操作的集合实现了IEnumerable接口,能够使用foreach语句遍历集合。而Rx操作的集合实现了IEnumerable,IQueryable集合,这样的集合称之为Observable集合。大家对Enumerable集合可能很熟悉,他是foreach语句的基础,我的另一篇文章对这个有详细介绍,这里就不多说了,下面主要来看看Observable集合。

    Rx对Observable集合进行操作,这个集合的命名是从观察者设计模式得来的,观察者模式的基础是委托和事件,所以要了解这一模式需要理解委托和事件,在这里推荐张子阳的文章C#中的委托和事件。Enumebrable集合中所有的元素在集合中都已经填充好了,是静态的,用户可以使用“拉”的方式从集合中遍历元素进行处理。而Observable集合则不同,在创建该集合时,集合中的元素可能会在以后的某个时间才能添加进去。由于集合注册了事件,一旦集合中的元素到达,就会触发这一事件,将信息“推”到注册者哪里去。


5.2 LINQ查询表达式

HelloInsightObservable项目中,过滤这一步使用了LINQ中的查询表达式

   var query =    from e in stream

         where e.value > 50

         select e;


参考(LINQ之路http://www.cnblogs.com/lifepoem/archive/2011/10/28/2227735.html

方法语法和查询表达式语法互为补充。

方法语法中,Where产生一个经过过滤的sequence;OrderBy生成输入sequence的排序版本;Select得到的序列中的每个元素都经过了给定lambda表达式的转换。

查询表达式语法中,查询表达式总是以from子句开始,以select或者group子句结束。From子句定义了查询的范围变量(range variable),可以认为该变量是对输入sequence的一个遍历,就像foreach做的那样。下面这幅图描述了查询表达式的完整语法:

31114327_cnff.png



6. 对XX项目CEP Project的调试

嗯,理论上说会有很多篇……

①重构和改进HelloInsightObservable

http://my.oschina.net/SnifferApache/blog/360563

转载于:https://my.oschina.net/SnifferApache/blog/338550

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值