Приложение на основе TcpListener, которое плохо масштабируется

У меня есть серверное приложение ECHO на основеTCPListener, Он принимает клиентов, читает данные и возвращает те же данные. Я разработал его, используя подход async / await, используяXXXAsync методы, предоставляемые структурой.

Я установил счетчики производительности, чтобы измерить количество входящих и исходящих сообщений и количество подключенных сокетов.

Я создал тестовое приложение, которое запускает 1400 асинхронныхTCPClientи отправляйте сообщения размером 1 Кб каждые 100-500 мс. Клиенты имеют случайное начало ожидания в начале 10-1000 мс, поэтому они не пытаются подключиться одновременно. У меня хорошо работает, я вижу в PerfMonitor 1400 подключенных, отправляющих сообщения с хорошей скоростью. Я запускаю клиентское приложение с другого компьютера. Процессор сервера и использование памяти очень мало, это Intel Core i7 с 8 ГБ оперативной памяти. Клиент кажется более загруженным, это i5 с 4 Гб оперативной памяти, но все равно даже не 25%.

Проблема в том, что я запускаю другое клиентское приложение. Соединения начинают терпеть неудачу в клиентах. Я не вижу значительного увеличения количества сообщений в секунду (более или менее увеличенного на 20%), но я вижу, что число подключенных клиентов составляет всего около 1900-2100, а не 2800 ожидаемых. Производительность немного снижается, и график показывает большие различия между максимальным и минимальным количеством сообщений в секунду, чем раньше.

Тем не менее, загрузка процессора не составляет даже 40%, а использование памяти все еще мало. Я попытался увеличить количество или пул потоков как на клиенте, так и на сервере:

ThreadPool.SetMaxThreads(5000, 5000);
ThreadPool.SetMinThreads(2000, 2000);

На сервере соединения принимаются в цикле:

while(true)
{
    var client = await _server.AcceptTcpClientAsync();
    HandleClientAsync(client);
}

HandleClientAsync функция возвращаетTask, но, как вы видите, цикл не ожидает обработки, просто продолжает принимать другого клиента. Эта функция обработки выглядит примерно так:

public async Task HandleClientAsync(TcpClient client)
{    
    while(ws.Connected && !_cancellation.IsCancellationRequested)
    {
        var msg = await ReadMessageAsync(client);
        await WriteMessageAsync(client, msg);
    }
}

Эти две функции только читают и записывают поток асинхронно.

Я видел, я могу начатьTCPListener указывая наbacklog сумма, но каково значение по умолчанию?

Почему может быть причина, по которой приложение не масштабируется, пока не достигнет максимальной загрузки ЦП?

Каким будет подход и инструменты, чтобы выяснить, в чем собственно проблема?

ОБНОВИТЬ

Я попробовалTask.Yield а такжеTask.Run подходы, и они не помогли.

Это также происходит с сервером и клиентом, работающими локально на одном компьютере. Увеличение количества клиентов или сообщений в секунду фактически снижает пропускную способность сервиса. 600 клиентов, отправляющих сообщение каждые 100 мс, генерируют большую пропускную способность, чем 1000 клиентов, отправляющих сообщение каждые 100 мс.

Исключений, которые я вижу на клиенте при подключении более чем 2000 клиентов, два. Приблизительно с 1500 я вижу исключения в начале, но клиенты наконец соединяются. С более чем 1500 я вижу много подключения / отключения:

«Существующее соединение было принудительно закрыто удаленным узлом» (System.Net.Sockets.SocketException) A System.Net.Sockets.SocketException было обнаружено: «Существующее соединение было принудительно закрыто удаленным узлом»

«Невозможно записать данные в транспортное соединение: существующее соединение было принудительно закрыто удаленным хостом». (System.IO.IOException) Возникло исключение System.IO.IOException: «Невозможно записать данные в транспортное соединение: существующее соединение было принудительно закрыто удаленным хостом».

ОБНОВЛЕНИЕ 2

Я создал оченьпростой проект с сервером и клиентом с использованием async / await и он масштабируется, как и ожидалось.

Проект, где у меня есть проблема масштабируемостиэтот сервер WebSocketи даже когда он использует тот же подход, очевидно, что-то вызывает споры. Eстьконсольное приложение, содержащее компоненти консольное приложение длягенерировать нагрузку (хотя для этого требуется как минимум Windows 8).

Обратите внимание, что я прошу не ответ, чтобы решить проблему напрямую, а методы и подходы, чтобы выяснить, что является причиной этого спора.

Ответы на вопрос(2)

Ваш ответ на вопрос