RogerTong's Tech Space

文章书傲骨,程序写春秋
posts - 15, comments - 57, trackbacks - 0, articles - 0

在前面的章节中,我们了解到Mussel是一个基于插件的应用框架。Mussel内核的本身不包含任何方面的功能,它只是负责对我们所编写的插件项目进行协调,维护起插件项目之间的依赖关起,并构建起插件项目之间的沟通渠道。通过编写各种不同的应用插件,我们可以无限的扩展Mussel的功能。同时,Mussel还可以按照用户指定的需求在进程中开辟不同的AppDomain,将不同职能的插件载入不同的AppDomain中,亦可以将相类似职能的不同插件加入到同一个AppDomain。在今天的讲述中,我们将看到如何利用Mussel默认提供的通信及Proxy生成器插件来开发一个简单的C/S(客户端/服务器)应用。


 我们先来准备一下解决方案,如图:



这是一种非常典型的C/S应用架构,由Client(客户端)、Contract(调用规范)、Implement(调用实现)及Server(服务端)四部分组成。我们也可以推理出:调用实现是位置入服务端的,客户端凭调用规范对服务端进行调用。


我们先来看看调用规范(接口)
 

C/S程序约定的调用规范
  1. namespace Unit10   
  2. {   
  3.     public interface IShowMessage   
  4.     {   
  5.         void SayTime();   
  6.         int Calc(int a, int b);   
  7.     }   
  8. }  


正如您所见来的一样,这个规范非常的简单。没什么好说的,我们直接来看看实现:

 

针对调用规范的实现代码
  1. namespace Unit10   
  2. {   
  3.     [MusselType("ShowMessage")]   
  4.     public class ShowMessage:AddinItem,IShowMessage   
  5.     {   
  6.         public void SayTime()   
  7.         {   
  8.             Console.WriteLine("Current Time:{0}", DateTime.Now);   
  9.         }   
  10.   
  11.         public int Calc(int a, int b)   
  12.         {   
  13.             Console.WriteLine("Client Call {0} + {1}!", a, b);   
  14.             return a + b;   
  15.         }   
  16.     }   
  17. }  


实现代码同样非常简单——ShowMessage类从AddinItem继承,我们回顾一下前面章节所讲述的Mussel内容便可以知道:从AddinItem继承的类型,即是一个标准的Mussel插件项目。而MusselType特性则用于标识这是一个需要被Mussel加载管理器注意的类型,并给这个类型一个全局惟一的标识符。Mussel加载器在加载配置文件时,会依据这个类型标识符匹配相应的类型。


接口及实现代码均已完成,现在我们该如何将他们构建成一个C/S的分布式应用呢?先来看看我们已经完成的服务端代码:
 

服务端程序的代码
  1. namespace Unit10   
  2. {   
  3.     class Program   
  4.     {   
  5.         static void Main()   
  6.         {   
  7.             MusselService service = new MusselService();   
  8.             service.Start();   
  9.             Console.WriteLine("Server is started!");   
  10.             Console.ReadLine();   
  11.         }   
  12.     }   
  13. }  


可能看到这些代码后大家非常疑惑:在这里并没有任何的针对ShowMessage操作的代码,那么ShowMessage插件项目是如何载入的呢?首先MusselService是一个加载器类型,在前面的章节我们讲到过,Mussel的加载器在Start时是会读取执行文件所处文件夹下面的所有*.addin文件,这些addin文件便是Mussel的插件配置,里面详细描述了Mussel的插件加载信息。我们来看看服务端的这个addin文件。
 

服务端的插件配置文件
  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <Addin Name="Core" CreateNewDomain="true">  
  3.   <ReferenceAssemblies>  
  4.     <Reference AssemblyFile="Mussel.Communication.IceServicePortal.dll" IsMusselAssembly="true"/>  
  5.     <Reference AssemblyFile="Unit10.Implement.dll" IsMusselAssembly="true"/>  
  6.   </ReferenceAssemblies>  
  7.   <AddinNode Path="/Mussel/Core">  
  8.     <AddinItem ClassKey="BasicFormatter"/>  
  9.     <AddinItem ClassKey="ShowMessage"/>  
  10.     <AddinItem ClassKey="SimpleServiceSiteFactory"/>  
  11.     <AddinItem ClassKey="IceServicePortal"  
  12.                ServiceSiteFactoryKey="/Mussel/Core,SimpleServiceSiteFactory"  
  13.                AdapterId="MusselCommunicationAdapter"  
  14.                ObjectId="IceServicePortal"  
  15.                Endpoint="tcp -p 4300"  
  16.                />  
  17.   </AddinNode>  
  18. </Addin>  

