HtttpListener и асинхронный метод BeginGetContext
От: gamburger  
Дата: 10.03.14 07:34
Оценка: -1 :)
Мне нужно сделать асинхронный сервер HtttpListener, который бы работал под большой нагрузкой. В инете полно различных реализаций такого сервера, начиная от вызова синхронного метода-слушателя GetContext в бесконечном цикле (потом обработчики запускаются в разных потоках, либо каждый раз в новых, либо из пула) и заканчивая реализацией с использованием асинхронных слушателей BeginGetContext , которые потом должны запускать обработчики. И у меня несколько вопросов:
1) почему при наличии готового асинхронного слушателя BeginGetContext , в инете полно других реализаций (почему разрабы просто не юзают готовый BeginGetContext, может быть он глючный\непроизводительный или еще чего? можно ли его юзать в высоконагруженном асинхронном приложении?)
2) юзает ли BeginGetContext пул потоков, или каждый раз при своем вызове должен создавать новый поток?
3)и самая большая проблема сейчас — я написал код с использованием Thread.Sleep и такое ощущение, что BeginGetContext всегда запускает обработчики в одном и том же потоке.
Вот код:


    public class Server
    {
        private readonly HttpListener listener;
        public Server(string prefix)
        {
            listener = new HttpListener();
            listener.Prefixes.Add(prefix);
            listener.Start();
//стартовый вызов слушателя
            IAsyncResult result = listener.BeginGetContext(CallBackHandler, listener);
            Console.Read();
        }

        private void CallBackHandler(IAsyncResult result)
        {
// поставили БРЯК на строке ниже
            HttpListenerContext context = listener.EndGetContext(result);
//зацикливаем вызов слушателя
            IAsyncResult res = listener.BeginGetContext(CallBackHandler, listener);
            System.Threading.Thread.Sleep(20000);
        }
    }

Я не знаю, почему, но если в браузере выполнить 2 запроса почти одновременно на мой сервер, то первый каллбэк выполняется сразу, а второй почему-то начинает выполняться только после окончания 20 секунд, то есть, после окончания первого каллбэка. Это заметно, так как бряк на 2-м запросе срабатывает только через 20 сек после запроса. Почему такое происходит? Это же не многопоточное приложение, так как потоки выполняются последовательно, тогда зачем нужен этот BeginGetContext ?
А может, BeginGetContext юзает пул и в нем всего 1 поток? Можно ли расширить макс. кол-во потоков для него?
И еще, если я усыпил поток, разве операционка не должна взять этот спящий и не приносящий никакой пользы поток и запустить на нем второй коллбэк?
Re: HtttpListener и асинхронный метод BeginGetContext
От: Rinbe Россия  
Дата: 10.03.14 10:37
Оценка:
У меня такое не повторяется как в твоем варианте, так и в моем:


 public class Server
    {
        private readonly HttpListener listener;

        public Server(string prefix)
        {
            listener = new HttpListener();
            listener.Prefixes.Add(prefix);
            listener.Start();
            //стартовый вызов слушателя
            IAsyncResult result = listener.BeginGetContext(CallBackHandler, listener);
            Console.Read();
        }

        private void CallBackHandler(IAsyncResult result)
        {
            // поставили БРЯК на строке ниже
            HttpListenerContext context = listener.EndGetContext(result);
            //зацикливаем вызов слушателя
            IAsyncResult res = listener.BeginGetContext(CallBackHandler, listener);
            HttpListenerRequest request = context.Request;
            // Obtain a response object.
            HttpListenerResponse response = context.Response;
            // Construct a response.
            string responseString = "<HTML><BODY> Hello world! I thread " + Thread.CurrentThread.ManagedThreadId +
                                    " </BODY></HTML>";
            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
            // Get a response stream and write the response to it.
            response.ContentLength64 = buffer.Length;
            System.IO.Stream output = response.OutputStream;
            output.Write(buffer, 0, buffer.Length);
            // You must close the output stream.
            output.Close();
            Thread.Sleep(20000);
        }
    }


Может ты много раз это повторил и все потоки на Thread.Sleep(20000) встали? У меня работает все ок, показывает id разных потоков.
Re[2]: HtttpListener и асинхронный метод BeginGetContext
От: gamburger  
Дата: 10.03.14 19:37
Оценка:
Здравствуйте, Rinbe, Вы писали:

R>У меня такое не повторяется как в твоем варианте, так и в моем:



R>
R> public class Server
R>    {
R>        private readonly HttpListener listener;

R>        public Server(string prefix)
R>        {
R>            listener = new HttpListener();
R>            listener.Prefixes.Add(prefix);
R>            listener.Start();
R>            //стартовый вызов слушателя
R>            IAsyncResult result = listener.BeginGetContext(CallBackHandler, listener);
R>            Console.Read();
R>        }

R>        private void CallBackHandler(IAsyncResult result)
R>        {
R>            // поставили БРЯК на строке ниже
R>            HttpListenerContext context = listener.EndGetContext(result);
R>            //зацикливаем вызов слушателя
R>            IAsyncResult res = listener.BeginGetContext(CallBackHandler, listener);
R>            HttpListenerRequest request = context.Request;
R>            // Obtain a response object.
R>            HttpListenerResponse response = context.Response;
R>            // Construct a response.
R>            string responseString = "<HTML><BODY> Hello world! I thread " + Thread.CurrentThread.ManagedThreadId +
R>                                    " </BODY></HTML>";
R>            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
R>            // Get a response stream and write the response to it.
R>            response.ContentLength64 = buffer.Length;
R>            System.IO.Stream output = response.OutputStream;
R>            output.Write(buffer, 0, buffer.Length);
R>            // You must close the output stream.
R>            output.Close();
R>            Thread.Sleep(20000);
R>        }
R>    }
R>


R>Может ты много раз это повторил и все потоки на Thread.Sleep(20000) встали? У меня работает все ок, показывает id разных потоков.


В твоем варианте не повторяется, так как ты поставил Thread.Sleep(20000) после output.Write(buffer, 0, buffer.Length);
если поставить Thread.Sleep(20000) прямо перед output.Write(buffer, 0, buffer.Length); , то у тебя повторится то же, что и у меня. Обработка второго запроса начнется только через 20 сек после окончания выполнения первого запроса и закончится только через 40 сек. Я как бы этим Thread.Sleep(20000) сымитировал очень долгое выполнение рассчетов до записи их в output.Write и почему-то многопоточность здесь не работает... В общем, это очень большая трабла, мне нужно как-то срочно ее решить. Какие могут быть варианты? И вообще, почему такое происходит?
Re[3]: HtttpListener и асинхронный метод BeginGetContext
От: Аноним  
Дата: 10.03.14 19:50
Оценка:
Здравствуйте, gamburger, Вы писали:


G> Я как бы этим Thread.Sleep(20000) сымитировал очень долгое выполнение рассчетов до записи их в output.Write и почему-то многопоточность здесь не работает...


Thread.Sleep(20000) — не эмулирует долгое выполнение расчетов, т.к. расчеты будут загружать процессор или 1 ядро на 100% во время своего выполнения.
Оно эмулирует ожидание I/O , когда процессор простаивает.
Re[4]: HtttpListener и асинхронный метод BeginGetContext
От: gamburger  
Дата: 10.03.14 20:21
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, gamburger, Вы писали:



G>> Я как бы этим Thread.Sleep(20000) сымитировал очень долгое выполнение рассчетов до записи их в output.Write и почему-то многопоточность здесь не работает...


А>Thread.Sleep(20000) — не эмулирует долгое выполнение расчетов, т.к. расчеты будут загружать процессор или 1 ядро на 100% во время своего выполнения.

А>Оно эмулирует ожидание I/O , когда процессор простаивает.

Да, но мне нужно было проверить асинхронность вызовов каллбэков и никакой асинхронности нету, несмотря на то , что BeginGetContext должен работать асинхронно... Точнее, есть, но почему-то response.OutputStream как будто лочится первым потоком и походу как не дает даже запуститься второму каллбэку. Какой смысл в этой "многопоточности"? Как это устранить?
Re[5]: HtttpListener и асинхронный метод BeginGetContext
От: Rinbe Россия  
Дата: 10.03.14 22:14
Оценка:
Здравствуйте, gamburger, Вы писали:

G>Здравствуйте, Аноним, Вы писали:


А>>Здравствуйте, gamburger, Вы писали:



G>>> Я как бы этим Thread.Sleep(20000) сымитировал очень долгое выполнение рассчетов до записи их в output.Write и почему-то многопоточность здесь не работает...


А>>Thread.Sleep(20000) — не эмулирует долгое выполнение расчетов, т.к. расчеты будут загружать процессор или 1 ядро на 100% во время своего выполнения.

А>>Оно эмулирует ожидание I/O , когда процессор простаивает.

G>Да, но мне нужно было проверить асинхронность вызовов каллбэков и никакой асинхронности нету, несмотря на то , что BeginGetContext должен работать асинхронно... Точнее, есть, но почему-то response.OutputStream как будто лочится первым потоком и походу как не дает даже запуститься второму каллбэку. Какой смысл в этой "многопоточности"? Как это устранить?


Судя по всему HttpListener так и работает: пока на запрос не отправлен ответ, следующий каллбэк не вызывается.
Re[6]: HtttpListener и асинхронный метод BeginGetContext
От: gamburger  
Дата: 11.03.14 21:50
Оценка:
Здравствуйте, Rinbe, Вы писали:


R>Судя по всему HttpListener так и работает: пока на запрос не отправлен ответ, следующий каллбэк не вызывается.


Странно , как такое может быть, если они утверждают, что это вариант для асинхронной работы? Получается, асинхронности нету практически вообще, так как вся обработка ответа идет до записи в output , но следующие обработчики никогда не запустятся до записи в output. Может есть способ заставить его работать асинхронно?
Re[7]: HtttpListener и асинхронный метод BeginGetContext
От: Rinbe Россия  
Дата: 12.03.14 15:32
Оценка:
Здравствуйте, gamburger, Вы писали:

G>Здравствуйте, Rinbe, Вы писали:



R>>Судя по всему HttpListener так и работает: пока на запрос не отправлен ответ, следующий каллбэк не вызывается.


G>Странно , как такое может быть, если они утверждают, что это вариант для асинхронной работы? Получается, асинхронности нету практически вообще, так как вся обработка ответа идет до записи в output , но следующие обработчики никогда не запустятся до записи в output.


С наружи выставили асинхронный интерфейс, а внутри сделали с блокировками.

G>Может есть способ заставить его работать асинхронно?


Нету, надо самому писать.
Re: HtttpListener и асинхронный метод BeginGetContext
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 12.03.14 20:27
Оценка:
Здравствуйте, gamburger, Вы писали:

G>А может, BeginGetContext юзает пул и в нем всего 1 поток? Можно ли расширить макс. кол-во потоков для него?


А может просто не запускается второй коллбек, пока не завершен первый?
Возможных причин такому поведению чуть менее, чем дофига. Но ни одна из них не мешает сделать сервер, который работает под большой нагрузкой. Достаточно обработку входящего соединения сделать такой же асинхронной, как и ожидание соединения.

Кстати почему бы не использовать IIS? Он и сам прекрасно масштабируется.
Re[2]: HtttpListener и асинхронный метод BeginGetContext
От: gamburger  
Дата: 12.03.14 20:57
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, gamburger, Вы писали:


G>>А может, BeginGetContext юзает пул и в нем всего 1 поток? Можно ли расширить макс. кол-во потоков для него?


G>А может просто не запускается второй коллбек, пока не завершен первый?

Нет, если Sleep поставить после записи в output, то второй поток вызовет обработчик до окончания слипа и первого обработчика. Ну вообще реально непонятно, для чего они сделал асинхронный метод, который работает как синхронный. Что можно сделать, чтобы httplistener заработал в настоящем асинхронном режиме?

G>Возможных причин такому поведению чуть менее, чем дофига. Но ни одна из них не мешает сделать сервер, который работает под большой нагрузкой. Достаточно обработку входящего соединения сделать такой же асинхронной, как и ожидание соединения.

Это как? по идее, BeginGetContext должен как раз это и делать.

Еще вопрос, при таком практически синхронном поведении обработчиков, можно ли вообще обойтись без локов(синхронизации) общих данных?
Re[2]: HtttpListener и асинхронный метод BeginGetContext
От: Rinbe Россия  
Дата: 12.03.14 21:05
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, gamburger, Вы писали:


G>>А может, BeginGetContext юзает пул и в нем всего 1 поток? Можно ли расширить макс. кол-во потоков для него?


G>А может просто не запускается второй коллбек, пока не завершен первый?


Не а. До тех пор пока в context.Request ни чего не записали, следующий CallBack не вызвается.

G>Возможных причин такому поведению чуть менее, чем дофига. Но ни одна из них не мешает сделать сервер, который работает под большой нагрузкой. Достаточно обработку входящего соединения сделать такой же асинхронной, как и ожидание соединения.


В данном случае это ни чего не дает, хоть в отдельном потоке обработку запускай.
Re[3]: HtttpListener и асинхронный метод BeginGetContext
От: drol  
Дата: 13.03.14 03:10
Оценка:
Здравствуйте, Rinbe, Вы писали:

R>Не а. До тех пор пока в context.Request ни чего не записали, следующий CallBack не вызвается.


А вот у меня всё успешно вызывается.
Re[7]: HtttpListener и асинхронный метод BeginGetContext
От: Rinbe Россия  
Дата: 13.03.14 08:49
Оценка:
Здравствуйте, gamburger, Вы писали:

G>Здравствуйте, Rinbe, Вы писали:



R>>Судя по всему HttpListener так и работает: пока на запрос не отправлен ответ, следующий каллбэк не вызывается.


G>Странно , как такое может быть, если они утверждают, что это вариант для асинхронной работы? Получается, асинхронности нету практически вообще, так как вся обработка ответа идет до записи в output , но следующие обработчики никогда не запустятся до записи в output. Может есть способ заставить его работать асинхронно?


Все просто оказывается, если использовать разные урлы или два разных браузера, запросы отрабатывают нормально, каждый за 20 секунд в своем потоке. Так что все ок. От одного клиента запросы на один урл обрабатываются последовательно. Так что все нормально.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.