Комментарии 15
Спасибо за статью. А зачем вы экспортируете accept_func? Она же нигде кроме этого модуля не используется.
0
Дело в том, что мы создаем процесс, вызывая функцию erlang:spawn_link/3 (spawn_link(?MODULE, accept_func, [LSocket]), чтобы вызов был успешен нужно экспортировать функцию accept_func. Использовать функцию с одним аргументом erlang:spawn_link/1 не получится, т.к. в accept_func нам дополнительно нужно передать слушающий сокет (LSocket).
0
О вот как, я почему то думал, что если делать spawn_link в том же модуле, то передаваемые функции экспортировать необязательно, тк они уже в его видимости. Но я только начал изучать erlang.
0
Статья не просто неполезная, она просто вредная.
В принципе, читать дальше в некоторых «костыльных» решениях это действительно нужно, в продакшене я бы предпочел использовать проверенные средства уже не стоит, потому как в этом контексте эта фраза говорит о том, что продакшна код автора ещё не видел.
Теперь давайте обсудим, почему непригоден для реальной жизни код, который здесь написан.
Во-первых, он совершенно несовместим с горячей загрузкой кода. Вообще. Никак.
Если бы автор сделал ?MODULE:accept_func, то тогда с жуткими непонятными глюками а также малопонятными сообщениями об ошибках, вызванными тем, что старая версия кода не отпускалась бы акцептором и OTP тупо прибивал бы процесс-акцептор с ошибкой, этот код обновлял бы версию.
Но нет. В этом случае даже при апгрейде кода в девелопмент режиме, когда надо регулярно перезагружать код, лог мог бы быть завален ошибками, не имеющими отношения к реальной проблеме.
Но на практике не будет и этого и это самое страшное. Сообщений об ошибках не будет.
Ведь автор запускает код через spawn_link даже не представляя себе всю опасность этого механизма.
Процесс, запущенный через spawn_link не находится под супервизором. Если приложение будет рестартнуто, акцептор останется не у дел. Он не находится в дереве супервизоров. Вся его жизнь и смерть пройдут мимо логов, потому что некому журналировать ошибки в этом микропроцессе.
Теперь давайте поговорим про настоящий продакшн, а не про придуманный. Про тот продакшн, в котором prim_inet работает и правильный, асинхронный accept реально работает.
Так вот в этом реальном продакшне выше указанный код будет частенько приводить к отключению всего сервиса. Дело в том, что секция
будет падать. По разным причинам: клиент отвалился в момент хендшейка, прислал неправильные данные или что-то ещё. Но суть в том, что это будет падать и с этим надо жить. Жить тут не получится, потому как несколько раз при приходе потока пользователя упадет мастер-процесс и на этом супервизор прибьет его как непригодный к жизни из-за вылетания больше допустимого количества раз за допустимое время.
В итоге, учитывая, что истинную причину увидеть не получится, в логах будет каша из {'EXIT', ...} без единого указания на реальную проблему.
Так что в реальном продакшне указанный здесь подход в таком исполнении вреден и неправилен.
А вот использование prim_inet действительно работает, притом работает предсказуемо и понятно.
В принципе, читать дальше в некоторых «костыльных» решениях это действительно нужно, в продакшене я бы предпочел использовать проверенные средства уже не стоит, потому как в этом контексте эта фраза говорит о том, что продакшна код автора ещё не видел.
Теперь давайте обсудим, почему непригоден для реальной жизни код, который здесь написан.
Во-первых, он совершенно несовместим с горячей загрузкой кода. Вообще. Никак.
Если бы автор сделал ?MODULE:accept_func, то тогда с жуткими непонятными глюками а также малопонятными сообщениями об ошибках, вызванными тем, что старая версия кода не отпускалась бы акцептором и OTP тупо прибивал бы процесс-акцептор с ошибкой, этот код обновлял бы версию.
Но нет. В этом случае даже при апгрейде кода в девелопмент режиме, когда надо регулярно перезагружать код, лог мог бы быть завален ошибками, не имеющими отношения к реальной проблеме.
Но на практике не будет и этого и это самое страшное. Сообщений об ошибках не будет.
Ведь автор запускает код через spawn_link даже не представляя себе всю опасность этого механизма.
Процесс, запущенный через spawn_link не находится под супервизором. Если приложение будет рестартнуто, акцептор останется не у дел. Он не находится в дереве супервизоров. Вся его жизнь и смерть пройдут мимо логов, потому что некому журналировать ошибки в этом микропроцессе.
Теперь давайте поговорим про настоящий продакшн, а не про придуманный. Про тот продакшн, в котором prim_inet работает и правильный, асинхронный accept реально работает.
Так вот в этом реальном продакшне выше указанный код будет частенько приводить к отключению всего сервиса. Дело в том, что секция
5. {ok, Pid} = tcp_client_sup:start_child(),
6. ok = gen_tcp:controlling_process(Socket, Pid),
7. tcp_fsm:set_socket(Pid, Socket),
будет падать. По разным причинам: клиент отвалился в момент хендшейка, прислал неправильные данные или что-то ещё. Но суть в том, что это будет падать и с этим надо жить. Жить тут не получится, потому как несколько раз при приходе потока пользователя упадет мастер-процесс и на этом супервизор прибьет его как непригодный к жизни из-за вылетания больше допустимого количества раз за допустимое время.
В итоге, учитывая, что истинную причину увидеть не получится, в логах будет каша из {'EXIT', ...} без единого указания на реальную проблему.
Так что в реальном продакшне указанный здесь подход в таком исполнении вреден и неправилен.
А вот использование prim_inet действительно работает, притом работает предсказуемо и понятно.
+15
Макс, кстати… Пользуясь случаем — ты в ЖЖ пару месяцев назад писал, что планируешь написать статью о том как правильно строить OTP приложения. Стоит ждать статью то?
+3
У меня просто вопрос для понимая работы erlang:
Если в схеме автора поста в tcp_listener повесить process_flag(trap_exit, true), в handle_info слушать 'Exit' и по нему перезапускать слушателя, повысится ли стабильность системы?
Если в схеме автора поста в tcp_listener повесить process_flag(trap_exit, true), в handle_info слушать 'Exit' и по нему перезапускать слушателя, повысится ли стабильность системы?
0
повысится. Но я категорически не рекомендую использовать механизм link.
Во-первых, он ненаправленный.
Во-вторых, очень легко упустить process_flag и тогда вы не получите сообщения о смерти при нормальном выходе процесса.
В-третьих, он не реентерабельный и очень легко разлинковаться с процессом.
Мониторы лишены всех этих проблем.
Ещё в приведенном здесь коде есть другие проблемы.
Необходимо либо журналировать неизвестные сообщения и call-ы, либо падать по ним. Это очень важно, без этого легкий рефакторинг может обернуться большой головной болью.
Во-первых, он ненаправленный.
Во-вторых, очень легко упустить process_flag и тогда вы не получите сообщения о смерти при нормальном выходе процесса.
В-третьих, он не реентерабельный и очень легко разлинковаться с процессом.
Мониторы лишены всех этих проблем.
Ещё в приведенном здесь коде есть другие проблемы.
Необходимо либо журналировать неизвестные сообщения и call-ы, либо падать по ним. Это очень важно, без этого легкий рефакторинг может обернуться большой головной болью.
+1
Макс, спасибо за отличный комментарий! Буду дальше учиться. Запуск через spawn_link я отметил как минус. Для связи не OTP с OTP процессами можно использовать supervisor_bridge или вообще лучше этого не делать?
Что посоветуешь для углубления знаний?
Что посоветуешь для углубления знаний?
0
Я советую не использовать вообще не-OTP процессы.
В erlyvideo есть пара мест, где были spawn-утые функции, но вроде осталась только одна в media_ticker-е.
Никаких плюсов от этого нет.
Мои рецепты простой — везде использовать gen_server (от gen_fsm много проблем), всегда запускать из под супервизора, падать на всех неотработанных handle_info и handle_call. Эти несложные рецепты сберегают кучу времени.
В erlyvideo есть пара мест, где были spawn-утые функции, но вроде осталась только одна в media_ticker-е.
Никаких плюсов от этого нет.
Мои рецепты простой — везде использовать gen_server (от gen_fsm много проблем), всегда запускать из под супервизора, падать на всех неотработанных handle_info и handle_call. Эти несложные рецепты сберегают кучу времени.
0
Максим, сделайте пожалуйста полезную статью. Я знаю, что вы большой спец в эрланге и ваш видео сервер вызывает только чувство уважения к Вам! напишите пожалуйста статью, например в простых словах расскажите о плтформе Erlang. Сейчас к ней очень большой интерес, но многие люди не понимаю принципа ее работы, а ваша статья им поможет им (и мне в том числе) квидеть все а ПРАВИЛЬНОМ свете.
Напишите?
Напишите?
0
Автар, предлагаю тебе квест. Написать модуль так, чтобы он умел всё что надо, при этом одновременно (в смысле или то или то) для голого tcp так и для tcp с ssl. Я дам подсказку. В ssl нет той самой недокументированной async которая спасала для tcp :) я в своё время хотел это сделать но получилась гадость.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Публикации
Изменить настройки темы
Неблокирующий TCP сервер без использования undocumented features