我们来解释一下这个插件配置文件:

  1. <Addin Name="Core" CreateNewDomain="true">,我们在这里标识了这个插件的名称为Core,并且这个插件需要在一个新的AppDomain中创建。
  2. 在“ReferenceAssemblies”节点,用于指示相关一些程序集的引用,由于我们会将其它需要引用的程序集直接放入执行文件所处的目录中,所以我们可以不一一指出。只需要指出一些需要从中搜寻并加载Mussel插件项目的一些程序集(IsMusselAssembly="true")。
  3. <AddinNode Path="/Mussel/Core">,这个指明插件项目的节点路径,不明白的可以翻阅以前章节的讲解。
  4. <AddinItem ClassKey="BasicFormatter"/>,指示需要在当前节点加载MusselType标识字为"BasicFormatter"的插件项目。这是一个类型的序列化及反序列化器,由Mussel内核直接提供,用于通信。
  5. <AddinItem ClassKey="ShowMessage"/>,指示需要在当前节点加载MusselType标识字为"ShowMessage"的插件项目。这个便是我们实现出来的插件项目。
  6. <AddinItem ClassKey="SimpleServiceSiteFactory"/> 指示需要在当前节点加载MusselType标识字为"SimpleServiceSiteFactory"的插件。这个插件项目由 Mussel.Communication.IceServicePortal.dll 程序集提供,是一个基于ICE通信的一个简单型服务门户站点构建工厂,配合IceServicePortal插件项目使用。在这个程序集中,还提供了相对复杂应用的一些服务门户构建工厂。关于服务门户方面的具体知识,大家可以参阅我在前面章节讲述的AOP指令路由 在这里只需要知道这个插件项及下面的插件项目共同构筑网络的通信通道。
  7. <AddinItem ClassKey="IceServicePortal" 指示需要在当前节点加载MusselType标识字为"IceServicePortal"的插件,这是一个服务门户插件。这个插件包含几个特性:
    • ServiceSiteFactoryKey,门户站点构建工厂插件项目的全路径
    • AdapterId,ICE通信架构的适配器标识
    • ObjectId,ICE通信架构的对象标识(客户端与服务端一致)
    • Endpoint,通信的端口及协义描述,在这里我们可以看到我们在本地主机4300端口上监听TCP通信。

OK,服务端配置完毕,我们试运行一下,用 netstat -an 指令可以检测到,4300端口已打开,ICE通信服务正常运行。

 

接下来,我们来看看客户端,因为客户端程序同样会用到Mussel加载起一些插件项目,所以,我们先来看看插件配置:
 

客户端的插件项目配置
  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <Addin Name="Core" CreateNewDomain="true">  
  3.   <ReferenceAssemblies>  
  4.     <Reference AssemblyFile="Mussel.Communication.IceServicePortalProxy.dll" IsMusselAssembly="true"/>  
  5.     <Reference AssemblyFile="Mussel.DynamicProxy.RemoteAddinItemProxyFactory.dll" IsMusselAssembly="true"/>  
  6.   </ReferenceAssemblies>  
  7.   <AddinNode Path="/Mussel/Core">  
  8.     <AddinItem ClassKey="BasicFormatter"/>  
  9.     <AddinItem ClassKey="SimpleIceServicePortalProxy"  
  10.                ObjectId="IceServicePortal"  
  11.                Endpoint="tcp -h 127.0.0.1 -p 4300"/>  
  12.     <AddinItem ClassKey="RemoteAddinItemProxyFactory"  
  13.                Formatter="/Mussel/Core,BasicFormatter"  
  14.                DefaultServicePortalProxy="/Mussel/Core,SimpleIceServicePortalProxy"/>  
  15.   </AddinNode>  
  16. </Addin>  

同样,我们也先来解释一下客户端的配置文件,前面的加载信息我们略过,从插件讲起:

  1.  <AddinItem ClassKey="BasicFormatter"/>,同服务端一样,在当前节点加载序列化器插件项目。
  2. <AddinItem ClassKey="SimpleIceServicePortalProxy",这个是由"Mussel.Communication.IceServicePortalProxy.dll"程序集提供的一个插件项目,用于在客户端建立到服务端的ICE连接。所以可以认为他是一个服务端IceServicePortal的Proxy,他有ObjectId及Endpoint两个特性,这两个特性与服务端对应。
  3. <AddinItem ClassKey="RemoteAddinItemProxyFactory"这个是由"Mussel.DynamicProxy.RemoteAddinItemProxyFactory.dll"程序集提供的一个插件项目,用于根据调用的接口生成一个服务端服务型插件项目位于客户端的Proxy,例如可以为我们的IShowMessage生成一个客户端的Proxy。这个插件项目需要指明序列化器及默认的ServicePortalProxy。

 


了解了上面的配置,我们再来看看客户端的程序代码:
 

