服務(wù)主機(jī)的路由器與負(fù)載均衡和實(shí)現(xiàn)思路
路由器介紹及演示
WCF中間件的路由功能是在客戶端與服務(wù)端之間加入中介服務(wù),用來轉(zhuǎn)發(fā)它們之間的消息。實(shí)現(xiàn)消息的轉(zhuǎn)發(fā)可以修改WCF服務(wù)消息頭的內(nèi)容,重新指定服務(wù)地址即可,那給消息頭指定的服務(wù)地址從哪來,需要給路由器配置服務(wù)端地址目錄,路由器與服務(wù)端肯定不是一對一的,路由器可以指定多個服務(wù)端,而路由器把客戶端連接指定給哪個服務(wù)端這里就有一個算法,算法的優(yōu)劣就決定了中間件負(fù)載均衡的能力。
下面演示了中間件的路由功能,把Out目錄中的程序復(fù)制6份,分別改名如下,3個客戶端,1個路有中間件,2個服務(wù)中間件,還要修改每個程序的相關(guān)配置;這樣先啟動路由中間件Router和服務(wù)中間件WCFservser1、WCFServer2,然后分別啟動2個客戶端程序,路由中間件和服務(wù)中間件就會顯示客戶端的連接信息。3個客戶端會有2個分配到一個服務(wù)中間件,一個客戶端分配到另外一個服務(wù)中間件,不會說3個客戶端都分配到1個服務(wù)中間件,這是由路由中間件的負(fù)載均衡算法決定的;


