核心梳理——消息处理的骨架流程——ESFramework 4.0 进阶(02)

ESFramework 4.0 概述一文中,咱们提到ESFramework.dll做为通讯框架的核心,定义了消息处理的骨架流程,本文咱们来详细剖析这个流程以及该骨架中所涉及的各个组件。ESFramework的骨架流程以下图所示: html

一.全部的网络引擎都使用同一消息处理骨架流程缓存

      ESFramework支持TCP/UDP、二进制协议/文本协议、服务端/客户端组合而成的2x2x2=8种引擎,不管是哪种引擎,都实现了INetEngine接口,也都使用上图所示的消息处理骨架流程来处理所接收到的全部消息。服务器

    因此,只要掌握了这一消息处理的骨架流程,就掌握了ESFramework的核心机密。也只有掌握了该骨架流程,咱们才能轻松自如地驾驭ESFramework。网络

      在该骨架流程中,涉及了多个消息组件,像消息分派器IMessageDispatcher、消息管道IMessagePipe、消息转换器IMessageTransformer、消息监控器IMessageSpy、消息内层分派器NakeDispatcher、消息处理器工厂IMessageProcesserFactory、以及消息处理器IMessageProcesser。框架

     注意,MessagePipe能够看作是MessageTransformer以及GatewayMessageSpy和InnerMessageSpy的封装。NakeDispatcher内部则包含了MessageProcesserFactory,而且利用MessageProcesserFactory来建立MessageProcesser。post

    网络引擎从网络接收到一个消息后会交给MessageDispatcher进行分派,MessageDispatcher在分派消息的时候,首先会让接收到的消息通过MessagePipe进行必要的监控和转换,而后调用NakeDispatcher对消息进行最终分派处理并获取其返回应答消息,而后再让应答消息通过MessagePipe进行必要的监控和转换,最终返回给网络引擎,网络引擎会将最后获得的应答消息经过网络发送出去。加密

     消息处理的骨架流程图中使用箭头表示出了消息在框架中的流动路径与方向,能够看到,消息进入的路径与返回的路径是对称的。而消息由进入转向为返回的转折点是在消息处理器MessageProcesser,即消息处理器处理接收到的消息并返回应答消息。若是没有应答消息,则消息的路径到达MessageProcesser节点被处理后即终止了。spa

 

二.消息分派器MessageDispatcher插件

    消息分派器IMessageDispatcher的基础接口定义以下: 调试

复制代码
    public interface IMessageDispatcher
    {
        IAgileLogger EsfLogger{set ;}
        IMessagePipe MessagePipe { set; }        
        INakeDispatcher NakeDispatcher{set ;}   
         
        IMessage DispatchMessage(IMessage reqMsg) ;
    }  
复制代码

      首先,消息分派器须要使用MessagePipe组件和NakeDispatcher组件,它其实是要保证一个流程,即在实现DispatchMessage方法的时候,让进入的消息必须先通过消息管道MessagePipe,而后才提交给NakeDispatcher去处理,若是有应答消息,则还要保证应答消息也必须通过MessagePipe以后,才能返回给网络引擎。IMessageDispatcher接口的实现--MessageDispatcher类,保证了这一点。

     其次,DispatchMessage方法的参数便是接收到的进入的消息,返回值便是应答消息,若是没有应答,则返回值为null。

     再次,框架要求DispatchMessage方法毫不能抛出异常,不然,这个异常将冲击到网络引擎组件,这可能致使对应链接或Session上的后续消息将不能被接收。

    ESFramework.Core.MessageDispatcher类确保了DispatchMessage方法不会抛出异常,由于其在实现DispatchMessage方法的内部截获了全部的异常,并将其经过IAgileLogger 记录到日志。这就是为何IMessageDispatcher须要注入EsfLogger属性的缘由。(关于ESFramework中采用的日志模式的更多信息,能够参见ESFramework 4.0 快速上手 -- 异常日志 )

     通常来讲,DispatchMessage内部抛出异常的来源一般是两个:

(1)消息处理器抛出异常。因为真正处理消息的是咱们应用程序提供的消息处理器,因此若是在业务处理的过程当中没有捕获抛出的异常,那么这个异常最终会被框架的消息分派器捕获。

(2)消息管道在监控或变换消息时抛出的异常。好比,加密解密失败等。

 

三.消息管道MessagePipe

 

    MessagePipe由MessageTransformer以及GatewayMessageSpy和InnerMessageSpy构成,其主要做用是监控和变换消息。

    IMessagePipe接口的定义以下:

