code4fun:host wcf service just in time
當ServiceContract非常多的情況下,比如要self-host hundred of service的時候,or更多的時候,每次hosting都是建立一個tcp listen.這樣,host的init工作會占用非常多的時間和資源。對于一些對start有較快需求的case,這明顯會影響用戶體驗。本文意在尋求一種技術手段解決這個問題,讓service只有在運行時才host。我們姑且稱之為host just in time吧
先說一下思路。
client如果能發送消息給service,并且消息可達,先決條件就是消息到達service之前,service已經被host,注意,是說消息到達之前,而并不是在客戶端發送消息之后,這就給我們提供了一個可以做手腳的時期,即消息從客戶端發送出去到消息在被服務端接收之前這段時間,如果我們能Inspect它,并根據其self description,便能在消息發送到真正的服務之前,host那個service,而做這樣一個事情,rounter明顯是最在行的了。下面是實現host just in time的原理圖。
按照上面的思路,做了一個簡單的demo,過程如下
首先創建Contracts項目Jillzhang.Wcf.HostJIT.Contracts,包含兩個ServiceContract:ICalculator和IActivity,代碼分別為:
ICalculator:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace Jillzhang.Wcf.HostJIT.Contracts { [ServiceContract] public interface ICalculator { [OperationContract] int Add(int a, int b); } }
IActivity:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace Jillzhang.Wcf.HostJIT.Contracts { [ServiceContract] public interface IActivity { [OperationContract] void Excute(); } }
demo只模擬對這兩個ServiceContract的host just in time
建立好契約項目之后,新建服務項目:Jillzhang.Wcf.HostJIT.Services,添加兩個類:Caculator和Activity,分別實現ICalculator和IActivity,其代碼如下:
Caculator:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Jillzhang.Wcf.HostJIT.Contracts; namespace Jillzhang.Wcf.HostJIT.Services { public class Calculator : ICalculator { public int Add(int a, int b) { return a + b; } } }
Activity:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Jillzhang.Wcf.HostJIT.Contracts; namespace Jillzhang.Wcf.HostJIT.Services { public class Activity:IActivity { public void Excute() { Console.WriteLine("activity is running!"); } } }
接著,我們需要簡歷本實例中最重要的項目:Jillzhang.Wcf.HostJIT.Dispatcher,這個項目的release自身就是一個對wcf service的self-host的hosting app.為了實現路由,截獲以及調度的功能,實現了路由接口IRounter和類Dispatcher,下面是他們的code:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; using System.Collections; using Jillzhang.Wcf.HostJIT.Services; namespace Jillzhang.Wcf.HostJIT.Dispatcher { [ServiceContract] public interface IRounter { [OperationContract(Action = "*", ReplyAction = "*")] Message ProcessMessage(Message msg); } [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)] public class Dispatcher : IRounter { public static readonly Hashtable hostTable = new Hashtable(); public Message ProcessMessage(Message msg) { string to = msg.Headers.To.ToString(); string serviceName = ""; //todo:if no host,host first if (to.IndexOf("8031") > -1) { if (!hostTable.ContainsKey("Calculator")) { ServiceHost host = new ServiceHost(typeof(Calculator)); host.Opened += new EventHandler(delegate(object sender, EventArgs arg) { Console.WriteLine("Calculator service is opened!"); }); host.Open(); hostTable.Add("Calculator", host); } serviceName = "Calculator"; } else { if (!hostTable.ContainsKey("Activity")) { ServiceHost host = new ServiceHost(typeof(Activity)); host.Opened += new EventHandler(delegate(object sender, EventArgs arg) { Console.WriteLine("activity service is opened!"); }); host.Open(); hostTable.Add("Activity", host); } serviceName = "Activity"; } //todo:rounter the message using (ChannelFactory<IRounter> channelFactory = new ChannelFactory<IRounter>(serviceName)) { var chanenl = channelFactory.CreateChannel(); using (chanenl as IDisposable) { return chanenl.ProcessMessage(msg); } } } } }
通過代碼可以看出,首先Dispatcher是一個有路由功能的服務,它會根據message中Header中的To判斷要調用的Service是哪一個,然后判斷是否已經承載了那個最終目的的Service,如果沒有,host frist and add it to hostTable ,host之后,就可以將消息調度給真正的服務了。
app.config的代碼如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <services> <service name="Jillzhang.Wcf.HostJIT.Services.Calculator"> <host> <baseAddresses> <add baseAddress="net.tcp://127.0.0.1:8031"/> </baseAddresses> </host> <endpoint address="Calculator" binding="netTcpBinding" contract="Jillzhang.Wcf.HostJIT.Contracts.ICalculator"></endpoint> </service> <service name="Jillzhang.Wcf.HostJIT.Services.Activity"> <host> <baseAddresses> <add baseAddress="net.tcp://127.0.0.1:8032"/> </baseAddresses> </host> <endpoint address="Activity" binding="netTcpBinding" contract="Jillzhang.Wcf.HostJIT.Contracts.IActivity"></endpoint> </service> <service name="Jillzhang.Wcf.HostJIT.Dispatcher.Dispatcher"> <host> <baseAddresses> <add baseAddress="net.tcp://127.0.0.1:8030"/> </baseAddresses> </host> <endpoint address="" binding="netTcpBinding" contract="Jillzhang.Wcf.HostJIT.Dispatcher.IRounter"></endpoint> </service> </services> <client> <endpoint address="net.tcp://127.0.0.1:8031/Calculator" binding="netTcpBinding" contract="Jillzhang.Wcf.HostJIT.Dispatcher.IRounter" name="Calculator"></endpoint> <endpoint address="net.tcp://127.0.0.1:8032/Activity" binding="netTcpBinding" contract="Jillzhang.Wcf.HostJIT.Dispatcher.IRounter" name="Activity"></endpoint> </client> </system.serviceModel> </configuration>
而在這個項目中,我們開始只需要host IRounter契約。host代碼為:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace Jillzhang.Wcf.HostJIT.Dispatcher { class Program { static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(Dispatcher))) { host.Opened += new EventHandler(delegate(object sender, EventArgs arg) { Console.WriteLine("dispatcher service is opened!"); }); host.Open(); Console.Read(); } } } }
建立客戶端程序Jillzhang.Wcf.HostJIT.Client,并且將app.config更改為:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <client> <endpoint behaviorConfiguration="via" address="net.tcp://127.0.0.1:8031/Calculator" binding="netTcpBinding" contract="Jillzhang.Wcf.HostJIT.Contracts.ICalculator" name="Calculator"></endpoint> <endpoint behaviorConfiguration="via" address="net.tcp://127.0.0.1:8032/Activity" binding="netTcpBinding" contract="Jillzhang.Wcf.HostJIT.Contracts.IActivity" name="Activity"></endpoint> </client> <behaviors> <endpointBehaviors> <behavior name="via"> <clientVia viaUri="net.tcp://127.0.0.1:8030/"/> </behavior> </endpointBehaviors> </behaviors> </system.serviceModel> </configuration>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using Jillzhang.Wcf.HostJIT.Contracts; namespace Jillzhang.Wcf.HostJIT.Client { class Program { static void Main(string[] args) { Console.WriteLine("press any key when dispatcher service is ok!"); Console.Read(); CallCalculator(); CallActivity(); CallCalculator(); CallActivity(); Console.ReadKey(true); } static void CallCalculator() { using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("Calculator")) { var channel = channelFactory.CreateChannel(); using (channel as IDisposable) { Console.WriteLine(channel.Add(1, 2).ToString()); } } } static void CallActivity() { using (ChannelFactory<IActivity> channelFactory2 = new ChannelFactory<IActivity>("Activity")) { var channel = channelFactory2.CreateChannel(); using (channel as IDisposable) { channel.Excute(); } } } } } 好了,在解決方案中設置多啟動項目,啟動Jillzhang.Wcf.HostJIT.Client和Jillzhang.Wcf.HostJIT.Dispatcher,f5,然后按照提示進行輸入
會得到下面的效果:
客戶端:
服務端:
最終驗證效果和原來想象中一致。OK!
demo項目:https://files.cnblogs.com/jillzhang/Jillzhang.Wcf.HostJIT.rar
出處:http://jillzhang.cnblogs.com/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

浙公網安備 33010602011771號