zoukankan      html  css  js  c++  java
  • 插件式架构实现批量服务寄宿


    目录

    • 什么是插件式编程
      • OCP:开放封闭原则
      • 插件式架构
      • C#实现插件式开发的理论基础
    • ServiceHost实现批量寄宿
    • 总结
    • 参考

    Dutch Door

         两截门(Dutch Door)——(名词)一个被水平分割为两部分的门,这样每一部分都可以独立保持开放或者封闭。(《美国传统英语字典》第4版,2000年)

         假设您设计的程序已经部署到用户的计算机上,并且能够正常运行了。但是有一天,用户打来了电话——他们要求增加新的功能。确定了用户的需求后,你竟然发现原有的软件架构已经无法胜任新增任务的需求——你需要重新设计这个应用了!但问题是,就算你又用了一个开发周期完成了用户需要的应用,却不能保证用户的需求不会再次变更。也就是说,需求蔓延的可能性依然存在。因此,这种情况下插件构架更能显示出它的优越性。

         同样的原理,可以应用到我们的widows服务开发中。

    什么是插件式编程

    OCP:开放封闭原则

      软件实体(类、模块、函数等)应该是可以扩展的,但是不可修改。

    • 对于扩展是开放的(open for extension)。

      这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。还句话说,我们可以改变模块的功能。

    • 对于修改时封闭的(closed for modification)

      对于模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者.EXE文件,都无需改动。

      怎样可能在不改动模块源代码的情况下去更改它的行为呢?如果不更改一个模块,又怎么能够去改变它的功能呢?

      答案是抽象。在C#或者其他任何的OOPL(面向对象程序设计语言,如java)中,可以创建出固定却能够描述一组任意个可能行为抽象体。这个抽象体就是抽象基类。而这一组任意个可能得行为则表现为可能得派生类。

      模块可能对抽象体进行操作。由于模块依赖于一个固定的抽象体,所以它对于更改可以是封闭的。同时,通过从这个抽象体派生,可以扩展此模块的行为。

    OCP

    插件式架构

         插件式架构是遵循OCP原则的。插件式架构,一种开放性的、高扩展性的架构体系。基于插件的设计好处很多,把扩展功能从框架中剥离出来,降低了框架的复杂度,让框架更容易实现。扩展功能与框架以一种很松的方式耦合,两者在保持接口不变的情况下,可以独立变化和发布。基于插件设计并不神秘,相反它比起一团泥的设计更简单,更容易理解。

         而笔者实际项目中,通常是按照业务归属将不同业务模块设计成不同插件。

    C#实现插件式开发的理论基础
         在dotNET framework中,给开发人员提供了反射机制,通过反射可以动态加载类库。

         Assembly类可以获得正在运行的装配件信息,也可以动态的加载装配件,以及在装配件中查找类型信息,并创建该类型的实例。
    Type类可以获得对象的类型信息,此信息包含对象的所有要素:方法、构造器、属性等等,通过Type类可以得到这些要素的信息,并且调用之。
    MethodInfo包含方法的信息,通过这个类可以得到方法的名称、参数、返回值等,并且可以调用之。
    诸如此类,还有FieldInfo、EventInfo等等,这些类都包含在System.Reflection命名空间下。

    插件方式实现ServiceHost批量寄宿WCF服务

    基础架构

    实现基本目标:

    • 以插件的方式开发wcf服务端,业务模块以独立dll的方式注册到宿主里
    • 业务模块的添加删除是动态的,服务端主程序无需重新编译
    • 无须为每个业务模快servicetype单独做配置,注册一个业务模块只需在host里添加一个endpoint和操作契约

         (1)创建一个WinowsService服务,创建三个wcf服务,然后分别实现各自wcf的业务逻辑。以上步骤比较简单。

         (2)然后就是编译wcf.dll,并拷贝到host服务的指定目录,比如我的在pluginsxx_wcf.dll。

         (3)在Host宿主的配置文件,添加endpoint和操作契约

    <system.serviceModel>
        <bindings>
          <netTcpBinding>
            <binding name="tcpBinding">
              <security mode="None">
              </security>
            </binding>
          </netTcpBinding>
        </bindings>
        <services>
          <!--添加服务-->
          <!--B服务-->
          <service name="Plugins.BService.WcfService.BusinessBService" behaviorConfiguration="Plugins.BService.WcfService">
            <!--name 必须与代码中的host实例初始化的服务一样 behaviorConfiguration 行为配置 -->
            <host>
              <baseAddresses>
                <!--添加调用服务地址-->
                <add baseAddress="net.tcp://172.16.0.194:3721/Plugins.BService.WcfService"/>
              </baseAddresses>
    
            </host>
            <!--添加契约接口 -->
            <endpoint address="net.tcp://172.16.0.194:3721/Plugins.BService.WcfService" binding="netTcpBinding" contract="Plugins.BService.WcfService.IBusinessBService" bindingConfiguration="tcpBinding" name="Plugins.BService.WcfService.BusinessBasicInfoService"></endpoint>
            <endpoint address="mex" binding="mexTcpBinding" name="MEX" contract="IMetadataExchange"/>
    
          </service>
    
          <!--A服务-->
          <service name="Plugins.AService.WcfService.BusinessAService" behaviorConfiguration="Plugins.AService.WcfService">
            <!--name 必须与代码中的host实例初始化的服务一样 behaviorConfiguration 行为配置 -->
            <host>
              <baseAddresses>
                <!--添加调用服务地址-->
                <add baseAddress="net.tcp://172.16.0.194:3722/Plugins.AService.WcfService"/>
              </baseAddresses>
    
            </host>
            <!--添加契约接口 -->
            <endpoint address="net.tcp://172.16.0.194:3722/Plugins.AService.WcfService" binding="netTcpBinding" contract="Plugins.AService.WcfService.IBusinessAService" bindingConfiguration="tcpBinding" name="Plugins.AService.WcfService.BusinessBasicInfoService"></endpoint>
            <endpoint address="mex" binding="mexTcpBinding" name="MEX" contract="IMetadataExchange"/>
    
          </service>
    
          <!--Resource.Robot服务-->
          <service name="ResourceRobot.White.WcfService.WhiteListService" behaviorConfiguration="ResourceRobot.White.WcfService">
            <!--name 必须与代码中的host实例初始化的服务一样 behaviorConfiguration 行为配置 -->
            <host>
              <baseAddresses>
                <!--添加调用服务地址-->
                <add baseAddress="net.tcp://172.16.0.194:3723/ResourceRobot.White.WcfService"/>
              </baseAddresses>
    
            </host>
            <!--添加契约接口 -->
            <endpoint address="net.tcp://172.16.0.194:3723/ResourceRobot.White.WcfService" binding="netTcpBinding" contract="ResourceRobot.Channels.IWhiteListService" bindingConfiguration="tcpBinding" name="ResourceRobot.White.WcfService.WhiteListService"></endpoint>
            <endpoint address="mex" binding="mexTcpBinding" name="MEX" contract="IMetadataExchange"/>
    
          </service>
    
        </services>
        <!--定义WcfServiceBehavior的行为-->
        <behaviors>
          <serviceBehaviors>
            <behavior name="Plugins.BService.WcfService">
              <serviceMetadata httpGetEnabled="false"/>
              <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
            <behavior name="Plugins.AService.WcfService">
              <serviceMetadata httpGetEnabled="false"/>
              <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
            <behavior name="ResourceRobot.White.WcfService">
              <serviceMetadata httpGetEnabled="false"/>
              <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
    

         (4)接下来就是利用.NET反射机制,去遍历plugins目录下的wcf.dll,分别注册到ServiceHost中。

         在service.cs的OnStart方法中,增加以下代码:

    //1.寻找wcf.dll
                List<string> pluginpaths = PluginHelper.Find();
    
                //2.遍历并解析
                foreach (string filename in pluginpaths)
                {
                    try
                    {
                        //获取文件名
                        string asmfile = filename;
                        string asmname = Path.GetFileNameWithoutExtension(asmfile);
                        if (asmname != string.Empty)
                        {
                            //利用反射,构造DLL文件的实例
                            Assembly asm = Assembly.LoadFile(asmfile);
    
                            //利用反射,从程序集(DLL)中,提取类,并把此类实例化
                            Type[] t = asm.GetExportedTypes();
                            foreach (Type type in t)
                            {
                                //3.注册
                                ServiceHost host = new ServiceHost(type);
                                if (host != null)
                                {
                                    host.Open();
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.Write(ex.Message);
                    }
    

         这样就实现了一个serivcehost承载多个wcf服务的要求。

    3 总结

         OCP:开放-封闭原则是敏捷开发中非常重要的原则。

         使用插件式架构能够是整个软件更加灵活,扩展性更强,同时也便于部署和维护。

         最后分享一下趣图:
    OCP

         如有需要demo的可以留言或私信笔者(576810529@qq.com),并留下您的邮箱~我会尽快send给大家。

    参考

         转载请注明出处,本文同步更新至

  • 相关阅读:
    C++对象模型
    C/C++内存结构
    第一篇
    Goodbye Steve(19552011)
    DirectX学习笔记_关于Sprite.Draw2D的说明
    Goodbye World!
    js把一个数组的数据平均到几个数组里面
    Django model字段类型清单
    Golang中间件——goredis操作Redis
    Python开发一个短网址生成器
  • 原文地址:https://www.cnblogs.com/lucky_hu/p/10031932.html
Copyright © 2011-2022 走看看