Node.JS: Пример HTTP-сервера в режиме prefork с использованием Web Workers

Original author: Peter Griess
  • Translation
Как обещал ранее, я публикую исходный код, демонстрирующий, как построить HTTP-сервер в режиме prefork, используя Web Workers и новый API net.Server.listenFD(). Я надеюсь, что этот код будет хорошим примером того, как легко нагрузить несколько ядер сервера, комбинируя пересылку файловых дескрипторов и Web Workers.

В master.js мы создаём сокет, прикрепленный к порту 8080, и порождаем 4 рабочих процесса для обработки запросов. Мы отправляем сообщение каждому обработчику со текстом, которую нужно будет использовать для всех ответов на запросы, а также файловый дескриптор нашего сокета.

  1. var path = require('path');
  2. var netBindings = process.binding('net');
  3. var Worker = require('webworker').Worker;
  4.  
  5. var fd = netBindings.socket('tcp4');
  6. netBindings.bind(fd, 8080);
  7. netBindings.listen(fd, 128);
  8.  
  9. for (var i = 0; i < 3; i++) {
  10.     var w = new Worker(path.join(__dirname, 'worker.js'));
  11.     w.postMessage({ 'banner' : 'Hello, world!' }, fd);
  12. }


В worker.js мы создаём экземпляр HTTP-сервера, но не вызываем listen(). Вместо этого мы ожидаем принятия сообщений от родительского процесса. При получении события иы забираем файловый дескриптор из сообщения и используем его для привязки нашего экземпляра http.Server к сокету. Как только будет вызван http.Server.listenFD(), этот процесс начнёт обрабатывать запросы.

  1. var assert = require('assert');
  2. var http = require('http');
  3.  
  4. var banner = undefined;
  5.  
  6. var srv = http.createServer(function(req, resp) {
  7.     resp.writeHead(200, {'Content-Type' : 'text/plain'});
  8.     resp.write(banner + ' (pid ' + process.pid + ')\n');
  9.     resp.end();
  10. });
  11.  
  12. onmessage = function(msg) {
  13.     assert.ok(msg.fd && msg.fd > 0);
  14.  
  15.     banner = msg.data.banner;
  16.  
  17.     srv.listenFD(msg.fd);
  18. };


Когда мы запускаем master.js, мы можем использовать curl для проверки, что запросы были обработаны разными процессами.

  1. % curl 'http://localhost:8080'
  2. Hello, world! (pid 27727)
  3. % curl 'http://localhost:8080'
  4. Hello, world! (pid 27728)
  5. % curl 'http://localhost:8080'
  6. Hello, world! (pid 27729)
  7. % curl 'http://localhost:8080'
  8. Hello, world! (pid 27727)


Очень просто.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 28

    +2
    Как можно заметить, это техника очень близка к реализации nginx (один мастер и несколько воркеров).

    Для тех, кто хочет опробовать, — этот API доступен только в ветке master репозитория github.com/ry/node, пока не документирован.

    Официально он появится в ветке 0.1.98, насколько можно ожидать.

    P.S.: сегодня был зафиксирован коммит, который позволяет обрабатывать не только TCP v4, но и другие варианты сокетов (например, сокеты Unix).
      –2
        +2
        Эта статья применима только к nginx.

        Во-первых, Node.JS создаёт только один контекст, то есть нет затрат на создание контекстов.

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

        В-третьих, в node.js замечательно работает сборка мусора. Процесс может ожиреть в час пик, но потом от исправно отдаст освобождённую память.
        0
        Чето термин не очень. Они действительно его употребляют?
        Дело в том, что слова «http-сервер в режиме prefork» уже были у apache и это совершенно другая модель обработки запросов.
          0
          Ну это не совсем префорк, да.
            0
            Дайте более правильное словосочетание, пожалуйста.

            Сам полчаса пытался подобрать аналог preforking HTTP server.

            Кстати, ведь и действительно это prefork. Просто у Node.JS всё описывается с помощью Javascript. Даже сам сервер по большей части написан на Javascript.
              0
              Да не аналог же он! пусть будет Группа конечных автоматов.
              Да, есть сходства, но раз уж вам довелось одним из первых расписывать эту модель, сделайте все возможное для уменьшения неправильного понимания модули в будущем.
          +2
          Это просто здорово, Node.JS прямо рожден для тонких клиентов :)
            +2
            Да что же такое творится? Вторая статья про ноде.жс за сегодня. Очень хочется пощупать, но дедлайны не дают — если начать, то боюсь оторваться не получится.
              +1
              охблин, клевая штука. спасибо.
              • UFO just landed and posted this here
                  0
                  ИМХО false или null
                  • UFO just landed and posted this here
                      0
                      var banner;
                      безо всяких undefined, null и т.п.
                      • UFO just landed and posted this here
                        0
                        Понятно, что undefined :)
                        Если мне надо проверять состояние переменной, то я использую false или null
                        • UFO just landed and posted this here
                      • UFO just landed and posted this here
                      0
                      а как Вы про require('webworker') узнали? его ни в Documentation, ни в ChangeLog нету.
                      // как и require('querystring'), но это, имхо, очевидно
                        +2
                        Автор оригинального материала — немало коммитил в Node, так что знает :) Вы видите перевод, что видно по значку около статьи.

                        Вдобавок никто не мешает самому просмотреть список модулей ;)
                          0
                          точно ж, перевод.
                          список модулей я привык в документашке смотреть, а в ней webworker'ами не пахнет. ток на глагне обозначено, что когда-нибудь webworker'ы появятся в ноде.
                          • UFO just landed and posted this here
                        0
                        Кто-нибудь в курсе, почему при удалении воркера падает node?
                        Пробовал убивать из родительского процесса через worker.terminate()
                        Пробовал изнутри воркера через process.exit()
                        Пишет так:
                        events:11
                               throw arguments[1];
                                              ^
                        Error: ECONNREFUSED, Connection refused
                           at doConnect (net:840:19)
                           at Stream.connect (net:913:30)
                           at Object.createConnection (net:619:5)
                           at Object.<anonymous> (/Users/norlin/Yandex/node/lib/webworker-
                        child.js:106:13)
                           at Module._compile (module:384:23)
                           at Module._loadscriptSync (module:393:8)
                           at Module.loadSync (module:296:10)
                           at Object.runMain (module:447:22)
                           at node.js:208:10
                        
                          +1
                          Вы уже задали этот вопрос в конференции nodejs, думаю, автор вам там ответит :)

                          На первый взгляд, в примере просто не перехватывается исключение обрыва соединения с рабочим процессом.
                            0
                            Вдруг кто-то тут уже решил проблему :)
                            Да, похоже на то, но что с этим делать пока не оч ясно… Начал разбираться во внутренностях node, но т.к. это мое первое с ним общение, то пока не очень ясно что к чему…
                              0
                              Кстати, анонсирован модуль multinode, гляньте в его сторону.

                              Всё никак не соберусь его пощупать и анонсировать.
                                0
                                Спасибо, и кстати автор веб-воркеров тогда починил ту ошибку)
                          0
                          Если кому то интересны подробности почему это работает, можно почитать тут.

                          Only users with full accounts can post comments. Log in, please.