Дизайн сервера мгновенных сообщений - PullRequest
2 голосов
/ 13 января 2011

Предположим, у нас есть приложение для обмена мгновенными сообщениями, клиент-серверное, а не p2p. Фактический протокол не имеет значения, важна архитектура сервера. Указанный сервер может быть закодирован для работы в однопоточном непараллельном режиме с использованием неблокирующих сокетов, которые по определению позволяют нам эффективно (например, мгновенно) эффективно выполнять операции чтения-записи. Эта особенность неблокирующих сокетов позволяет нам использовать какую-то функцию выбора / опроса в самом ядре сервера и практически не тратить время на реальные операции чтения / записи сокетов, а тратить время на обработку всей этой информации. , Правильно написано, это может быть очень быстро, насколько я понимаю. Но есть и второй подход, который заключается в агрессивной многопоточности, создании нового потока (очевидно, с использованием некоторого пула потоков, потому что эта операция может быть (очень) медленной на некоторых платформах и при некоторых обстоятельствах), и позволяя этим потокам работать параллельно, в то время как основной фоновый поток обрабатывает accept () и прочее. Я видел, как этот подход объяснялся в разных местах в сети, поэтому он, очевидно, существует.

Теперь возникает вопрос: если у нас есть неблокирующие сокеты, немедленные операции чтения / записи и простой, легко кодируемый дизайн, почему второй вариант вообще существует? Какие проблемы мы пытаемся преодолеть со вторым дизайном, то есть потоками? AFAIK, которые обычно используются для обхода некоторых медленных и, возможно, блокирующих операций, но, похоже, таких операций там нет!

1 Ответ

1 голос
/ 13 января 2011

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

Причиной такой архитектуры по сравнению с однопоточным сервером является простота использования нескольких процессоров. Вы делаете больше работы, чем просто ввод / вывод. Вы должны анализировать сообщения, выполнять различную работу, возможно, даже запускать более мощные криптоалгоритмы. Все это занимает процессор. Если вы хотите масштабировать, использование преимуществ нескольких процессоров позволит вам масштабировать еще больше и / или сохранить задержку еще ниже для каждого клиента.

Некоторая выгода в таком дизайне может быть немного скомпенсирована тем фактом, что вам может потребоваться дополнительная блокировка в многопоточной среде, но все сделано правильно, и, безусловно, в зависимости от того, что вы делаете, это может быть огромной победой за счет большей сложности.

Кроме того, это может помочь преодолеть ограничения ОС. Пути ввода / вывода в ядре могут быть более распределены между процессорами. Не все операционные системы могут в полной мере поддерживать поток ввода-вывода из однопоточных приложений. В прежние времена существовали не все отличные альтернативы старому * nix select (), который обычно имел ограничение на количество файловых дескрипторов 1024, и подобные API-интерфейсы начали серьезно деградировать, как только вы сказали ему контролировать слишком много сокетов. Распределение всех этих клиентов по нескольким потокам или процессам помогло преодолеть это ограничение.

Что касается отображения 1: 1 между потоками, есть несколько причин для реализации этой архитектуры:

  • Более простая модель программирования, которая может привести к менее трудному поиску ошибок и более быстрой реализации.

  • Поддержка API блокировки. Это повсюду. Наличие потока, управляющего многими / всеми клиентами, а затем выполнение блокирующего вызова базы данных остановит всех. Даже чтение файлов может блокировать ваше приложение, и вы обычно не можете отслеживать обычные файловые дескрипторы / дескрипторы для событий ввода-вывода - или, когда вы можете, модель программирования часто исключительно сложна.

Недостатком здесь является то, что он не будет масштабироваться, по крайней мере, не с наиболее широко используемыми языками / фреймворком. Наличие тысяч собственных потоков ухудшит производительность. Хотя некоторые языки предоставляют гораздо более легкий подход, например, Erlang и в некоторой степени Go.

...