- A+
本文技术点思路梳理:
- 创建Web服务组的通用基类“接口”。并在该“接口”上附加ServiceContract、OperationContract、WebInvoke三属性。
- 创建WCF服务并实现上述“接口”。并在Web.config中添加<services>与<endpointBehaviors>两项配置,让WCF服务兼具Web服务的特性。
- 把上述Web服务分别部署在网络上多个不同的地方,形成“Web服务组”。
- 通过DeveloperSharp.xml来配置“Web服务组”的负载均衡策略。
- 创建客户端,在其App.config/Web.config中添加链接DeveloperSharp.xml的appSettings节点。再在代码中创建基于DeveloperSharp.dll的“Web服务负载均衡调用”工具,并通过该工具调用远程的Web服务。
【要点综述1】:为了演示Web服务的负载均衡,本文会创建3个形式一致的web服务(或者说,创建1个web服务但分别拷贝部署在3个不同的地址),然后在客户端创建一个代理以“负载均衡”的算法形式远程调用这3个web服务。
【要点综述2】:为了实现“统一/透明”的调用形式连接“web服务端”与“客户端”两者,一个居中的“接口”类是必须创建的,且所有远程web服务需要继承此接口。
【要点综述3】:Web Api是一个经典的http服务提供形式,WCF也是一个经典的两端通信模式,本文还将会极巧妙的整合这两者,让一个服务一次编写同时具备Web Api与WCF两者的特性。
我们首先在Visual Studio中新建第一个名字叫“YZZ.Interface”的解决方案+类库工程,并在此工程中创建“IBase接口”与“Person实体类”,代码分别如下:
IBase接口:
using System.ServiceModel;//需要添加对System.ServiceModel.dll程序集的引用 using System.ServiceModel.Web;//需要添加对System.ServiceModel.Web.dll程序集的引用 namespace YZZ.Interface { [ServiceContract] public interface IBase { [OperationContract] [WebInvoke(Method = "POST", UriTemplate = "/AccessRight", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)] Person AccessRight(int age); } }
Person实体类:
namespace YZZ.Interface { public class Person { public string name; public int age; public int right; } }
如上代码所示,IBase接口中定义了一个通过年龄来做权限判断的AccessRight方法,并在该方法上附加了OperationContract与WebInvoke两个属性。OperationContract这个属性是用来给方法赋予WCF的特性,而WebInvoke这个属性则是给方法赋予了Web Api的特性。
编译该解决方案,获得YZZ.Interface.dll。
然后,我们再在Visual Studio中新建第二个名字叫“YZZ.Service”的解决方案+WCF服务应用程序工程,并在此工程中创建名为“MyBaseService.svc”的WCF服务文件,该文件的后台代码如下:
using YZZ.Interface;//需要添加前述IBase接口定义的YZZ.Interface.dll的引用 namespace YZZ.Service { public class MyBaseService : IBase { public Person AccessRight(int age) { if (age > 27) return new Person { name = "管理员", age = age, right = 1 }; else return new Person { name = "学生/员工", age = age, right = 0 }; } } }
如上代码所示,MyBaseService类实现了IBase接口中定义的AccessRight方法。
并且,此时我们还需要在此工程的Web.config中做如下两项配置,才能使得WCF与Web Api的特性在AccessRight方法身上完全见效。
配置一:在<system.serviceModel>节点下添加如下配置
<!--用来对外发布WCF与WebAPI--> <services> <service name="YZZ.Service.MyBaseService"> <!-- 对应Web API的End Point --> <endpoint address="webapi" binding="webHttpBinding" contract="YZZ.Interface.IBase" behaviorConfiguration="WebApiEndPointBehavior" /> <!-- 对应WCF的End Point --> <endpoint address="" binding="basicHttpBinding" contract="YZZ.Interface.IBase" /> </service> </services>
配置二:在<behaviors>节点下添加如下配置
<!-- WCF Service转换为Web API后,使用的behavior --> <endpointBehaviors> <behavior name="WebApiEndPointBehavior"> <webHttp /> </behavior> </endpointBehaviors>
如此一来,AccessRight方法就兼具了Web Api与WCF的双重特性。其Web Api(Web服务)的调用路径如下:
http://部署的服务器路径(ip+port)/MyBaseService.svc/webapi/AccessRight
为了演示Web服务的负载均衡的效果,现在我们把AccessRight方法的内容稍做修改成3个版本,分别编译YZZ.Service工程后部署在3个不同的地方(如何部署请参考其它资料,这是基础知识)。如下:
部署地址1:http://139.244.52.136:120/MyBaseService.svc
对应AccessRight代码1:
public Person AccessRight(int age) { if (age > 27) return new Person { name = "管理员", age = age, right = 1 }; else return new Person { name = "学生/员工", age = age, right = 0 }; }
部署地址2:http://139.244.52.136:220/MyBaseService.svc
对应AccessRight代码2:
public Person AccessRight(int age) { if (age > 27) return new Person { name = "管理员1", age = age, right = 1 }; else return new Person { name = "学生/员工1", age = age, right = 0 }; }
部署地址3:http://139.244.52.136:230/MyBaseService.svc
对应AccessRight代码3:
public Person AccessRight(int age) { if (age > 27) return new Person { name = "管理员2", age = age, right = 1 }; else return new Person { name = "学生/员工2", age = age, right = 0 }; }
当“接口”与其对应的“3个Web服务”全部准备就绪,我们最后就来创建一个客户端以“负载均衡”的算法形式远程调用这3个web服务。
我们再次打开Visual Studio,新建第三个名字叫“YZZ.Client”的解决方案+控制台工程。然后,我们做如下四步操作:
【第一步】:为工程添加DeveloperSharp.dll核心组件引用。并添加YZZ.Interface.dll的引用。
【第二步】:创建一个名为DeveloperSharp.xml的配置文件,并在该文件中设置如上3个web服务的负载均衡策略。文件内容如下:
<?xml version="1.0" encoding="utf-8" ?> <DeveloperSharp> <IServiceList> <IService Id="YZZ.Interface.IBase"> <Service Id="A11" Enable="true" Weight="100" BindType="basicHttpBinding" Address="http://139.244.52.136:120/MyBaseService.svc"/> <Service Id="A22" Enable="true" Weight="100" BindType="basicHttpBinding" Address="http://139.244.52.136:220/MyBaseService.svc"/> <Service Id="A33" Enable="true" Weight="100" BindType="basicHttpBinding" Address="http://139.244.52.136:230/MyBaseService.svc"/> </IService> </IServiceList> </DeveloperSharp>
对此xml配置文件说明如下:
(1) 每一个IService节点代表了一组Web服务,此节点的Id值(本文示例值是:YZZ.Interface.IBase)必须是这组Web服务的“基类接口”的完全限定名。
(2) Service节点中的Weight属性代表了使用权重。本文示例的3个服务的Weight值分别是100、100、100,则这3个服务的负载均衡使用分配比例将会是1:1:1。若把这三个值分别设置为100、50、50,则这3个服务的使用分配比例将会变为2:1:1。设置成你想要的比例吧。
(3) Service节点中的Enable属性代表了是否可用。true代表可用,false代表不可用。
【第三步】:在工程配置文件App.config(或Web.config)中添加appSettings节点,节点内容如下:
<appSettings> <add key="DatabaseType" value="" /> <add key="ConnectionString" value="" /> <add key="ErrorPage" value="" /> <add key="ErrorLog" value="D:Test2YZZ.Clientlog.txt" /> <add key="ConfigFile" value="D:Test2YZZ.ClientDeveloperSharp.xml" /> </appSettings>
其中,ConfigFile的设置是为了链接前述的DeveloperSharp.xml这个配置文件。ErrorLog则是设置一个错误日志文件。它们均需要设置为文件的“绝对路径”(此处使用“绝对路径”而不是“相对路径”,一是有利于安全性,二是有利于分布式部署)
【第四步】:在控制台应用类的代码中,添加创建“Web服务”负载均衡调用的工具IUtility.GetService<T>(),并通过该工具调用远程的Web服务,注意:核心代码就一行而已!!此示例连续3次调用Web服务,看会显示什么结果。如下:
class Program { static void Main(string[] args) { YZZ.Interface.Person p; DeveloperSharp.Framework.CoreUtility.IUtility IU = new DeveloperSharp.Framework.CoreUtility.Utility();//创建“Web服务”负载均衡调用的工具 //第一次调用远程Web服务 p = IU.GetService<YZZ.Interface.IBase>().AccessRight(20); Console.WriteLine(p.name); //第二次调用远程Web服务 p = IU.GetService<YZZ.Interface.IBase>().AccessRight(20); Console.WriteLine(p.name); //第三次调用远程Web服务 p = IU.GetService<YZZ.Interface.IBase>().AccessRight(20); Console.WriteLine(p.name); Console.ReadLine(); } }
从以上示例代码我们可以清晰的得知:IUtility.GetService<T>()就是实现负载均衡的关键所在。泛型T需要设置为被调用“Web服务组”的“基类接口”,而IUtility.GetService<T>()方法则会每次根据配置的负载均衡策略创建对应的远程Web服务代理。
YZZ.Client控制台工程输出显示结果如下:
学生/员工
学生/员工1
学生/员工2
最后提示一点:若要把一组Web服务的负载均衡应用改为单Web服务应用,只需要把DeveloperSharp.xml配置文件中IService节点下的Service节点数量设置为一个即可实现。
后记:读完本文,你有没有发现,“微服务”中常用的注册、发现、服务降级、等功能,往往可通过修改DeveloperSharp.xml这个配置文件来实现?未完待续…
【本文采用.NET/C#语言撰写,如需其它语言版本,请联系作者索取。】
相关下载: