Erlang принимает входящие TCP-соединения динамически - PullRequest
10 голосов
/ 27 марта 2011

Что я пытаюсь решить: иметь сервер Erlang TCP, который прослушивает определенный порт (код должен находиться в каком-либо внешнем интерфейсе / API), и каждое входящее соединение должно обрабатываться gen_server (что даже gen_tcp:accept должно быть закодировано внутри gen_server), но я на самом деле не хочу изначально порождать заранее определенное количество процессов, которые принимают входящее соединение). Это как-то возможно?

Ответы [ 4 ]

10 голосов
/ 27 марта 2011

Основная процедура

У вас должен быть один статический процесс (реализованный как gen_server или пользовательский процесс), который выполняет следующую процедуру:

  1. Прослушивает входящие соединения, используя gen_tcp:accept/1
  2. Каждый раз, когда он возвращает соединение, сообщайте супервизору о порождении рабочего процесса (например, другого gen_server процесса)
  3. Получите pid для этого процесса
  4. Вызовите gen_tcp:controlling_process/2 с вновь возвращенным сокетом и этим pid
  5. Отправьте сокет этому процессу

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

Процесс прослушивания должен иметь только одну ответственность,и это нерест рабочих для новых связей.Этот процесс будет блокироваться при вызове gen_tcp:accept/1, что нормально, потому что запущенные работники будут обрабатывать текущие соединения одновременно.Блокировка при принятии гарантирует самое быстрое время отклика, когда новые соединения инициированы.Если процессу необходимо выполнить другие промежуточные действия, gen_tcp:accept/2 можно использовать с другими действиями, чередующимися между тайм-аутами.

Масштабирование

  • Вы можете иметь несколько процессов, ожидающих с gen_tcp:accept/1 в одном прослушивающем сокете, что еще больше увеличивает параллелизм и минимизирует задержку принятия.

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

  • Третий и последний этап - сделать ваши процессы более легкими, внедрив принципы разработки OTP в ваши собственные пользовательские процессы, используя proc_lib (больше информации ).Однако это следует делать только в том случае, если вы проводите тестирование и придете к выводу, что именно поведение gen_server замедляет вас.

4 голосов
/ 27 марта 2011

Проблема с gen_tcp:accept заключается в том, что он блокируется, поэтому, если вы вызываете его в пределах gen_server, вы блокируете сервер от получения других сообщений.Вы можете попытаться избежать этого, передав тайм-аут, но это в конечном итоге сводится к форме опроса, которую лучше избегать.Вместо этого вы можете попробовать gen_nb_server Кевина Смита;он использует внутреннюю недокументированную функцию prim_inet:async_accept и другие prim_inet функции, чтобы избежать блокировки.

3 голосов
/ 27 марта 2011

Возможно, вы захотите проверить http://github.com/oscarh/gen_tcpd и использовать функцию handle_connection, чтобы преобразовать полученный вами процесс в gen_server.

2 голосов
/ 27 марта 2011

Вы должны использовать «prim_inet: async_accept (Listen_socket, -1)», как сказал Стив. Теперь входящее соединение будет принято вашим обратным вызовом handle_info (при условии, что ваш интерфейс также является gen_server), так как вы использовали асинхронный принять вызов.

Принимая соединение, вы можете запустить еще один ger_server (я бы порекомендовал gen_fsm) и сделать это как «контролирующий процесс», вызвав "gen_tcp: control_process (CliSocket, Pid запущенного процесса)".

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

...