路由功能的實(shí)現(xiàn)
框架增加了一個路由服務(wù)對象Router,用它來攔截客戶端發(fā)送的消息,攔截方法ProcessMessage(Message requestMessage);
首先根據(jù)路由目錄結(jié)合負(fù)載均衡的算法取得服務(wù)地址endpointAddress,然后創(chuàng)建WCF通道并綁定新的服務(wù)地址,調(diào)用服務(wù)端的方法;
- /// <summary>
- /// 截獲從Client端發(fā)送的消息轉(zhuǎn)發(fā)到目標(biāo)終結(jié)點(diǎn)并獲得返回值給Client端
- /// </summary>
- /// <param name="requestMessage"></param>
- /// <returns></returns>
- public Message ProcessMessage(Message requestMessage)
- {
- //Binding binding = null;
- EndpointAddress endpointAddress = null;
- GetServiceEndpoint(requestMessage, out endpointAddress);
- IDuplexRouterCallback callback = OperationContext.Current.GetCallbackChannel<IDuplexRouterCallback>();
- NetTcpBinding tbinding = new NetTcpBinding("netTcpExpenseService_ForSupplier");
- using (DuplexChannelFactory<IRouterService> factory = new DuplexChannelFactory<IRouterService>(new InstanceContext(null, new DuplexRouterCallback(callback)), tbinding, endpointAddress))
- {
- factory.Endpoint.Behaviors.Add(new MustUnderstandBehavior(false));
- IRouterService proxy = factory.CreateChannel();
- using (proxy as IDisposable)
- {
- // 請求消息記錄
- IClientChannel clientChannel = proxy as IClientChannel;
- //Console.WriteLine(String.Format("Request received at {0}, to {1}\r\n\tAction: {2}", DateTime.Now, clientChannel.RemoteAddress.Uri.AbsoluteUri, requestMessage.Headers.Action));
- if (Convert.ToInt32(HostSettingConfig.GetValue("debug")) == 1)
- hostwcfMsg(DateTime.Now, String.Format("路由請求消息發(fā)送: {0}", clientChannel.RemoteAddress.Uri.AbsoluteUri));
- // 調(diào)用綁定的終結(jié)點(diǎn)的服務(wù)方法
- Message responseMessage = proxy.ProcessMessage(requestMessage);
- // 應(yīng)答消息記錄
- //Console.WriteLine(String.Format("Reply received at {0}\r\n\tAction: {1}", DateTime.Now, responseMessage.Headers.Action));
- //Console.WriteLine();
- //hostwcfMsg(DateTime.Now, String.Format("應(yīng)答消息: {0}", responseMessage.Headers.Action));
- return responseMessage;
- }
- }
- }
#p#負(fù)載均衡的實(shí)現(xiàn)
負(fù)載均衡實(shí)現(xiàn)代碼在Router對象中的GetServiceEndpoint方法中,定義了RegistrationList對象用來存儲客戶端列表,在消息頭中增加了兩個標(biāo)識routerID和CMD,routerID用來識別客戶端,值是客戶端創(chuàng)建發(fā)送到路由中間件,每個客戶端只有一個routerID;CMD用來客戶端發(fā)送給路由中間件的命令標(biāo)識,這里只用到了一個就是”Quit”就是卸載路由中間件中的RegistrationList客戶端列表;
解決了識別客戶端的問題,那平均算法每個客戶端分配到哪個服務(wù)中間件就很簡單了,RoundRobinCount就記錄每個服務(wù)中間件對應(yīng)的客戶端個數(shù),哪個服務(wù)中間件數(shù)量少新的客戶端就分配給它;
- private void GetServiceEndpoint(Message requestMessage,out EndpointAddress endpointAddress)
- {
- string ns = "http://www.3yxx.com/";
- string routerID = GetHeaderValue("routerID", ns);
- string cmd = GetHeaderValue("CMD", ns);
- string contractNamespace = requestMessage.Headers.Action.Substring(0, requestMessage.Headers.Action.LastIndexOf("/"));
- RegistrationInfo regInfo = null;
- if (Router.RoundRobinCount.ContainsKey(routerID))
- {
- int key = Router.RoundRobinCount[routerID];
- regInfo = Router.RegistrationList[key];
- if (cmd == "Quit")
- {
- regInfo.ClientNum -= 1;
- }
- }
- else
- {
- //根據(jù)指定的協(xié)議名稱空間從注冊表容器中得到注冊項列表
- var results = from item in Router.RegistrationList
- where item.Value.ContractNamespace.Contains(contractNamespace)
- orderby item.Value.ClientNum ascending
- select item;
- if (results.Count<KeyValuePair<int, RegistrationInfo>>() > 0)
- {
- var val = results.First<KeyValuePair<int, RegistrationInfo>>();
- Router.RoundRobinCount.Add(routerID, val.Key);
- val.Value.ClientNum += 1;
- regInfo = val.Value;
- }
- }
- Uri addressUri = new Uri(regInfo.Address);
- //binding = CustomBindConfig.GetRouterBinding(addressUri.Scheme);
- endpointAddress = new EndpointAddress(regInfo.Address);
- //重設(shè)Message的目標(biāo)終結(jié)點(diǎn)
- requestMessage.Headers.To = new Uri(regInfo.Address);
- hostwcfRouter(RegistrationList.Values.ToList());
- }
WCF客戶端配置和中間件配置還有路由地址配置
如果部署的時候不使用中間件的路由功能,那客戶端配置服務(wù)地址直接指定服務(wù)端WCF地址就行了,而如果啟用路由功能,那客戶端就配置路由中間件的WCF地址,路由中間件再配置路由目錄,對應(yīng)服務(wù)端;
客戶端WCF配置和服務(wù)端WCF配置還有一個地方值得注意,就是netTcpBinding節(jié)點(diǎn)的配置;***
1)客戶端App.Config配置


2)路由中間件App.Config配置和路由目錄RouterBill.xml配置


3)服務(wù)中間件App.Config配置

5.總結(jié)
本章我們詳細(xì)講解了EFW框架中的WCF中間件的路由功能和負(fù)載均衡的實(shí)現(xiàn),代碼很簡單,但深入理解卻沒那么容易,我也只是略懂點(diǎn)皮毛,參考了網(wǎng)上資料把功能實(shí)現(xiàn)而已,而想要做成專業(yè)級別的中間件是有一個過程的,所以不只是我,也需要有興趣的人一起完善它;
路由實(shí)例程序下載 :http://pan.baidu.com/s/1eQ8FscE
注意:實(shí)例中的配置文件中的IP地址192.168.1.3修改為你本機(jī)的IP地址;