标签:
负载均衡服务器最出名的当数 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 属性设置。标签:
原文地址:http://www.cnblogs.com/Leo_wl/p/4984634.html