zoukankan      html  css  js  c++  java
  • 简易WCF负载均衡方案

    简易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平衡服务器,这只是一个简单的方案,肯定有很多很多问题没有考虑到,希望大家指出讨论。

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

    BTW:求 苏州,上海地区有激情,有意义的技术类工作!!

    Email:kklldog@gmail.com 
    作者:Agile.Zhou(kklldog) 
    出处:http://www.cnblogs.com/kklldog/ 
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

     
     
    标签: WCF
  • 相关阅读:
    【产品分析】数据中台
    【设计】设计规范
    【产品分析】用户行为分析套路
    【产品复盘】谷歌Chrome是如何蚕食互联网的?
    【设计】图表设计
    command
    解决win7系统远程桌面 server 2003 卡的问题
    sqlmap遇到url重写的示例
    ASP中可能出现的一种包含漏洞(Server.execute)
    绕过杀毒软件抓取windows密码
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3114828.html
Copyright © 2011-2022 走看看