Ответ 1
Обычно вам нужно работать в asynchronous, чтобы эти долгоживущие подключения работали. Существует несколько различных методов для асинхронного ввода-вывода; все из которых имеют свои преимущества и недостатки.
Тот, который уже должен быть знаком любому, кто работал с JavaScript и AJAX, является моделью обратного вызова; в котором вы отправляете запрос, и устанавливаете обратный вызов для вызова, когда он завершается. Вот как работает XMLHTTPRequest
, не блокируя все остальные страницы, пока они ждут завершения одного запроса страницы. Это также работает в рамках Twisted Python, хотя он может вызывать методы для объектов или функции обратного вызова в зависимости от используемых вами интерфейсов.
Еще одна мощная модель - стиль Erlang, называемый моделью Actor, имеет множество легких процессов (например, потоков, но с нет общего состояния), каждый из которых взаимодействует друг с другом через асинхронные сообщения. Время выполнения Erlang реализовано для того, чтобы сделать тысячи процессов очень эффективным; то вы можете просто иметь один процесс для каждого подключения и отправлять сообщения другим процессам, реализующим бэкэнд приложения. Процессы Erlang также могут автоматически планироваться на нескольких потоках ОС, чтобы в полной мере использовать многоядерные системы. ejabberd, популярный Jabber-сервер (протокол чата, который требует много долгоживущих открытых подключений), реализован в Erlang, также как и Система чата Facebook.
Новый Go language от Google использует подобный подход, ближе к Hoare, сообщающему последовательно, чем модель Actl Erlang, но которая имеет много общего.
В Mac OS X 10.6 Apple представила Grand Central Dispatch вместе с блоками (по сути, закрытие) в C, С++ и Objective-C, Это позволяет что-то вроде модели обратного вызова, зависящей от AJAX или Twisted style, но с явно управляемыми очередями, которые выполняются последовательно для управления доступом к общим ресурсам в многопоточной многоядерной среде. Twisted и JavaScript работают как однопоточные, поэтому могут использовать только одно ядро, если только вы не используете несколько процессов операционной системы, которые могут быть довольно тяжелыми и увеличивать затраты на связь между ними.
Тогда существуют более традиционные модели, такие как Unix select
или более современные и способные epoll
или kqueue()
. В них у вас обычно есть основной цикл в вашей программе, который настраивает кучу событий для просмотра (сетевой ввод-вывод возвращает еще несколько данных, файлы ввода-вывода возвращают больше данных, новое сетевое соединение и т.д.), а затем вызывает системный вызов, который блокируется до тех пор, пока не произойдет одно из этих событий, после чего вы проверите, какой из них произошел, а затем обработайте его соответствующим образом. Эти системные вызовы обычно используются для обеспечения вышеописанных структур, описанных выше.
Для очень хорошего обзора ошеломляющего массива доступных опций (с упором на более традиционные и более низкие уровни, подходы Unix), см. Проблема C10K, обзор различных методов, помогающих одновременно обрабатывать 10 000 одновременных соединений. У этого также есть хороший список библиотек C и С++ для абстрагирования по доступным различным API-интерфейсам, таким как libevent.
Конечным вариантом, конечно же, является использование одного процесса или одного потока ОС для каждого соединения. Проблема в том, что процессы имеют очень большой вес, и даже потоки довольно тяжелые по сравнению со многими из этих вариантов. В общем, для лучшей производительности вы хотели бы иметь один процесс или поток на каждый процессор, каждый из которых использует асинхронный API ввода-вывода, чтобы выяснить, когда ему нужно выполнить работу, а затем отправить эту работу одному из нескольких объектов или обратным вызовам которые были зарегистрированы для обработки соединений или один из нескольких легких процессов Erlang, ожидающих сообщения, или что-то в этом роде.
В качестве побочного примечания, соединение в веб-сокетах не является HTTP-соединениями, а новым протоколом, протоколом websocket, хотя вы можете использовать тот же порт, что и HTTP, и обновить HTTP-соединение с веб-сокетом, чтобы быть совместимым с существующими правилами брандмауэра.