Серджио прав. Ваше приложение на данный момент, вероятно, лучше, чем традиционная модель Apache/Passenger. Если вы выберете маршрут с событиями, особенно на однопоточных платформах, таких как Ruby, вы НИКОГДА не сможете блокировать что-либо, будь то БД, серверы кэширования, другие HTTP-запросы, которые вы можете сделать - ничего.
Это то, что усложняет асинхронное (событийное) программирование — его легко заблокировать, обычно в форме синхронного дискового ввода-вывода или разрешений DNS. Неблокирующие (событийные) фреймворки, такие как nodejs, осторожны в том, что они (почти) никогда не предоставляют вам вызов функции фреймворка, который блокирует, скорее все обрабатывается с использованием обратных вызовов (включая запросы к БД).
Это может быть легче визуализировать, если вы посмотрите на сердцевину однопоточного неблокирующего сервера:
while( wait_on_sockets( /* list<socket> */ &$sockets, /* event */ &$what, $timeout ) ) {
foreach( $socketsThatHaveActivity as $fd in $sockets ) {
if( $what == READ ) { // There is data availabe to read from this socket
$data = readFromSocket($fd);
processDataQuicklyWithoutBlocking( $data );
}
elseif ($what == WRITE && $data = dataToWrite($fd)) { // This socket is ready to be written to (if we have any data)
writeToSocket( $fd, $data );
}
}
}
То, что вы видите выше, называется циклом событий. wait_on_sockets
обычно предоставляется ОС в форме системного вызова, такого как select, poll, epoll или kqueue. Если processDataQuicklyWithoutBlocking занимает слишком много времени, сетевой буфер вашего приложения, поддерживаемый ОС (новые запросы, входящие данные и т. д.), в конечном итоге заполнится, что приведет к отклонению новых подключений и тайм-ауту существующих, поскольку $socketsThatHaveActivity обрабатывается недостаточно быстро. . Это отличается от многопоточного сервера (например, типичной установки Apache) тем, что каждое соединение обслуживается с использованием отдельного потока/процесса, поэтому входящие данные будут считываться в приложение, как только они поступят, а исходящие данные будут отправлены без задержки. .
Когда вы делаете (например) запрос к БД, неблокирующие фреймворки, такие как nodejs, добавляют сокетное соединение сервера БД в список отслеживаемых сокетов ($ sockets), поэтому, даже если ваш запрос занимает некоторое время, ваш (только) поток не заблокирован на этом одном сокете. Скорее они обеспечивают обратный вызов:
$db.query( "...sql...", function( $result ) { ..handle result ..} );
Как вы можете видеть выше, db.query немедленно возвращается без каких-либо блокировок на сервере db. Это также означает, что вам часто приходится писать подобный код, если только сам язык программирования не поддерживает асинхронные функции (например, новый C#):
$db.query( "...sql...", function( $result ) { $httpResponse.write( $result ); $connection.close(); } );
Правило никогда не блокировать можно несколько ослабить, если у вас есть много процессов, каждый из которых запускает цикл событий (обычно это способ запуска кластера узлов) или используете пул потоков для поддержания цикла событий (Java Jetty, Netty и т. д.). , вы можете написать свой собственный на C/C++). Пока один поток чем-то заблокирован, другие потоки все еще могут выполнять цикл обработки событий. Но при достаточно большой нагрузке даже они не будут работать. Так что НИКОГДА НЕ БЛОКИРУЙТЕ событийный сервер.
Итак, как видите, событийные серверы обычно пытаются решить другую проблему — у них может быть очень много открытых подключений. В чем они преуспевают, так это в том, что просто перемещают байты с помощью легких вычислений (например, серверы комет, кэши, такие как memcached, лак, прокси, такие как nginx, squid и т. д.). Ничего не стоит, хотя они лучше масштабируются, время отклика обычно имеет тенденцию к увеличению (нет ничего лучше, чем резервировать целый поток для соединения). Конечно, может быть экономически/вычислительно невыполнимо запускать такое же количество потоков, как количество одновременных подключений.
Теперь вернемся к вашей проблеме — я бы рекомендовал вам по-прежнему использовать Nginx, так как он отлично подходит для управления соединениями (которое основано на событиях) — обычно это означает обработку поддержки активности HTTP, SSL и т. д. Затем вы должны подключить это к своему приложению Rails. используя FastCGI, где вам по-прежнему нужно запускать воркеры, но не нужно переписывать приложение, чтобы оно было полностью событийным. Вы также должны позволить Nginx обслуживать статический контент — нет смысла привязывать ваших воркеров Rails к чему-то, что Nginx обычно может делать лучше. Этот подход обычно масштабируется намного лучше, чем Apache/Passenger, особенно если вы используете веб-сайт с высокой посещаемостью.
Если вы можете написать все свое приложение для обработки событий, то это прекрасно, но я понятия не имею, насколько просто или сложно это сделать в Ruby.
person
tejas
schedule
09.05.2012