在ESFramework 4.0 进阶(09)-- ESPlatform 支持的三种群集模型一文中,我们介绍了ESPlatform支持的三种群集模型 -- 垂直分割模型、水平分割模型、交叉模型。我们看到,在垂直分割模型和交叉模型中,每个客户端都要与多个应用服务器AS进行通信,这就要求客户端与多个AS中的每一个都建立一条通信通道,如此才能保证客户端能获得服务端提供的完整的服务。
在ESPlatform中有一系列基础设施和组件来支持这样的结构,而多通道引擎就是其中的一个关键组件。首先要注意,多通道引擎的说法对客户端才有意义,因为服务端引擎都是多通道的 -- 服务端与每个客户端之间都有一个通道。所以,当我们提到多通道引擎时,一定指的是多通道客户端引擎。在ESFramework 4.0 进阶(03)-- 驱动力:通信引擎 中,我们介绍的基础的客户端引擎都是单通道的,即一个客户端引擎实例只与一个AS通信。单通道客户端引擎是ESFramework提供的基元组件,我们可以将多个单通道引擎实例组装起来,构成一个多通道引擎。实际上,ESPlatform已经为我们做好了这件事,那就是ESPlatform.Paasive.MultiChannelEngine。
一.多通道引擎的结构
MultiChannelEngine 内部集成了多个单通道引擎(ESFramework.Passive.IPassiveEngine),每个引擎实例与一个对应的AS通信。示意图如下所示:
(1)首先,MultiChannelEngine 也实现了ESFramework.Passive.IPassiveEngine接口(Composite模式),在任何使用IPassiveEngine的地方,都可以使用MultiChannelEngine来替换对应的单通道引擎。
(2)MultiChannelEngine 内部集成的是IPassiveEngine实例,所以,其既可以支持客户端的TCP引擎集成,也支持客户端UDP引擎的集成,甚至可以交叉搭配。比如,AS01通过TCP提供服务,而AS02通过UDP提供服务,那么engine1实例就使用TCP客户端引擎,engine2就使用UDP客户端引擎。
(3)所有的引擎实例都公用同一个消息处理骨架流程实例,即公用同一个IMessageDispatcher对象来分派处理消息。这种设计可以使配置非常简单。
二.再论MessageType
接下来,我们需要考虑一个问题,那就是当我们要通过MultiChannelEngine发一个消息给服务端时,该选择哪个引擎实例、即哪个通道进行发送了 ?
在ESFramework中,使用MessageType来标志消息的类型,每一个消息的消息头MessageHeader都有一个MessageType属性,以说明当前消息的类别。在ESFramework 1.0之前的版本中,MessageType原名叫ServiceKey或ServiceType,即服务类型。其隐藏的含义是,每一个消息类型都对应着服务端提供的一种服务。所以,在ESFramework 4.0 进阶(09)-- ESPlatform 支持的三种群集模型 一文中,我们在提到“服务”时,经常都加备注说明就是“MessageType”。可以这么认为,ESFramework中的MessageType就是服务的类型。
在垂直分割模型中,对服务加以拆分,实际就是对MessageType进行拆分,使不同的AS处理不同的消息类型。比如,AS01处理A类型的业务,其对应MessageType为101~200的消息;而AS02处理的B类型业务,其对应MessageType为201~300的消息。基于这样的认识,我们刚提出的问题就就解决了,那就是如果客户端要发送的消息类型在101~200之间,则使用engine1进行发送;若要发送的消息类型在201~300之间,则使用engine2进行发送。
至于每个引擎实例能处理哪些消息类型,ESFramework使用自描述的PassiveEngineUnit类进行封装:
MessageTypesAllowed属性是一个集合,包含了当前引擎实例允许发送的所有消息类型。所以,MultiChannelEngine的内部集成了多个PassiveEngineUnit,它就据此知道即将发送的消息要交给哪个引擎实例去发送。
三.Default引擎
我们再来考虑一个问题,在垂直分割群集模型中,当多个AS中的某一个挂掉,或者客户端与某个AS的连接突然断开时,该如何处理?出现这种情况是非常严重的,因为,此时客户端就不能获得服务端提供的完整的服务了。ESPlatform是如何解决的了?
ESFramework提供的多通道引擎MultiChannelEngine是支持Default模式的,我们再将上面的结构图稍作变形:
相比于原来的结构图,该图增加了一个default AS应用服务器,多通道引擎内部也增加了一个default engine。ESPlatform的垂直群集模型中的default模式描述如下:
(1)default AS 必须能处理所有类型的消息,即default AS 能提供完整的服务端服务。
(2)当AS01,AS02,AS...指定了要处理的消息类型后,剩下的未被指定的消息类型则统统由default AS处理(因为default AS能处理所有类型的消息,所以这点肯定没问题)。MultiChannelEngine内部也一样,没有被其它引擎实例允许发送的消息类型统统交由default engine发送。
(3)当某个引擎实例与对应的AS的连接断开或该AS挂掉时,原本由该引擎实例发送的类型的消息全部交由default engine发送,这些消息将被default AS处理。当挂掉的AS恢复正常且对应的引擎实例重连成功后,指定类型的消息还是由原引擎实例发送。如此看来,default AS和default engine就充当了冗余后备的角色,以增强整个系统的健壮性。
(4)我们的建议是,更进一步,除default AS外,其它的AS将所有的消息类型全部分配完,这样,在正常情况下,default AS 和 default engine是没有任务的,只有在出现异常状况时,default AS 和default engine才作为后备上场。
(5)更进一步,ESPlatform也支持使用多个default AS和多个default engine,因为单个default AS也有可能出状况啊。至于实际上需要部署多少个default AS,取决于你项目的具体要求。
(6)要特别强调,default AS 能作为后备顺利上场接替原AS是有前提的 -- 最好的情况是,所有的AS都是无状态的(stateless),即AS不需要保存用户的任何状态,或者,同一个用户发过来的前后两条消息是没有逻辑关联的。
如果我们的应用一定要保存用户的某些状态数据,并且一定要依靠这些状态数据才能处理用户的后续消息,这种情况就不能随意让default AS接替原AS了。解决的办法也是有的,比如说:
a.把状态数据从AS中迁移出来,放到一个default AS 也能够访问的地方,这样普通AS就是无状态的,等必要时切换到default AS,业务也能正常继续执行。
b.在设计时,所有涉及到用户状态管理的消息类型都交给default AS处理,这样普通的AS就是没有状态的了。
c.在设计时,所有涉及到用户状态管理的消息类型交给固定的几个普通AS处理,但是客户端与这些消息类型相关的业务逻辑代码在引用IPassiveEngine时,就直接使用对应的普通引擎对象,而不是使用MultiChannelEngine对象。
这几个方案在ESPlatform中都是被支持的,但就我们的经验来说,保证普通AS无状态是最简单也是最容易实施的方案。只有到迫不得已的时候,才采用方案C。当然就具体的项目而言,可能还存在其它的解决方法,设计者可以在ESPlatform提供的基础设施上自由发挥。
四.MultiChannelEngine类
前面做了这么多铺垫,再来理解本文的主角MultiChannelEngine就非常容易了。
(1)MultiChannelEngine由多个Default引擎和多个普通引擎构成。
(2)客户端状态以default引擎为准。
(3)Initialize/Start/Stop/InitializeAndStart方法以及所有属性set等方法会逐个调用内部所有的引擎。
(4)当向服务器发送一条消息时,首先看是否有普通引擎允许该消息通过,如果有这样的普通引擎,则采用该普通引擎发送;否则通过default引擎列表中第一个可用的default引擎发送。
(5)如果要发送的是心跳消息,则会将其使用每个引擎发送一遍,以保证每个引擎实例都不会超时掉线。
在项目中具体使用时,我们只要装配好MultiChannelEngine对象,然后,在以前所有使用IPassiveEngine的地方,更改为引用这个MultiChannelEngine对象就可以了。如果是使用类似Spring.net的IOC容器,那么通过修改配置文件就可以很快地做到这一点。
本文我们探讨的是客户端与服务器之间的多通道结构,实际上,当在客户端启用了P2P Channel时,客户端与客户端之间的也是多通道的模型,只不过这种多通道模型是依据消息的接收者而不是消息的类型,来决定当前消息应该使用哪一条通道进行发送的。关于P2P Channel及其管理,已经超出了本文探讨的范围,这里就不多说了。
另外,虽然ESPlatform水平分割群集模型在相对简单的应用中只需要单通道即可,但是某些情况下,也需要做一些扩展,比如“租借”更多的通道来减少服务端跨服务器的P2P消息转发 -- 而这也正是我们在实践ESPlatform水平分割群集模型时,觉得最有效的模式。后面我们将撰文详细介绍ESPlatform水平分割群集模型的这种扩展模式,以及ESPlatform对支持这种模式所提供的基础设施和组件。敬请关注,谢谢。