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

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

  • 相关阅读:
    设计模式系列
    Python3 系列之 可变参数和关键字参数
    设计模式系列
    【HANA系列】SAP HANA ODBC error due to mismatch of version
    【FICO系列】SAP FICO FS00修改科目为未清项目管理
    【FIORI系列】SAP OpenUI5 (SAPUI5) js框架简单介绍
    【HANA系列】SAP HANA SQL获取当前日期加若干天后的日期
    【HANA系列】SAP HANA SQL获取本周的周一
    【HANA系列】SAP HANA SQL获取当前日期
    【HANA系列】SAP HANA SQL获取当前日期最后一天
  • 原文地址:https://www.cnblogs.com/wanshutao/p/3921387.html
Copyright © 2020-2023  润新知