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 设置会出错,所以我注掉了。

时间: 2024-11-25 10:13:10

C#手动做一个负载均衡服务器的相关文章

Haproxy做LB负载均衡集群的搭建和配置,可以通过web页面监控web服务器的运行状态

HAProxy提供高可用性.负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费.快速并且可靠的一种解决方案.HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理. 实验(一) 实验目的:使用Haproxy做负载均衡集群(七层) 实验环境准备: 客户端  IP地址:1.1.1.1 主机名waiwang web1   IP地址:1.1.1.10 主机名:localhost web2   IP地址:1.1.1.20 主机名:localhost 配置HA

nginx做反向负载均衡,后端服务器获取真实客户端ip

开门见山,操作如下: 首先,在前端nginx上需要做如下配置: location / { proxy_pass http://test1; proxy_set_hearder host $host; proxy_set_header X-Real_IP $remoute_addr; proxy_set_header $proxy_add_x_forwarded_for; }; nginx会在把请求转向后台real-server前把http报头中的ip地址进行替换:这样操作完成后,real-ser

Nginx反向代理、缓存、负载均衡服务器构建

代理服务可简单的分为正向代理和反向代理: 正向代理: 用于代理内部网络对Internet的连接请求(如VPN/NAT),客户端指定代理服务器,并将本来要直接发送给目标Web服务器的HTTP请求先发送到代理服务器上,然后由代理服务器去访问Web服务器, 并将Web服务器的Response回传给客户端: 反向代理: 与正向代理相反,如果局域网向Internet提供资源,并让Internet上的其他用户可以访问局域网内资源, 也可以设置一个代理服务器, 它提供的服务就是反向代理. 反向代理服务器接受来

7.创建负载均衡服务器lb01:

创建负载均衡服务器lb01: # optimization by onekey sed -i 's#SELINUX=enforcing#SELINUX=disabled#' /etc/selinux/config grep SELINUX=disabled /etc/selinux/config setenforce 0 getenforce /etc/init.d/iptables stop /etc/init.d/iptables stop chkconfig iptables off ch

用 LVS 搭建一个负载均衡集群(转)

http://blog.jobbole.com/87503/ 第一篇:<如何生成每秒百万级别的 HTTP 请求?> 第二篇:<为最佳性能调优 Nginx> 第三篇:<用 LVS 搭建一个负载均衡集群> 这篇文章是<打造3百万次请求/秒的高性能服务器集群>系列的第3部分,有关于性能测试工具以及优化WEB服务器部分的内容请参看以前的文章. 本文基于你已经优化好服务器以及网络协议栈的基础之上,并使用 iperf 与 netperf 工具测试将服务器已优化到支持 5

Nginx之负载均衡服务器揭秘

Nginx代理服务器, 一次性代理多台后端机器, 利用负载算法, 决定将当前请求传递给某台服务器执行. 有哪些后台服务器?例如微软的IIS,Apache,Nginx 负载算法是什么? 加权轮询. nginx的配置项: upstream 可以配置一个服务器集群 配置方式, 指出服务器列表, 并指明需要的算法即可: 典型的算法, 加权轮询. 配置如下: nginx –s reload 重启服务器 例如:此时通过浏览器请求 loadBalance.nginx.com /index.html 配置浏览器

一个负载均衡的实现

再高并发的编程中,多个服务器并发的协作工作中,只有好的均衡协调各自的负载才能充分利用各个服务器的资源 负载均衡的一个目标是:让资源丰富的某些设备的负载更大一些,否则就小,根据设备特性,合理分配负载 这里的这个方法是让资源越丰富,那么每当有新的负载来到,被分配到其上的概率就更大 首先,实时统计各个设备资源剩余量,或者各个设备的负载情况,然后获得每个设备获得负载概率的加权值,原则是 资源越多权值越大 其次,根据加权值,随机分配负载给设备(原则是权值越大,分配到请求的概率越大) 一个负载均衡的实现,布

6.Nginx作为负载均衡服务器应用

案例:Nginx作为负载均衡服务器应用 nginx的负载均衡功能是通过upstream命令实现的,因此他的负载均衡机制比较简单,是一个基于内容和应用的7层交换负载均衡的实现.Nginx负载均衡默认对后端服务器有健康监测能力,但是监测能力较弱,仅限于端口监测,在后端服务器比较少的情况下(10台以下)负载均衡能力表现突出.而对于有大量后端节点的负载应用,由于所有访问请求都从一台服务器进出,容易发生请求堵塞进而引发连接失败,因此无法充分发挥后端服务器的性能. Nginx负载均衡算法 Nginx的负载均

如何使用Weave以及Docker搭建Nginx反向代理/负载均衡服务器

Hi, 今天我们将会学习如何使用 Weave 和 Docker 搭建 Nginx 的反向代理/负载均衡服务器.Weave 可以创建一个虚拟网络将 Docker 容器彼此连接在一起,支持跨主机部署及自动发现.它可以让我们更加专注于应用的开发,而不是基础架构.Weave 提供了一个如此棒的环境,仿佛它的所有容器都属于同个网络,不需要端口/映射/连接等的配置.容器中的应用提供的服务在 weave 网络中可以轻易地被外部世界访问,不论你的容器运行在哪里.在这个教程里我们将会使用 weave 快速并且简单