Martin Fowler (リンク)のパターンに基づいて、HTTP フロント コントローラーを開発しています。私の場合、コントローラーには次の責任があります。 - カプセル化されたデータをアンマーシャリングする - リクエストを承認する - ロギング - リクエストを別のサーバーに中継/転送する
次の可能な解決策が思い浮かびました: - (同期) IHttpHandler、WebClient または HttpWebRequest クラスで要求を転送する - (非同期) IHttpListener (非 IIS ソリューション) - (非同期) IHttpAsyncHandler
理想的な状況は、FC が CPU を消費することなく多数の同時要求 (>10000 TPS) を処理できることです。
ソリューションをテストするために、リクエストを行う 3 つのクライアント、中央に位置するフロント コントローラー、および FC によって渡されたリクエストに応答する 2 つのサーバーを持つ小さなフレームワークを作成しました。フレームワークは 3 つのシナリオのベンチマークを行います。最初に小さなペイロードで高速な応答をテストし、次に大きなペイロード (> 100KB) で高速な応答をテストし、最後に遅い応答 (>3 秒) と小さなペイロードでテストします。
同期 HTTP ハンドラーを使用した最後のテストでは、1 秒あたりのトランザクション数 (TPS) が極端に低くなりました (<25 TPS)。私の推測では、これはハンドラーが応答を待っているときにスレッドをブロックするためです。この問題を克服するために、非同期ハンドラーの実装を開始しました (以下のコードを参照)。問題は、単純に機能しないことです。
頭に浮かんだ最後の (テストされていない) 解決策は、HttpListener クラスを使用することです。これは、同時実行性をきめ細かく制御できる、より軽量なソリューションであると推測しています。José F. Romaniello (リンク) による RX フレームワークを使用した実装例を見てきました。
私の質問は、なぜハンドラーコードが機能しないのですか?これが最も効率的な方法ですか? または、HttpListenerソリューションを支持する必要があります。
非同期 HTTP ハンドラ コード:
public class ForwardRequestHandler : IHttpAsyncHandler
{
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
var uri = GetForwardUriFor(context.Request.Url.PathAndQuery);
var proxy = HttpWebRequest.Create(uri) as HttpWebRequest;
return proxy.BeginGetResponse(new AsyncCallback(EndProcessRequest), new ForwardedRequestContext(context, proxy));
}
public void EndProcessRequest(IAsyncResult result)
{
var proxy = result.AsyncState as ForwardedRequestContext;
proxy.TransferResponse(result);
}
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
throw new NotSupportedException();
}
private Uri GetForwardUriFor(string path)
{
var loadbalancer = new RoundRobinLoadBalancer();
var endpoint = loadbalancer.GetRandomEndPoint();
return new Uri(
string.Format("http://{0}{1}", endpoint, path)
);
}
}
public class ForwardedRequestContext
{
private readonly HttpContext context;
private readonly HttpWebRequest forwarder;
public ForwardedRequestContext(HttpContext context, HttpWebRequest forwarder)
{
this.context = context;
this.forwarder = forwarder;
}
public void TransferResponse(IAsyncResult ar)
{
var response = GetResponse();
var result = forwarder.EndGetResponse(ar);
response.StatusCode = 200;
response.ContentType = result.ContentType;
response.AddHeader("Content-Length", result.ContentLength.ToString());
result.GetResponseStream().CopyTo(response.OutputStream);
response.Flush();
result.Close();
}
private HttpResponse GetResponse()
{
return context.Response;
}
}