Web服务的负载均衡(Load Balance

(请在电脑上打开页面,会获取更好的阅读效果)

 

本文技术点思路梳理:

1.     创建Web服务组的通用基类“接口”。并在该“接口”上附加ServiceContractOperationContractWebInvoke三属性。

2.     创建WCF服务并实现上述“接口”。并在Web.config中添加<services><endpointBehaviors>两项配置,让WCF服务兼具Web服务的特性。

3.     把上述Web服务分别部署在网络上多个不同的地方,形成“Web服务组”。

4.     通过DeveloperSharp.xml来配置“Web服务组”的负载均衡策略。

5.     创建客户端,在其App.config/Web.config中添加链接DeveloperSharp.xmlappSettings节点。再在代码中创建基于DeveloperSharp.dll的“Web服务负载均衡调用”工具,并通过该工具调用远程的Web服务。

 

【要点综述1】:为了演示Web服务的负载均衡,本文会创建3个形式一致的web服务(或者说,创建1web服务但分别拷贝部署在3个不同的地址),然后在客户端创建一个代理以“负载均衡”的算法形式远程调用这3web服务。

【要点综述2】:为了实现“统一/透明”的调用形式连接“web服务端”与“客户端”两者,一个居中的“接口”类是必须创建的,且所有远程web服务需要继承此接口。

【要点综述3】:Web Api是一个经典的http服务提供形式,WCF也是一个经典的两端通信模式,本文还将会极巧妙的整合这两者,让一个服务一次编写同时具备Web ApiWCF两者的特性。

 

我们首先在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方法,并在该方法上附加了OperationContractWebInvoke两个属性。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中做如下两项配置,才能使得WCFWeb Api的特性在AccessRight方法身上完全见效。

配置一:在<system.serviceModel>节点下添加如下配置

    <!--用来对外发布WCFWebAPI-->

    <services>

      <service name="YZZ.Service.MyBaseService">

        <!-- 对应Web APIEnd Point -->

        <endpoint address="webapi" binding="webHttpBinding" contract="YZZ.Interface.IBase" behaviorConfiguration="WebApiEndPointBehavior" />

        <!-- 对应WCFEnd 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 ApiWCF的双重特性。其Web ApiWeb服务)的调用路径如下:

http://部署的服务器路径(ip+port)/MyBaseService.svc/webapi/AccessRight

 

 

为了演示Web服务的负载均衡的效果,现在我们把AccessRight方法的内容稍做修改成3个版本,分别编译YZZ.Service工程后部署在3个不同的地方(如何部署请参考其它资料,这是基础知识)。如下:

部署地址1http://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 };

        }

部署地址2http://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 };

        }

部署地址3http://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 };

        }

 

 

当“接口”与其对应的“3Web服务”全部准备就绪,我们最后就来创建一个客户端以“负载均衡”的算法形式远程调用这3web服务。

我们再次打开Visual Studio,新建第三个名字叫“YZZ.Client”的解决方案+控制台工程。然后,我们做如下四步操作:

【第一步】:为工程添加DeveloperSharp.dll核心组件引用。并添加YZZ.Interface.dll的引用。

【第二步】:创建一个名为DeveloperSharp.xml的配置文件,并在该文件中设置如上3web服务的负载均衡策略。文件内容如下:

<?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值分别是100100100,则这3个服务的负载均衡使用分配比例将会是111。若把这三个值分别设置为1005050,则这3个服务的使用分配比例将会变为211。设置成你想要的比例吧。

(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:\Test2\YZZ.Client\log.txt" />

    <add key="ConfigFile" value="D:\Test2\YZZ.Client\DeveloperSharp.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这个配置文件来实现?未完待续