插件式架構實現批量服務寄宿



目錄

  • 什么是插件式編程
    • 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服務的指定目錄,比如我的在\plugins\xx_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給大家。

參考

     轉載請注明出處,本文同步更新至


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM