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


目錄

  • 什麼是插件式編程
    • OCP:開放封閉原則
    • 插件式架構
    • C#實現插件式開發的理論基礎
  • ServiceHost實現批量寄宿
  • 總結
  • 參考

Dutch Door

     兩截門(Dutch Door)——(名詞)一個被水平分割爲兩部分的門,這樣每一部分均可以獨立保持開放或者封閉。(《美國傳統英語字典》第4版,2000年)html

     假設您設計的程序已經部署到用戶的計算機上,而且可以正常運行了。可是有一天,用戶打來了電話——他們要求增長新的功能。肯定了用戶的需求後,你居然發現原有的軟件架構已經沒法勝任新增任務的需求——你須要從新設計這個應用了!但問題是,就算你又用了一個開發週期完成了用戶須要的應用,卻不能保證用戶的需求不會再次變動。也就是說,需求蔓延的可能性依然存在。所以,這種狀況下插件構架更能顯示出它的優越性。java

     一樣的原理,能夠應用到咱們的widows服務開發中。編程

什麼是插件式編程

OCP:開放封閉原則api

  軟件實體(類、模塊、函數等)應該是能夠擴展的,可是不可修改。微信

  • 對於擴展是開放的(open for extension)。

    這意味着模塊的行爲是能夠擴展的。當應用的需求改變時,咱們能夠對模塊進行擴展,使其具備知足那些改變的新行爲。還句話說,咱們能夠改變模塊的功能。架構

  • 對於修改時封閉的(closed for modification)

    對於模塊行爲進行擴展時,沒必要改動模塊的源代碼或者二進制代碼。模塊的二進制可執行版本,不管是可連接的庫、DLL或者.EXE文件,都無需改動。框架

  怎樣可能在不改動模塊源代碼的狀況下去更改它的行爲呢?若是不更改一個模塊,又怎麼可以去改變它的功能呢?tcp

  答案是抽象。在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給你們。

參考

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

相關文章
相關標籤/搜索