复制代码
    public interface IMessagePipe
    {
        IMessageTransformerMessageTransformer{ set;}

        /// <summary>
        /// 工做于网关层,网络组件收到的消息须要通过的第一个组件就是GatewayMessageSpy,
        /// 发送的消息在到达网络组件前通过的最后一个组件也是GatewayMessageSpy
        /// </summary>
        IMessageSpy GatewayMessageSpy { set; }

        /// <summary>
        /// 接收的消息到达处理器以前通过的最后一个组件就是InnerMessageSpy,
        /// 处理器返回的结果消息通过的第一个组件也是InnerMessageSpy
        /// </summary>
        IMessageSpy InnerMessageSpy { set; }    

        /// <summary>
        /// 消息在发送以前通过管道处理。
        /// </summary>        
        IMessage PipeOutMessage(IMessage msg);

        /// <summary>
        /// 接收到的消息在被NakeDispatcher分派以前通过管道处理。
        /// </summary>
        IMessage PipeInMessage(IMessage msg);

        /// <summary>
        /// 当消息在Pipe中传递被Pipe丢弃时,触发此事件。
        /// </summary>
        event CbGeneric<IMessage> MessageForbidden;
    }
复制代码

     当消息由MessageDispatcher分派给MessagePipe时,首先通过的组件是GatewayMessageSpy,其次通过MessageTransformer,最后通过InnerMessageSpy到达NakeDispatcher组件;而NakeDispatcher返回的应答消息所通过的路径恰好与此相反。ESFramework.Core.MessagePipe类的PipeInMessage方法和PipeOutMessage方法的实现保证了这一点。

 

1.消息监控器MessageSpy

     GatewayMessageSpy和InnerMessageSpy都是MessageSpy类型的组件,其都继承自IMessageSpy接口: 

复制代码
    public interface IMessageSpy
    {
        /// <summary>
        /// 监控全部收到的消息,如请求消息,返回false代表丢弃消息。
        /// </summary>       
        bool SpyMessageReceived(IMessage msg); 

        /// <summary>
        /// 监控全部即将发送的消息,如回复消息,返回false代表丢弃消息。
        /// </summary>       
        bool SpyMessageToBeSent(IMessage msg);
    }
复制代码

      对于进入的消息,将调用SpyMessageReceived方法对其监控;而对于返回的消息,将调用SpyMessageToBeSent方法对其监控。

  之因此称为“监控”,是由于MessageSpy不会修改消息,它不会改变消息的任何内容;而负责修改或变换消息的是MessageTransformer组件。这一点正是MessageSpy和MessageTransformer的本质区别所在,不一样类型的消息组件,职责不同。

     至于MessageSpy要作什么样的监控,是由咱们具体的应用决定的,好比,咱们的应用须要记录接收到的最后10条“原始”的消息,那么就能够实现一个MessageSpy,挂接在MessagePipe的GatewayMessageSpy属性上便可。

     除了监控以外, MessageSpy还有个特权 -- 它能决定放行哪些消息、丢弃哪些消息,这由SpyMessageReceived方法和SpyMessageToBeSent方法返回的bool值体现出来。若是返回值为false,则表示丢弃该消息。消息被丢弃后,到此终结,不会再被传递到下一个消息组件。

     通常在什么状况下,MessageSpy会丢弃消息了?好比说,被监控的消息格式不正确,消息多是黑客模拟的"恶意"的消息;又好比说,在使用UDP通讯时,接收到的消息是不完整的,等等。这些消息在MessageSpy这个环节就被过滤掉,不会污染到后续的消息组件,特别是不会给消息处理器带来额外的麻烦。

     若是咱们的应用不须要任何消息监控,则可使用一个“占位符”消息监控器挂接到MessagePipe,ESFramework提供了null object模式实现的ESFramework.Core.EmptyMessageSpy做为这个“占位符”供应用直接使用。

 

