2013-07-31 1 views
0

모노에서 HttpListener로 처리하는 멀티 스레드 HTTP 요청에서 문제가 발생했습니다. httpListener.BeginGetContext (_handleRequestCallback, null)처럼 보이는 것은 "동시에"두 개 이상의 요청을 처리 할 수 ​​없습니다. 예상대로 SyncTest으로 모든 일을모노의 HttpListener가 두 개 이상의 요청을 동시에 처리 할 수 ​​없습니까?

using System; 
using System.Net; 
using NUnit.Framework; 
using System.IO; 
using log4net; 
using System.Threading; 

namespace Other 
{ 
    public class ListenerTest 
    { 
     private ILog _log; 
     private HttpListener _httpListener; 
     private const int PORT = 8080; 
     private const int REQUESTS = 20; 

     private AsyncCallback _handleRequestCallback; 

     [TestFixtureSetUp] 
     public void SetUp() 
     { 
      ServicePointManager.DefaultConnectionLimit = REQUESTS; 
      ThreadPool.SetMinThreads(REQUESTS, REQUESTS); 
      ThreadPool.SetMaxThreads(REQUESTS, REQUESTS); 

      _httpListener = new HttpListener(); 
      _httpListener.Prefixes.Add(String.Format("http://*:{0}/", PORT)); 
      _httpListener.Start(); 

      _handleRequestCallback = new AsyncCallback(processAsync); 

      _log = LogManager.GetLogger(typeof(ListenerTest)); 
     } 

     [Test] 
     public void SyncTest() 
     { 
      Thread serverThread = new Thread(() => processSyncThread()); 
      serverThread.Start(); 
      for(int i = 0; i < REQUESTS; ++i) 
      { 
       makeRequest(i.ToString(), HttpStatusCode.OK, 1000); 
      } 
     } 

     [Test] 
     public void AsyncTest() 
     { 
      int serverThreads = 5; 
      Thread serverThread = new Thread(() => processAsyncThread(serverThreads)); 
      serverThread.Start(); 

      for(int i = 0; i < REQUESTS; ++i) 
      { 
       int position = i; 
       Thread thread = new Thread(() => makeRequest(position.ToString(), HttpStatusCode.OK, 5000)); 
       thread.Start(); 
      } 

      Thread.Sleep(5000); 
     } 

     private void processAsyncThread(int threads) 
     { 
      for(int i = 0; i < threads; ++i) 
       _httpListener.BeginGetContext(_handleRequestCallback, null); 
     } 

     private void processAsync(IAsyncResult asyncResult) 
     { 
      Thread thread = new Thread(() => processSync(_httpListener.EndGetContext(asyncResult))); 
      thread.Start(); 
//   processSync(_httpListener.EndGetContext(asyncResult)); 
      _httpListener.BeginGetContext(_handleRequestCallback, null); 
     } 

     private void processSyncThread() 
     { 
      while(true) 
      { 
       var context = _httpListener.GetContext(); 
       processSync(context); 
      } 
     } 

     private void processSync(HttpListenerContext context) 
     { 
      string request = context.Request.RawUrl.TrimStart('/'); 
      _log.InfoFormat("Received request:{0}", request); 
      Thread.Sleep(500); 
      using(StreamWriter writer = new StreamWriter(context.Response.OutputStream)) 
       writer.Write(request); 
      _log.InfoFormat("Sent response for:{0}", request); 
     } 

     private string makeRequest(string request, HttpStatusCode expectedCode, int timeout) 
     { 
      _log.InfoFormat("Sending {0} request.", request); 
      WebRequest webRequest = WebRequest.Create(String.Format("http://localhost:{0}/{1}", PORT, request)); 
      webRequest.Timeout = timeout; 
      try 
      { 
       HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse(); 
       if(expectedCode != response.StatusCode) 
        throw new IOException(String.Format(
         "Returned unexpected status code: {0} for request: {1} when expected: {2}", 
         response.StatusCode, request, expectedCode)); 

       StreamReader reader = new StreamReader(response.GetResponseStream()); 
       string responseContent = reader.ReadToEnd(); 
       response.Close(); 
       _log.InfoFormat("Received response:{0}", responseContent); 

       return responseContent; 
      } 
      catch(WebException exception) 
      { 
       if (exception.Response is HttpWebResponse 
        && expectedCode == ((HttpWebResponse)exception.Response).StatusCode) 
        return ""; 
       else if (exception.Status == WebExceptionStatus.Timeout 
        && expectedCode == HttpStatusCode.RequestTimeout) 
        return ""; 
       throw; 
      } 
     } 
    } 
} 

