简易WCF负载均衡方案

最近跟高老师讨论nginx跟tomcat集群做负载均衡方案。感觉很有意思。想到自己项目中服务用的WCF技术,于是就想WCF如何做负载均衡,Google了一会,发现wcf4.0的路由服务好像可以实现。不过在研究路由服务期间,我有了个自己的方案,哈哈。

我要在客户端跟WCF服务中间部署一台WCF平衡服务器,用来分发请求,模拟nginx的工作。

image

WCF平衡服务器我同样用WCF来实现,所有服务接口全部通过平衡服务区暴露给客户端。对于客户端来说,只要跟正常调用服务一样,添加平衡器的远程服务引用。

实现:

1.平衡服务类库

namespace WcfSimpleBalance

{

    /// <summary>

    /// 负载均衡基类

    /// </summary>

    /// <typeparam name="T"></typeparam>

    public class WcfBalance<T>

    {

        private ChannelFactory<T> _channelFactory;



        public T BalanceProxy { get; set; }



        public WcfBalance(string serviceName)

        {

            string endpoint = EndpointBalance.GetBalanceEndpoint(serviceName);//获取随机endpoint

            this._channelFactory = new ChannelFactory<T>(endpoint);

            this.BalanceProxy = this._channelFactory.CreateChannel();

        }

    }

}

其中泛型T为协定,这样就能动态构建wcf的通道了。

namespace WcfSimpleBalance

{

    internal class EndpointBalance

    {

        /// <summary>

        /// 平衡节点配置

        /// </summary>

        private static List<WcfBalanceSection> _wcfBalanceCfg;

        static EndpointBalance()

        {

            _wcfBalanceCfg = ConfigurationManager.GetSection("wcfBalance") as List<WcfBalanceSection>;

        }



        /// <summary>

        /// 随机一个Endpoint

        /// </summary>

        /// <param name="serviceName"></param>

        /// <returns></returns>

        public static string GetBalanceEndpoint(string serviceName)

        {

            var serviceCfg = _wcfBalanceCfg.Single(s=>s.ServiceName==serviceName);

            var ran = new Random();

            int i = ran.Next(0, serviceCfg.Endpoints.Count);

            string endpoint = serviceCfg.Endpoints[i];

            Console.WriteLine(endpoint);

            return endpoint;

        }

    }

} 
这个类提供一个静态方法可以根据服务名称从配置文件中配置的endpoint,并且从中随机一个。随机数的算法可能分布不是特别均匀,不知有什么好的办法。
namespace WcfSimpleBalance

{

    /// <summary>

    /// 配置模型

    /// </summary>

    internal class WcfBalanceSection

    {

        public string ServiceName { get; set; }



        public List<string> Endpoints { get; set; }

    }
    /// <summary>

    /// 自定义配置处理

    /// </summary>

    public class WcfBalanceSectionHandler : IConfigurationSectionHandler

    {

        public object Create(object parent, object configContext, XmlNode section)

        {

            var config = new List<WcfBalanceSection>();

            foreach (XmlNode node in section.ChildNodes)

            {

                if (node.Name != "balanceService")

                    throw new ConfigurationErrorsException("不可识别的配置项", node);

                var item = new WcfBalanceSection();

                foreach (XmlAttribute attr in node.Attributes)

                {

                    switch (attr.Name)

                    {

                        case "ServiceName":

                            item.ServiceName = attr.Value;

                            break;

                        case "Endpoints":

                            item.Endpoints = attr.Value.Split(',').ToList();

                            break;

                        default:

                            throw new ConfigurationErrorsException("不可识别的配置属性", attr);

                    }

                }

                config.Add(item);

            }

            return config;

        }

    }

}
这2个是用来处理配置文件的。

2.普通的WCF服务

协定:
namespace WcfServiceContracts

{

     [ServiceContract(Name = "CalculatorService")]

    public interface IAdd

    {

         [OperationContract]

         int Add(int x, int y);

    }

}

一个简单的加法。

wcf服务实现:
namespace WcfService

{

    public class AddServices:IAdd

    {

        public int Add(int x, int y)

        {

            return x + y;

        }

    }

}

3.WCF平衡器实现

同样新建一个wcf服务类库,引用同样的协定,引用上面的平衡类库

namespace WcfServiceBalance

{

    public class AddServices : WcfBalance<IAdd>, IAdd

    {

        public AddServices()

            : base("AddServices")

        {

        }



        public int Add(int x, int y)

        {

            return BalanceProxy.Add(x, y);

        }

    }

}

继承WcfBalance跟协定接口。构造函数调用基类的构造函数,传入服务名称。Add实现直接调用基类的方法。

模拟:

1.wcf服务器寄宿

WCF服务可以寄宿在多个方案下面,IIS,win服务,控制台。这里为了方便直接寄宿在控制台下。

新建2个控制台程序,一个寄宿普通的wcf服务。一个寄宿wcf平衡服务。代码不表,给出服务地址。

3个普通的服务。(把寄宿普通服务的控制台程序的bin目录复制3份,改3个端口就成了3个服务)

http://localhost:8081/Wcf 

http://localhost:8082/Wcf

http://localhost:8083/Wcf

平衡服务

http://localhost:8088/WcfBalance

配置文件

在平衡服务器的配置文件中定义所有后台服务器的endpoint,然后在自定义wcfBalance节点中配置,服务名对应的endpoint列表用逗号分隔。

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <configSections>

    <section name="wcfBalance" type="WcfSimpleBalance.WcfBalanceSectionHandler, WcfSimpleBalance" />

  </configSections>

    <wcfBalance>

       <balanceService ServiceName="AddServices"  Endpoints="AddService1,AddService2,AddService3" />

    </wcfBalance>

  <system.serviceModel>

    <bindings>

      <basicHttpBinding>

        <binding name="BasicHttpBinding_CalculatorService" />

      </basicHttpBinding>

    </bindings>

    <client>

      <endpoint address="http://localhost:8081/Wcf" binding="basicHttpBinding"

          bindingConfiguration="BasicHttpBinding_CalculatorService"

          contract="WcfServiceContracts.IAdd" name="AddService1" />

      <endpoint address="http://localhost:8082/Wcf" binding="basicHttpBinding"

          bindingConfiguration="BasicHttpBinding_CalculatorService"

          contract="WcfServiceContracts.IAdd" name="AddService2" />

      <endpoint address="http://localhost:8083/Wcf" binding="basicHttpBinding"

          bindingConfiguration="BasicHttpBinding_CalculatorService"

          contract="WcfServiceContracts.IAdd" name="AddService3" />

    </client>

  </system.serviceModel>

    <startup> 

        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />

    </startup>

</configuration>

2.客户端调用

添加平衡服务器引用,然后用代码调用。

启动30个线程去跑服务。

namespace WcfServiceClient

{

    class Program

    {

        static void Main(string[] args)

        {

            for (int i = 0; i < 30; i++)

            {

                var thread = new Thread(new ThreadStart(CallAdd));

                thread.Start();

            }

            Console.Read();

        }



        private static void CallAdd()

        {

            using (var proxy = new CalculatorServiceClient())

            {

                proxy.Add(1,2);

            }

        }

    }

}

运行结果:

image

请求被分布到3个服务上面,不过貌似不太均匀,这个跟算法有关系。

通过以上我们实现了一个简单的wcf平衡服务器,这只是一个简单的方案,肯定有很多很多问题没有考虑到,希望大家指出讨论。

不过我想虽然实现了请求的分发,但是面对真正的高并发环境,平衡服务器会不会成为另外一个瓶颈。

你可能感兴趣的:(负载均衡)