2.消息转换器 MessageTransformer

  刚刚提到,MessageTransformer对消息能够进行变换,通过MessageTransformer组件的消息,其内容会被改变。这就为咱们对消息的加密、解密、压缩、解压等等提供了挂接点。好比,网络上传递的消息都是加密的,网络引擎接收到这些加密的消息传递给分派器,分派器又传递给了MessagePipe,因此,GatewayMessageSpy做为消息进入MessagePipe的第一个组件,其监控到的就是加密的消息;而接下来当消息通过MessageTransformer被解密后,被传递给InnerMessageSpy的消息是已经解密的、是明文的,这种消息在ESFramework中称为赤裸消息“NakeMessage”。因此,InnerMessageSpy监控到的消息是明文的NakeMessage。当接收到的消息被传递到内层消息分派器NakeDispatcher被分派时,已是NakeMessage,因此内层的消息分派器被称为NakeDispatcher。

     对于NakeMessageDispatcher返回的应答消息,也是明文消息,当其沿出去的方向通过MessageTransformer时,MessageTransformer会对其进行加密,因此,最终网络引擎发送出去的应答消息也是加密的。

     从上面的描述,咱们已经看出,InnerMessageSpy和GatewayMessageSpy所处的位置不一样而决定了它们监控到的消息的状态不同。一般,GatewayMessageSpy看到的消息都是通过加密的、压缩的,是密文;而InnerMessageSpy看到的消息都是已经被解密的、被解压缩的,是明文。因此,你的应用究竟是须要挂接一个GatewayMessageSpy仍是一个InnerMessageSpy,取决于你须要监控到什么状态的消息。

  消息过滤器必须实现IMessageTransformer接口:

复制代码
    public interface IMessageTransformer
    {
        /// <summary>
        /// 截获即将发出去的消息,能够对截获到的消息进行转换,好比加密、压缩等。
        /// </summary>
        /// <param name="msg">即将发送的消息</param>
        /// <returns>通过截获转换获得的结果</returns>
        IMessage CaptureBeforeSendMessage(IMessage msg);    

        /// <summary>
        /// 截获收到的消息,能够对截获到的消息进行转换,好比解密、解压缩等。
        /// </summary>      
        /// <param name="msg">从网络接收到的消息</param>
        /// <returns>通过截获转换获得的结果</returns>
        IMessage CaptureReceivedMessage(IMessage msg) ;        
    } 
复制代码

  对于进入的消息,框架将调用MessageTransformer的CaptureReceivedMessage方法对消息进行转换;对于要出去的应答消息,将调用MessageTransformer的CaptureBeforeSendMessage方法对消息进行变换。

     从IMessagePipe接口的定义咱们看到,其只能挂接一个MessageTransformer实例,若是咱们要挂接多个MessageTransformer,好比,一个MessageTransformer用于加密/解密,一个MessageTransformer用于压缩/解压缩,那该怎么办了?ESFramework提供了一个容器类型MessageTransformer-- ESFramework.Core.ContainerStyleTransformer,它是一个实现了IMessageTransformer接口的容器,内部有一个列表能够容纳多个MessageTransformer实例。因为,ContainerStyleTransformer实现了IMessageTransformer接口,因此,能够将其挂接到MessagePipe组件,而咱们再把须要用到的多个MessageTransformer实例放到这个容器里就解决了MessagePipe须要挂接多个MessageTransformer的问题。

     同MessageSpy的状况同样,若是咱们的应用不须要加密/解密消息等任何变换,则可使用一个“占位符”消息过滤器挂接到MessagePipe,ESFramework提供了null object模式实现的ESFramework.Core.EmptyMessageTransformer做为这个“占位符”供应用直接使用。

 

四.内层消息分派器NakeDispatcher

  刚才已经提到,当进入的消息被传递到NakeDispatcher时,已是明文的了,这个时候,该消息就能够直接被消息处理器处理了。内层消息分派器的接口INakeDispatcher定义以下:

复制代码
    public interface INakeDispatcher
    {
        IProcesserFactory ProcesserFactory { set; }     
  
        /// <summary>
        /// 在最内部分配消息给最终的消息处理器去处理,并返回处理的结果。
        /// </summary>     
        IMessage DispatchMessage(IMessage msg) ;
    }
复制代码

    NakeDispatcher须要经过DispatchMessage方法最终分派并处理消息,且返回应答消息(若是有的话)。消息须要被消息处理器处理,那么从哪里获取正确的消息处理器了?是消息处理器工厂。因此NakeDispatcher须要依赖于ProcesserFactory。ESFramework已经提供了ESFramework.Core.NakeDispatcher类实现了INakeDispatcher接口,供咱们使用,咱们没必要再实现此接口。

 

1.消息处理器工厂 ProcesserFactory

  处理器工厂用于建立或返回正确的消息处理器,IProcesserFactory的接口定义以下:

复制代码
    public interface IProcesserFactory
    {
        /// <summary>
        /// 根据消息的类型建立对应的处理器 。
        /// </summary>
        /// <param name="messageType">消息的类型</param>       
        IMessageProcesser CreateProcesser(int messageType);
    }  