(이 NUNIT 및 log4net 실행이 필요하지만 그들은 쉽게 제거 할 수 있습니다) : 나는 작은 시험을 썼다. AsyncTest에서는 httpListener가 _handleRequestCallback을 두 번 이상 병렬로 호출하지 않는다는 것을 알 수 있습니다. 이전 요청이 처리되는 3 요청 시작 처리 :

01:24:23,788 [(null)-27] INFO Other.ListenerTest {(null)} - Sending 10 request. 
01:24:23,788 [(null)-30] INFO Other.ListenerTest {(null)} - Sending 13 request. 
... 
01:24:23,788 [(null)-23] INFO Other.ListenerTest {(null)} - Sending 8 request. 
01:24:23,793 [(null)-36] INFO Other.ListenerTest {(null)} - Sending 19 request. 
01:24:23,885 [(null)-40] INFO Other.ListenerTest {(null)} - Received request:12 
01:24:23,885 [(null)-39] INFO Other.ListenerTest {(null)} - Received request:5 
01:24:24,400 [(null)-39] INFO Other.ListenerTest {(null)} - Sent response for:5 
01:24:24,400 [(null)-40] INFO Other.ListenerTest {(null)} - Sent response for:12 
01:24:24,413 [(null)-20] INFO Other.ListenerTest {(null)} - Received response:5 
01:24:24,414 [(null)-29] INFO Other.ListenerTest {(null)} - Received response:12 
01:24:24,414 [(null)-54] INFO Other.ListenerTest {(null)} - Received request:0 
01:24:24,414 [(null)-53] INFO Other.ListenerTest {(null)} - Received request:15 
01:24:24,915 [(null)-54] INFO Other.ListenerTest {(null)} - Sent response for:0 
01:24:24,915 [(null)-53] INFO Other.ListenerTest {(null)} - Sent response for:15 
01:24:24,917 [(null)-15] INFO Other.ListenerTest {(null)} - Received response:0 
01:24:24,918 [(null)-32] INFO Other.ListenerTest {(null)} - Received response:15 
01:24:24,919 [(null)-65] INFO Other.ListenerTest {(null)} - Received request:18 
01:24:24,919 [(null)-66] INFO Other.ListenerTest {(null)} - Received request:3 
01:24:25,419 [(null)-65] INFO Other.ListenerTest {(null)} - Sent response for:18 
01:24:25,420 [(null)-66] INFO Other.ListenerTest {(null)} - Sent response for:3 
01:24:25,458 [(null)-18] INFO Other.ListenerTest {(null)} - Received response:3 
01:24:25,458 [(null)-77] INFO Other.ListenerTest {(null)} - Received request:10 
01:24:25,558 [(null)-35] INFO Other.ListenerTest {(null)} - Received response:18 
01:24:25,558 [(null)-80] INFO Other.ListenerTest {(null)} - Received request:6 
01:24:25,959 [(null)-77] INFO Other.ListenerTest {(null)} - Sent response for:10 
01:24:25,998 [(null)-27] INFO Other.ListenerTest {(null)} - Received response:10 
01:24:25,998 [(null)-81] INFO Other.ListenerTest {(null)} - Received request:4 
01:24:26,059 [(null)-80] INFO Other.ListenerTest {(null)} - Sent response for:6 
01:24:26,059 [(null)-21] INFO Other.ListenerTest {(null)} - Received response:6 
01:24:26,060 [(null)-82] INFO Other.ListenerTest {(null)} - Received request:2 

