簡易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平衡服務器,這只是一個簡單的方案,肯定有很多很多問題沒有考慮到,希望大家指出討論。

不過我想雖然實現了請求的分發,但是面對真正的高並發環境,平衡服務器會不會成為另外一個瓶頸。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM