外掛式架構實現批量服務寄宿
目錄
- 什麼是外掛式程式設計
- OCP:開放封閉原則
- 外掛式架構
- C#實現外掛式開發的理論基礎
- ServiceHost實現批量寄宿
- 總結
- 參考
兩截門(Dutch Door)——(名詞)一個被水平分割為兩部分的門,這樣每一部分都可以獨立保持開放或者封閉。(《美國傳統英語字典》第4版,2000年)
假設您設計的程式已經部署到使用者的計算機上,並且能夠正常運行了。但是有一天,使用者打來了電話——他們要求增加新的功能。確定了使用者的需求後,你竟然發現原有的軟體架構已經無法勝任新增任務的需求——你需要重新設計這個應用了!但問題是,就算你又用了一個開發週期完成了使用者需要的應用,卻不能保證使用者的需求不會再次變更。也就是說,需求蔓延的可能性依然存在。因此,這種情況下外掛構架
更能顯示出它的優越性。
同樣的原理,可以應用到我們的widows服務開發中。
什麼是外掛式程式設計
OCP:開放封閉原則
軟體實體(類、模組、函式等)應該是可以擴充套件的,但是不可修改。
- 對於擴充套件是開放的(open for extension)。
這意味著模組的行為是可以擴充套件的。當應用的需求改變時,我們可以對模組進行擴充套件,使其具有滿足那些改變的新行為。還句話說,我們可以改變模組的功能。
- 對於修改時封閉的(closed for modification)
對於模組行為進行擴充套件時,不必改動模組的原始碼或者二進位制程式碼。模組的二進位制可執行版本,無論是可連結的庫、DLL或者.EXE檔案,都無需改動。
怎樣可能在不改動模組原始碼的情況下去更改它的行為呢?如果不更改一個模組,又怎麼能夠去改變它的功能呢?
答案是抽象。在C#或者其他任何的OOPL(面向物件程式設計語言,如java)中,可以創建出固定卻能夠描述一組任意個可能行為抽象體。這個抽象體就是抽象基類。而這一組任意個可能得行為則表現為可能得派生類。
模組可能對抽象體進行操作。由於模組依賴於一個固定的抽象體,所以它對於更改可以是封閉的。同時,通過從這個抽象體派生,可以擴充套件此模組的行為。
外掛式架構
外掛式架構是遵循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:開放-封閉原則是敏捷開發中非常重要的原則。
使用外掛式架構能夠是整個軟體更加靈活,擴充套件性更強,同時也便於部署和維護。
最後分享一下趣圖:
如有需要demo的可以留言或私信筆者([email protected]),並留下您的郵箱~我會盡快send給大家。
參考
轉載請註明出處,本文同步更新至