나는 MS 닷넷에 동일한 코드를 (하지만 log4net없이) 실행하고 나는 무엇을 기대 참조 :

01:38:39:754 Sending 9 request. 
01:38:39:738 Sending 5 request. 
01:38:39:738 Sending 3 request. 
01:38:39:738 Sending 1 request. 
01:38:40:660 Received request:18 
01:38:40:660 Received request:1 
01:38:40:660 Received request:0 
01:38:40:660 Received request:2 
01:38:40:660 Received request:3 
01:38:40:660 Received request:4 
01:38:40:676 Received request:5 
01:38:40:676 Received request:6 
01:38:40:676 Received request:7 
01:38:40:676 Received request:8 
01:38:40:692 Received request:9 
01:38:40:692 Received request:10 
01:38:40:692 Received request:11 
01:38:40:692 Received request:12 
01:38:40:707 Received request:13 
01:38:40:707 Received request:14 
01:38:40:707 Received request:15 
01:38:40:707 Received request:16 
01:38:40:723 Received request:17 
01:38:40:723 Received request:19 
01:38:41:160 Sent response for:4 
01:38:41:160 Received response:18 
01:38:41:160 Received response:1 
01:38:41:160 Sent response for:3 
01:38:41:160 Sent response for:2 
01:38:41:176 Received response:8 
01:38:41:160 Sent response for:1 
01:38:41:160 Sent response for:0 
01:38:41:160 Sent response for:18 
01:38:41:160 Received response:2 
01:38:41:160 Received response:3 
01:38:41:160 Received response:4 
01:38:41:176 Sent response for:8 
01:38:41:176 Sent response for:7 
01:38:41:176 Sent response for:6 
01:38:41:176 Sent response for:5 
01:38:41:176 Received response:7 
01:38:41:176 Received response:6 
01:38:41:176 Received response:5 
01:38:41:192 Received response:9 
01:38:41:192 Sent response for:9 
01:38:41:192 Received response:10 
01:38:41:192 Sent response for:10 
01:38:41:192 Received response:11 
01:38:41:192 Sent response for:11 
01:38:41:192 Received response:12 
01:38:41:192 Sent response for:12 
01:38:41:160 Received response:0 
01:38:41:207 Received response:16 
01:38:41:207 Sent response for:16 
01:38:41:223 Sent response for:19 
01:38:41:223 Received response:19 
01:38:41:223 Sent response for:13 
01:38:41:223 Sent response for:14 
01:38:41:223 Sent response for:15 
01:38:41:223 Sent response for:17 
01:38:41:238 Received response:13 
01:38:41:238 Received response:14 
01:38:41:238 Received response:15 
01:38:41:223 Received response:17 

나는이 문제가 있다고 생각 이 one과 관련되어 있지만 제안 된 해결 방법이 작동하지 않습니다. 누구도 해결 방법에 대한 아이디어가 있습니까?

+0

모노 버전은 무엇입니까? – knocte

+0

버전은 2.10.8.1입니다. (Debian 2.10.8.1-5ubuntu1) – 4ybaka

답변

0

모노 2.10이 너무 오래, 당신은 여전히 ​​버전과 같은 문제에 직면 경우에도 새 버전으로 시도

모노 3.2.x로 테스트 해보십시오 : 새로운 있기 때문에 자신의 모노 (마스터 지점) 컴파일 플래그 "- 서버"는 단지 implemented이고 푸시되었습니다.

+0

최신 버전의 모노를 사용하게되어 기쁩니다 만 [look] (http://www.go-mono.com/mono-downloads/download.html)) 3.2.x는 MacOS에서만 사용할 수 있습니다. 어디 우분투에 대한 deb 패키지를 찾을 수 있습니까? (내 프로젝트에 컴파일 된 버전을 사용할 수 없음) – 4ybaka

+0

프로젝트를 지원하고 싶다면 irc : //irc.oftc.net/debian-cli의 토론에 참여하십시오 (자원 봉사자가 기고 함) – knocte

관련 문제