客户端的程序代码
  1. namespace Unit10   
  2. {   
  3.     class Program   
  4.     {   
  5.         static void Main(string[] args)   
  6.         {   
  7.             MusselService service = new MusselService();   
  8.             service.Start();   
  9.   
  10.             IRemoteAddinItemProxyFactory factory =   
  11.                 (IRemoteAddinItemProxyFactory)    
  12.                 service.Container["/Mussel/Core,RemoteAddinItemProxyFactory"];   
  13.   
  14.             IShowMessage showmessage =    
  15.                 factory.CreateProxy<IShowMessage>("/Mussel/Core,ShowMessage"null);   
  16.   
  17.             showmessage.SayTime();   
  18.   
  19.             Console.WriteLine("{0} + {1} = {2}", 4, 5, showmessage.Calc(4, 5));   
  20.   
  21.             Console.ReadLine();   
  22.         }   
  23.     }   
  24. }  

在上面的代码中,我们先从Mussel加载器的Container中取出一个远程插件项目的Proxy生成器,然后通过这个生成器产生了一个IShowMessage的客户端Proxy对象,于是我们就可以像调用本地对象一样调用服务端的方法了:

 

程序运行截图:

 

本文首发自:http://www.rogertong.cn/article.asp?id=25


点击下载程序源文件

Tag标签: Mussel

Feedback

#1楼    回复  引用  查看    

2008-07-14 23:16 by 没剑      
呵呵,不懂,不过支持一下太古,呵呵

#2楼 [楼主]   回复  引用  查看    

2008-07-14 23:28 by RogerTong      
@没剑
多谢支持

#3楼    回复  引用  查看    

2008-07-14 23:30 by 横刀天笑      
看起来很不错的说,和SharpDevelop在内核上也很相似

#4楼 [楼主]   回复  引用  查看    

2008-07-14 23:32 by RogerTong      
@横刀天笑
在设计时参考过SD,我尽量想做得比它简单

#5楼    回复  引用  查看    

2008-07-14 23:48 by Jeffrey Zhao      
除了“简单”还有什么特色呢?

#6楼 [楼主]   回复  引用  查看    

2008-07-14 23:53 by RogerTong      
@Jeffrey Zhao
插件可以分散在不同的AppDomain,同时亦可以合并加载到指定的AppDomain中。因此插件树可以跨越多个不同的AppDomain。
另外,完全支持Compact Framework,当然AppDomain方面就肯定不行了,呵可CF对Remoting没有支持

#7楼    回复  引用  查看    

2008-07-15 01:09 by Jeffrey Zhao      
@RogerTong
跨越多个appDomain的目的是什么呢?

#8楼 [楼主]   回复  引用  查看    

2008-07-15 08:16 by RogerTong      
@Jeffrey Zhao
主要是为了对一些服务性的插件项目提供更好的稳定性,
同时也实现插件项目的热插拔能力(可在程序运行时动态卸载并更新相应AppDomain中的插件项目)

#9楼    回复  引用  查看    

2008-07-15 08:29 by 杨义金      
你用的ICE,和WCF相比,哪个更有优势?作者能否做个比较?
另外,远程调用时,对象的序列化有什么要求?

#10楼 [楼主]   回复  引用  查看    

2008-07-15 08:36 by RogerTong      
@杨义金
因为对WCF的了解不是太深入,所以不敢枉做比较。但是可以肯定的是WCF对多种
通信协议客户端的兼容能力肯定是强于ICE的:)
ICE有自己的序列化标准,这个标准其实是源于Corba,即预先定义出一套通信及调
用的规范,并用ICE的方式描述,然后再用ICE的代码生成器生成相应语言的代码。
但是我兼这样做太麻烦,所以在ICE中直接传送字节流,然后自己处理序列化及反
序化的过程(就是上面的那个BasicFormatter)。

#11楼    回复  引用  查看    

2008-07-15 09:26 by 雅阁布      
向楼主学习!!!!
up!!!

#12楼    回复  引用    

2008-07-15 11:45 by 东成 [未注册用户]
个人认为Ice做异构系统/环境的架构还是十分好的,而只是在.NET环境下进行通信处理的话,WCF应该是一个比较好的选择。

#13楼 [楼主]   回复  引用  查看    

2008-07-15 14:26 by RogerTong      
@东成
主要是为了避免在客户端及服务器上安装.NET3.0以上的框架(因为不少用户有)
2.0的Framework,不过可以考虑将WCF通信做成一个Mussel的插件项目。

#14楼    回复  引用    

2008-07-15 19:06 by Knic [未注册用户]
看了这个系列 觉得这种方式很不错啊

但不知道能否适用于web开发呢?

#15楼 [楼主]   回复  引用  查看    

2008-07-15 19:15 by RogerTong      
@Knic
我们目前也在尝试对Web的支持,如果你有好的思路,请提供给我

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      


相关链接: