欢迎光临
我们一直在努力

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

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

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

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

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

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

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

代码如下:

(项目Svn地址: http://code.taobao.org/svn/MyMvcApp/MyProxy)

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

 

  • 海报
海报图正在生成中...
赞(0) 打赏
声明:
1、本博客不从事任何主机及服务器租赁业务,不参与任何交易,也绝非中介。博客内容仅记录博主个人感兴趣的服务器测评结果及一些服务器相关的优惠活动,信息均摘自网络或来自服务商主动提供;所以对本博客提及的内容不作直接、间接、法定、约定的保证,博客内容也不具备任何参考价值及引导作用,访问者需自行甄别。
2、访问本博客请务必遵守有关互联网的相关法律、规定与规则;不能利用本博客所提及的内容从事任何违法、违规操作;否则造成的一切后果由访问者自行承担。
3、未成年人及不能独立承担法律责任的个人及群体请勿访问本博客。
4、一旦您访问本博客,即表示您已经知晓并接受了以上声明通告。
文章名称:《C#手动做一个负载均衡服务器》
文章链接:https://www.456zj.com/525.html
本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址