复制代码

     CreateProcesser方法建立或返回什么样的处理器取决于消息的类型。即NakeDispatcher会从消息的头部IMessagHeader(详情请参见ESFramework 4.0 进阶(01) -- 消息)中取出MessageType属性的值,而后调用IProcesserFactory的CreateProcesser方法获取正确类型的处理器。若是工厂中没有对应的处理器存在,CreateProcesser方法将返回null,这种状况通常只会在调试阶段出现,一般是由于为对应的消息尚未定义相应的消息处理器。若是在正式运行阶段出现CreateProcesser返回null,那将是很是严重的,要么是服务端和客户端所引用的程序集的版本不一致,要么是黑客在向服务器发送试探消息。当CreateProcesser返回null时,NakeDispatcher会将详细的信息记录到日志文件,咱们能够从日志中查看到消息的具体内容。

     ESFramework已经提供了ESFramework.Core.ProcesserFactory类实现了IProcesserFactory接口,其内部能够挂接多个消息处理器实例,大部分状况下,咱们直接使用这个实现就好了,不须要再实现本身的处理器工厂。而且,ESFramework.Core.ProcesserFactory类的实现采用的缓存,当根据消息类型寻找处理器时,不须要每次都遍历全部的处理器。

  当NakeDispatcher从IProcesserFactory获取到正确的处理器以后,即将消息提交给处理器进行处理。

    

2.消息处理器MessageProcesser

  消息处理器接口IMessageProcesser定义以下:

复制代码
    public interface IMessageProcesser
    {      
        /// <summary>
        /// 该消息处理器是否可以处理类型为messageType的消息。
        /// </summary>        
        bool CanProcess(int messageType);

        /// <summary>
        /// 处理消息并返回回复消息,若是返回null,表示没有回复消息。
        /// </summary>       
        IMessage ProcessMessage(IMessage message);        
    }    
复制代码

      一个消息处理器可以处理哪些类型的消息,它本身是最清楚的,因此IMessageProcesser提供了CanProcess方法来让咱们能够查询它是否能处理目标类型的消息。

      同须要定义哪些类型的消息同样,继承了IMessageProcesser接口的消息处理器的实现是由具体的应用来作的,当它们定义好以后,咱们即可以将它们挂接处处理器工厂来处理消息了,框架会根据消息的类型来自动调用它们(IOC模式)。     

      附带说一下,之因此采用ESPlus能够加快ESFramework的开发,就是由于ESPlus已经为咱们预约义好了一批消息类型、消息协议以及消息处理器,咱们只要将它们挂接到ESFramework的消息骨架上,就能够运转起来。而若是咱们直接基于ESFramework.dll开发,则这些事情须要咱们本身手动去打造,虽然麻烦一些,可是足够灵活。ESPlus虽然让咱们省了很多事,可是也限制了ESFramework的某些方面,正所谓“有得必有失”。

 

五.实例化一个最简单消息处理流程    

     假设咱们的某个项目不须要任何类型的消息监控,也不须要任何类型的消息变换,仅仅须要挂接两个消息处理器便可。那么,咱们能够经过相似下面的代码来实例化这个处理流程:

复制代码
    //构造MessagePipe
    IMessagePipe messagePipe = new MessagePipe();
    messagePipe.InnerMessageSpy = new EmptyMessageSpy();
    messagePipe.GatewayMessageSpy = new EmptyMessageSpy();
    messagePipe.MessageTransformer= new EmptyMessageTransformer();

    //构造NakeDispatcher
    IMessageProcesser messageProcesser1 = ......;
    IMessageProcesser messageProcesser2 = ......;
    IMessageProcesser[] messageProcessers = new IMessageProcesser[] { messageProcesser1, messageProcesser2 };
    IProcesserFactory processerFactory = new ProcesserFactory(messageProcessers);            
    INakeDispatcher nakeDispatcher = new NakeDispatcher(processerFactory);

    //构造MessageDispatcher
    IMessageDispatcher messageDispatcher = new MessageDispatcher(nakeDispatcher, messagePipe);
复制代码

     上面的组装代码看视简单,却具备很是强的扩展性,若是项目须要,咱们能够构造出很是复杂的流程。甚至,因为采用了接口分离原则,咱们还能够实现本身的消息分派器或处理器工厂,好比,咱们能够实现一个插件处理器工厂,专门从插件中动态地加载所须要的消息处理器(ESPlus提供的ESPlus.Core.Addins.ServerAddinProcesserFactory正是作这件事情的)。

     若是有通讯引擎实例,那么将构造好的messageDispatcher对象挂接到通讯引擎,就可使整个流程运转起来了。

 

  关于ESFramework消息处理的骨架流程,就介绍这么多,下一篇咱们将深刻到ESFramework提供的各类网络引擎内部去看看。that's all,thanks.

相关文章
相关标签/搜索