Как масштабировать TCP-прослушиватель на современных многоядерных / многосетевых машинах - PullRequest
7 голосов
/ 22 октября 2009

У меня есть демон для записи на C, который должен обрабатывать 20-150K TCP-соединений одновременно. Они - длительные связи, и редко когда-либо разрушают. Они имеют очень небольшое количество данных (редко превышающее MTU даже ... это протокол стимула / ответа) при передаче в любой момент времени, но время отклика на них является критическим. Мне интересно, что текущее сообщество UNIX использует для получения большого количества сокетов, и минимизирует задержку при ответе на них. Я видел проекты, вращающиеся вокруг мультиплексирования соединений с рабочими пулами форка, потоками (на соединение), пулами потоков статического размера. Есть предложения?

Ответы [ 6 ]

7 голосов
/ 22 октября 2009

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

если обработка каждого ответа занимает некоторое время или если он использует какой-то блокирующий API (как почти все из БД), то вам понадобится некоторая многопоточность.

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

  • Другой способ состоит в том, чтобы использовать несколько потоков и дать каждому из них часть этих 150К соединений. каждый из них будет иметь свой собственный цикл процесса и работать в основном как однопоточный сервер, за исключением порта прослушивания, который будет обрабатываться одним потоком. Это помогает распределить нагрузку между ядрами, но если вы используете блокирующий ресурс, он заблокирует все соединения, обрабатываемые этим конкретным потоком.

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

2 голосов
/ 23 октября 2009

Если производительность критична, то вам действительно нужно пойти на решение многопоточного цикла событий - то есть пул рабочих потоков для обработки ваших соединений. К сожалению, для этого нет библиотеки абстракций, которая бы работала на большинстве платформ Unix (обратите внимание, что libevent является однопоточным, как и большинство этих библиотек цикла обработки событий), поэтому вам придется выполнять грязную работу самостоятельно.

В Linux это означает использование epoll-triggered epoll с пулом рабочих потоков (в Windows были бы порты завершения ввода-вывода, которые также отлично работают в многопоточной среде - я не уверен в других Unixes) .

Кстати, я проделал некоторую работу, пытаясь абстрагировать инициируемый краем epoll на портах завершения ввода-вывода Linux и Windows на http://nginetd.cmeerw.org (он находится в стадии разработки, но может дать некоторые идеи).

1 голос
/ 23 декабря 2009

я думаю, что ответ Хавьера имеет смысл. если вы хотите проверить теорию, то посмотрите проект javascript node .

Узел основан на движке Google v8, который компилирует javascript в машинный код и работает так же быстро, как и c, для определенных задач. Он также основан на libev и спроектирован полностью неблокирующим, что означает, что вам не нужно беспокоиться о переключении контекста между потоками (все выполняется в одном цикле событий). Это очень похоже на эрланга в этом отношении.

Писать высокопроизводительные серверы в javascript теперь очень легко с помощью узла. Вы также можете, приложив немного усилий, написать свой собственный код на c и создать привязки для узла, который будет вызывать его для выполнения фактической обработки (посмотрите на исходный код узла, чтобы узнать, как это сделать - документация немного схематична в момент). в качестве более уродливой альтернативы, вы можете создать свой собственный код c как приложение и использовать stdin / stdout для связи с ним.

Я сам проверил узел с количеством подключений более 150 тыс. Без каких-либо проблем (конечно, вам понадобится какое-то серьезное оборудование, если все эти соединения будут обмениваться данными одновременно). TCP-соединение в node.js в среднем использует всего 2-3 КБ памяти, поэтому теоретически вы можете обрабатывать 350-500 КБ-соединений на 1 ГБ ОЗУ.

Примечание. В настоящее время Node.js не поддерживается в Windows, но это только на ранней стадии разработки, и я предполагаю, что он будет перенесен на каком-то этапе.

Примечание 2 - вы должны убедиться, что код, на который вы звоните с узла, не блокирует

1 голос
/ 22 октября 2009

Если у вас есть доступ к конфигурации системы , не переусердствуйте и настройте некоторые iptables / pf / etc на соединения с балансировкой нагрузки через n экземпляров (процессов) демона как это будет работать из коробки. В зависимости от того, насколько заблокирован характер демона, n должно быть из числа ядер в системе или в несколько раз больше. Этот подход выглядит грубым, но он может обрабатывать сломанные демоны и даже перезапускать их при необходимости. Также миграция будет гладкой , поскольку вы можете начать переадресацию новых соединений на другой набор процессов (например, новый выпуск или миграцию на новый ящик) вместо прерываний обслуживания. Кроме того, вы получаете несколько функций, таких как привязка к источнику , которые могут значительно помочь кэширование и разрешение проблемных сессий.

Если у вас нет доступа к системе (или ops не может быть обеспокоен), вы можете использовать демон балансировки нагрузки (есть много открытых программ) вместо iptables / pf / etc и используйте также n сервисных демонов, как указано выше.

Также этот подход помогает с разделением привилегий портов. Если внешнему сервису требуется обслуживание на низком порте (<1024), вам нужен только балансировщик нагрузки, на котором выполняется привилегированное / или admin / root, или ядро.) </p>

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

0 голосов
/ 22 октября 2009

Вы должны начать с нуля? Вы можете использовать что-то вроде gearman .

0 голосов
/ 22 октября 2009

Для улучшения производительности select (2) было разработано несколько систем: kqueue , epoll и /dev/poll. Во всех этих системах вы можете иметь пул рабочих потоков, ожидающих выполнения задач; Вы не будете вынуждены настраивать все файловые дескрипторы снова и снова, когда закончите с одним из них.

...