1. 程式人生 > >外掛式架構實現批量服務寄宿

外掛式架構實現批量服務寄宿

目錄

  • 什麼是外掛式程式設計
    • 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的可以留言或私信筆者([email protected]),並留下您的郵箱~我會盡快send給大家。

參考

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