Erlang принимает входящие TCP-соединения динамически
Что я пытаюсь решить: иметь сервер Erlang TCP, который прослушивает определенный порт (код должен находиться в каком-то внешнем интерфейсе /API), и каждое входящее соединение должно обрабатываться gen_server
(это даже gen_tcp:accept
должны быть закодированы внутри gen_server
), но на самом деле я не хочу изначально порождать заранее определенное количество процессов, которые принимают входящее соединение). Это как-то возможно?
4 ответа
Основная процедура
У вас должен быть один статический процесс (реализованный как gen_server
или пользовательский процесс), который выполняет следующую процедуру:
- Прослушивает входящие соединения, используя
gen_tcp:accept/1
- Каждый раз, когда он возвращает соединение, скажите супервизору порождать рабочий процесс (например, другой
gen_server
процесс) - Получить pid для этого процесса
- Вызов
gen_tcp:controlling_process/2
с недавно возвращенным сокетом и этим пидом - Отправить сокет этому процессу
Примечание. Вы должны сделать это в таком порядке, иначе новый процесс может использовать сокет до передачи права собственности. Если этого не сделать, старый процесс может получить сообщения, связанные с сокетом, когда новый процесс уже вступил во владение, что приведет к отброшенным или неправильно обработанным пакетам.
Процесс прослушивания должен иметь только одну ответственность, и это нерест работников для новых соединений. Этот процесс будет блокироваться при вызове gen_tcp:accept/1
Это нормально, потому что запущенные работники будут обрабатывать текущие соединения одновременно. Блокировка при принятии гарантирует самое быстрое время отклика, когда новые соединения инициированы. Если процесс должен делать другие вещи между ними, gen_tcp:accept/2
может использоваться с другими действиями, чередующимися между таймаутами.
пересчет
Вы можете иметь несколько процессов, ожидающих с
gen_tcp:accept/1
на одном сокете прослушивания, что еще больше увеличивает параллелизм и минимизирует задержку принятия.Еще одна оптимизация - предварительный запуск некоторых работников сокетов, чтобы еще больше минимизировать задержки после принятия нового сокета.
Вопрос с gen_tcp:accept
является то, что он блокирует, так что если вы вызываете его в gen_server
вы блокируете сервер от получения других сообщений. Вы можете попытаться избежать этого, передав тайм-аут, но это в конечном итоге сводится к форме опроса, которую лучше избегать. Вместо этого вы можете попробовать использовать gen_nb_server Кевина Смита; он использует внутреннюю недокументированную функцию prim_inet:async_accept
и другие prim_inet
функции, чтобы избежать блокировки.
Возможно, вы захотите проверить http://github.com/oscarh/gen_tcpd и использовать функцию handle_connection для преобразования полученного процесса в gen_server.
Вы должны использовать "prim_inet:async_accept(Listen_socket, -1)", как сказал Стив. Теперь входящее соединение будет принято вашим обратным вызовом handle_info (при условии, что ваш интерфейс также является gen_server), поскольку вы использовали асинхронный вызов accept.
Принимая соединение, вы можете порождать еще один ger_server(я бы порекомендовал gen_fsm) и сделать это "контролирующим процессом", вызвав "gen_tcp:control_process(CliSocket, Pid of spwned process)".
После этого все данные из сокета будут получены этим процессом, а не кодом вашего интерфейса. Таким образом, новый процесс управления будет создан для другого соединения.