在前面的章节中,我们了解到Mussel是一个基于插件的应用框架。Mussel内核的本身不包含任何方面的功能,它只是负责对我们所编写的插件项目进行协调,维护起插件项目之间的依赖关起,并构建起插件项目之间的沟通渠道。通过编写各种不同的应用插件,我们可以无限的扩展Mussel的功能。同时,Mussel还可以按照用户指定的需求在进程中开辟不同的AppDomain,将不同职能的插件载入不同的AppDomain中,亦可以将相类似职能的不同插件加入到同一个AppDomain。在今天的讲述中,我们将看到如何利用Mussel默认提供的通信及Proxy生成器插件来开发一个简单的C/S(客户端/服务器)应用。
我们先来准备一下解决方案,如图:
这是一种非常典型的C/S应用架构,由Client(客户端)、Contract(调用规范)、Implement(调用实现)及Server(服务端)四部分组成。我们也可以推理出:调用实现是位置入服务端的,客户端凭调用规范对服务端进行调用。
我们先来看看调用规范(接口)
- namespace Unit10
- {
- public interface IShowMessage
- {
- void SayTime();
- int Calc(int a, int b);
- }
- }
正如您所见来的一样,这个规范非常的简单。没什么好说的,我们直接来看看实现:
- namespace Unit10
- {
- [MusselType("ShowMessage")]
- public class ShowMessage:AddinItem,IShowMessage
- {
- public void SayTime()
- {
- Console.WriteLine("Current Time:{0}", DateTime.Now);
- }
- public int Calc(int a, int b)
- {
- Console.WriteLine("Client Call {0} + {1}!", a, b);
- return a + b;
- }
- }
- }
实现代码同样非常简单——ShowMessage类从AddinItem继承,我们回顾一下前面章节所讲述的Mussel内容便可以知道:从AddinItem继承的类型,即是一个标准的Mussel插件项目。而MusselType特性则用于标识这是一个需要被Mussel加载管理器注意的类型,并给这个类型一个全局惟一的标识符。Mussel加载器在加载配置文件时,会依据这个类型标识符匹配相应的类型。
接口及实现代码均已完成,现在我们该如何将他们构建成一个C/S的分布式应用呢?先来看看我们已经完成的服务端代码:
- namespace Unit10
- {
- class Program
- {
- static void Main()
- {
- MusselService service = new MusselService();
- service.Start();
- Console.WriteLine("Server is started!");
- Console.ReadLine();
- }
- }
- }
可能看到这些代码后大家非常疑惑:在这里并没有任何的针对ShowMessage操作的代码,那么ShowMessage插件项目是如何载入的呢?首先MusselService是一个加载器类型,在前面的章节我们讲到过,Mussel的加载器在Start时是会读取执行文件所处文件夹下面的所有*.addin文件,这些addin文件便是Mussel的插件配置,里面详细描述了Mussel的插件加载信息。我们来看看服务端的这个addin文件。
- <?xml version="1.0" encoding="utf-8" ?>
- <Addin Name="Core" CreateNewDomain="true">
- <ReferenceAssemblies>
- <Reference AssemblyFile="Mussel.Communication.IceServicePortal.dll" IsMusselAssembly="true"/>
- <Reference AssemblyFile="Unit10.Implement.dll" IsMusselAssembly="true"/>
- </ReferenceAssemblies>
- <AddinNode Path="/Mussel/Core">
- <AddinItem ClassKey="BasicFormatter"/>
- <AddinItem ClassKey="ShowMessage"/>
- <AddinItem ClassKey="SimpleServiceSiteFactory"/>
- <AddinItem ClassKey="IceServicePortal"
- ServiceSiteFactoryKey="/Mussel/Core,SimpleServiceSiteFactory"
- AdapterId="MusselCommunicationAdapter"
- ObjectId="IceServicePortal"
- Endpoint="tcp -p 4300"
- />
- </AddinNode>
- </Addin>
我们来解释一下这个插件配置文件:
- <Addin Name="Core" CreateNewDomain="true">,我们在这里标识了这个插件的名称为Core,并且这个插件需要在一个新的AppDomain中创建。
- 在“ReferenceAssemblies”节点,用于指示相关一些程序集的引用,由于我们会将其它需要引用的程序集直接放入执行文件所处的目录中,所以我们可以不一一指出。只需要指出一些需要从中搜寻并加载Mussel插件项目的一些程序集(IsMusselAssembly="true")。
- <AddinNode Path="/Mussel/Core">,这个指明插件项目的节点路径,不明白的可以翻阅以前章节的讲解。
- <AddinItem ClassKey="BasicFormatter"/>,指示需要在当前节点加载MusselType标识字为"BasicFormatter"的插件项目。这是一个类型的序列化及反序列化器,由Mussel内核直接提供,用于通信。
- <AddinItem ClassKey="ShowMessage"/>,指示需要在当前节点加载MusselType标识字为"ShowMessage"的插件项目。这个便是我们实现出来的插件项目。
- <AddinItem ClassKey="SimpleServiceSiteFactory"/> ,指示需要在当前节点加载MusselType标识字为"SimpleServiceSiteFactory"的插件。这个插件项目由 Mussel.Communication.IceServicePortal.dll 程序集提供,是一个基于ICE通信的一个简单型服务门户站点构建工厂,配合IceServicePortal插件项目使用。在这个程序集中,还提供了相对复杂应用的一些服务门户构建工厂。关于服务门户方面的具体知识,大家可以参阅我在前面章节讲述的AOP指令路由。 在这里只需要知道这个插件项及下面的插件项目共同构筑网络的通信通道。
- <AddinItem ClassKey="IceServicePortal" ,指示需要在当前节点加载MusselType标识字为"IceServicePortal"的插件,这是一个服务门户插件。这个插件包含几个特性:
- ServiceSiteFactoryKey,门户站点构建工厂插件项目的全路径
- AdapterId,ICE通信架构的适配器标识
- ObjectId,ICE通信架构的对象标识(客户端与服务端一致)
- Endpoint,通信的端口及协义描述,在这里我们可以看到我们在本地主机4300端口上监听TCP通信。
OK,服务端配置完毕,我们试运行一下,用 netstat -an 指令可以检测到,4300端口已打开,ICE通信服务正常运行。
接下来,我们来看看客户端,因为客户端程序同样会用到Mussel加载起一些插件项目,所以,我们先来看看插件配置:
- <?xml version="1.0" encoding="utf-8" ?>
- <Addin Name="Core" CreateNewDomain="true">
- <ReferenceAssemblies>
- <Reference AssemblyFile="Mussel.Communication.IceServicePortalProxy.dll" IsMusselAssembly="true"/>
- <Reference AssemblyFile="Mussel.DynamicProxy.RemoteAddinItemProxyFactory.dll" IsMusselAssembly="true"/>
- </ReferenceAssemblies>
- <AddinNode Path="/Mussel/Core">
- <AddinItem ClassKey="BasicFormatter"/>
- <AddinItem ClassKey="SimpleIceServicePortalProxy"
- ObjectId="IceServicePortal"
- Endpoint="tcp -h 127.0.0.1 -p 4300"/>
- <AddinItem ClassKey="RemoteAddinItemProxyFactory"
- Formatter="/Mussel/Core,BasicFormatter"
- DefaultServicePortalProxy="/Mussel/Core,SimpleIceServicePortalProxy"/>
- </AddinNode>
- </Addin>
同样,我们也先来解释一下客户端的配置文件,前面的加载信息我们略过,从插件讲起:
- <AddinItem ClassKey="BasicFormatter"/>,同服务端一样,在当前节点加载序列化器插件项目。
- <AddinItem ClassKey="SimpleIceServicePortalProxy",这个是由"Mussel.Communication.IceServicePortalProxy.dll"程序集提供的一个插件项目,用于在客户端建立到服务端的ICE连接。所以可以认为他是一个服务端IceServicePortal的Proxy,他有ObjectId及Endpoint两个特性,这两个特性与服务端对应。
- <AddinItem ClassKey="RemoteAddinItemProxyFactory"这个是由"Mussel.DynamicProxy.RemoteAddinItemProxyFactory.dll"程序集提供的一个插件项目,用于根据调用的接口生成一个服务端服务型插件项目位于客户端的Proxy,例如可以为我们的IShowMessage生成一个客户端的Proxy。这个插件项目需要指明序列化器及默认的ServicePortalProxy。
了解了上面的配置,我们再来看看客户端的程序代码:
- namespace Unit10
- {
- class Program
- {
- static void Main(string[] args)
- {
- MusselService service = new MusselService();
- service.Start();
- IRemoteAddinItemProxyFactory factory =
- (IRemoteAddinItemProxyFactory)
- service.Container["/Mussel/Core,RemoteAddinItemProxyFactory"];
- IShowMessage showmessage =
- factory.CreateProxy<IShowMessage>("/Mussel/Core,ShowMessage", null);
- showmessage.SayTime();
- Console.WriteLine("{0} + {1} = {2}", 4, 5, showmessage.Calc(4, 5));
- Console.ReadLine();
- }
- }
- }
在上面的代码中,我们先从Mussel加载器的Container中取出一个远程插件项目的Proxy生成器,然后通过这个生成器产生了一个IShowMessage的客户端Proxy对象,于是我们就可以像调用本地对象一样调用服务端的方法了:
程序运行截图:
本文首发自:http://www.rogertong.cn/article.asp?id=25