zoukankan      html  css  js  c++  java
  • 负载均衡

    C#手动做一个负载均衡服务器

    思路

    负载均衡服务器最出名的当数 Nginx了。Nginx服务器通过异步的方式把连接转发给内网和N个服务器,用来分解单台应用服务器的压力,了解了原理及场景后,用C#来实现一个。思路如下:

    1. 使用一个站点的 Application_BeginRequest 来接收连接,转发连接。

    2. 对各类静态资源做单独处理,(可转可不转)

    3. 可以转发Get,Post,异步转发。

    4. 对指定的请求,转发到同一台服务器,保持使用者的登录状态。

    实现

    Vs2015建一个Mvc建站: localhost:1500/。修改Web.config,用于接收所有连接。

     <system.webServer> 
        <modules runAllManagedModulesForAllRequests="true">
        </modules>
     </system.webServer>

    引入 MyCmn.dll (http://code.taobao.org/svn/MyOql/libs4),MyHelper 封装了 类型转换函数,方便使用。

    代码如下:

    复制代码
            public class RequestWrap
            {
                public HttpWebRequest Request { get; set; }
                private ManualResetEvent Event { get; set; }
                private Action<HttpWebResponse> Action { get; set; }
                public RequestWrap(HttpWebRequest request)
                {
                    Event = new ManualResetEvent(false);
                    this.Request = request;
                }
    
                public void Run(Action<HttpWebResponse> act)
                {
                    this.Action = act;
    
                    Request.BeginGetResponse(new AsyncCallback(GetResponseCallback), this);
                    this.Event.WaitOne(15000);
                }
    
                private static void GetResponseCallback(IAsyncResult asyncResult)
                {
                    RequestWrap wrap = (RequestWrap)asyncResult.AsyncState;
                    HttpWebResponse response = null;
                    try
                    {
                        response = wrap.Request.EndGetResponse(asyncResult) as HttpWebResponse;
                    }
                    catch (WebException ex)
                    {
                        response = ex.Response as HttpWebResponse;
                    }
                    wrap.Action(response);
                    wrap.Event.Set();
                }
            }
    
            private void Application_BeginRequest(Object source, EventArgs e)
            {
                var lastExtName = "";
                {
                    var lastIndex = Request.Url.LocalPath.LastIndexOf('.');
                    if (lastIndex > 0)
                    {
                        lastExtName = Request.Url.LocalPath.Slice(lastIndex);
                    }
                }
    
                // <modules runAllManagedModulesForAllRequests="true"> 设置之后,静态资源就进来了。
                if (lastExtName.IsIn(".js", ".css", ".html", ".htm", ".png", ".jpg", ".gif"))
                {
                    return;
                }
    
                HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(GetTransferHost() + Request.RawUrl);
                myRequest.Proxy = null;
    
                myRequest.Timeout = 15000;
                myRequest.ReadWriteTimeout = 3000;
    
                myRequest.Method = Request.HttpMethod;
    
                Request.Headers.AllKeys.All(k =>
                {
                    if (!WebHeaderCollection.IsRestricted(k))
                    {
                        myRequest.Headers.Add(k, Request.Headers[k]);
                    }
                    else
                    {
                        var val = Request.Headers[k];
                        if (k.Is("Range"))
                        {
                            myRequest.AddRange(val.AsInt());
                        }
                        else if (k.Is("If-Modified-Since"))
                        {
                            myRequest.IfModifiedSince = val.AsDateTime();
                        }
                        else if (k.Is("Accept"))
                        {
                            myRequest.Accept = val;
                        }
                        else if (k.Is("Content-Type"))
                        {
                            myRequest.ContentType = val;
                        }
                        else if (k.Is("Expect"))
                        {
                            myRequest.Expect = val;
                        }
                        else if (k.Is("Date"))
                        {
                            myRequest.Date = val.AsDateTime();
                        }
                        else if (k.Is("Host"))
                        {
                            myRequest.Host = val;
                        }
                        else if (k.Is("Referer"))
                        {
                            myRequest.Referer = val;
                        }
                        else if (k.Is("Transfer-Encoding"))
                        {
                            myRequest.TransferEncoding = val;
                        }
                        else if (k.Is("User-Agent"))
                        {
                            myRequest.UserAgent = val;
                        }
                        //else if (k.Is("Connection"))
                        //{
                        //    o.Connection = val;
                        //}
                        else if (k.Is("KeepAlive"))
                        {
                            myRequest.KeepAlive = val.AsBool();
                        }
                    }
                    return true;
                });
    
                using (var readStream = Request.InputStream)
                {
                    while (true)
                    {
                        byte[] readBuffer = new byte[1024];
                        var readLength = readStream.Read(readBuffer, 0, readBuffer.Length);
                        if (readLength == 0) break;
                        myRequest.GetRequestStream().Write(readBuffer, 0, readLength);
                    }
                }
    
                new RequestWrap(myRequest).Run(myResponse =>
                {
                    myResponse.Headers.AllKeys.All(k =>
                    {
                        if (k.Is("X-Powered-By"))
                        {
                            return true;
                        }
                        Response.Headers[k] = myResponse.Headers[k];
                        return true;
                    });
    
                    using (var readStream = myResponse.GetResponseStream())
                    {
    
                        while (true)
                        {
                            byte[] readBuffer = new byte[1024];
                            var read = readStream.Read(readBuffer, 0, readBuffer.Length);
                            if (read == 0) break;
                            Response.OutputStream.Write(readBuffer, 0, read);
                        }
                    }
                    Response.StatusCode = myResponse.StatusCode.AsInt();
                });
                Response.End();
            }
    
            public static string GetClientIPAddress()
            {
                if (HttpContext.Current == null)
                    return string.Empty;
                HttpContext context = HttpContext.Current;//System.Web.HttpContext.Current;
    
                string ipAddress = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
    
                if (!string.IsNullOrEmpty(ipAddress))
                {
                    string[] addresses = ipAddress.Split(',');
                    if (addresses.Length != 0)
                    {
                        return addresses[0];
                    }
                }
    
                return context.Request.ServerVariables["REMOTE_ADDR"]; //+ " host " + context.Request.UserHostAddress;
            }
    
    
            private string GetTransferHost()
            {
                string[] hosts = new string[] { "http://localhost" };
    
                var index = GetClientIPAddress().Last() % hosts.Length ;
    
                return hosts[index];
            }
    复制代码

    解释

    其中, RequestWrap 是对异步请求包装的请求类。封装了一个 Run 方法进行异步调用。过滤了应用服务器的回发头 X-Powered-By 

    关于 WebHeaderCollection.IsRestricted ,是由于一个错误引出的: 异常处理:必须使用适当的属性或方法修改此标头,文章地址: http://blog.useasp.net/archive/2013/09/03/the-methods-to-dispose-http-header-cannot-add-to-webrequest-headers.aspx,摘录如下:

    你可以在这里设置其他限制的标头.
    注意:
    Range HTTP标头是通过AddRange来添加
    If-Modified-Since HTTP标头通过IfModifiedSince 属性设置
    Accept由 Accept 属性设置。
    Connection由 Connection 属性和 KeepAlive 属性设置。
    Content-Length由 ContentLength 属性设置。
    Content-Type由 ContentType 属性设置。
    Expect由 Expect 属性设置。
    Date由 Date属性设置,默认为系统的当前时间。
    Host由系统设置为当前主机信息。
    Referer由 Referer 属性设置。
    Transfer-Encoding由 TransferEncoding 属性设置(SendChunked 属性必须为 true)。
    User-Agent由 UserAgent 属性设置。
     
    其中: Connection 设置会出错,所以我注掉了。
     
    alarm   作者:NewSea     出处:http://newsea.cnblogs.com/    QQ,MSN:iamnewsea@hotmail.com

  • 相关阅读:
    图文描述Vs2005制作WEB应用程序安装包的方法[E8.Net正式用户可以找我们获取全部代码参考]
    20080517 ITIL本地化实施沙龙活动记录
    制作 类似 google 下拉框的用户自定义控件的实现思路
    SQL SERVER 2005 Reporting Services 部署报表管理站点在 有主机头的站点上时的处理方法
    五月 我们都是四川人
    通过VML技术 及 XSLT技术实现WEB图形化处理界面.
    工作流技术杂谈 2008
    Visual Studio 2005 通过静态变量及可系列化的HASHTABLE变量优化工厂模式(Factory)的效率,提升性能(E8.Net工作流应用系统优化系列四)
    2008年4月21日 QQ群关于服务级别管理的聊天记录,欢迎加入ITIL与服务管理新QQ群:58342347
    Tapestry5之Application Module
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4984634.html
Copyright © 2011-2022 走看看