Страшная история из Королевства Эликсира и OTP
Не буду сейчас рассказывать вам мораль этой истории.
Вместо этого я надеюсь, что эта история говорит сама за себя.
Мораль этой истории такова: есть смысл изучать шаблоны проектирования одноразовых паролей в начале карьеры Эликсира и рисковать их игнорированием.
Королевство
Царство эликсира и отп - светлое и счастливое место. Например, стабильность. В просторечии этот мир называют временем отказоустойчивости. Эта стабильность была одной из основных целей предшественника и соседа королевства Эрланга. Он был выкован непосредственно в первых принципах королевства и во многих его последующих основополагающих решениях.
Царство состоит из множества изолированных частей, называемых отростками. Эти процессы изолированы в самом своем ядре, известном как их куча и стек. Если один поддается болезни или неудаче, это не распространяется на другого.
Хотя они изолированы, эти процессы часто взаимодействуют друг с другом. У каждого из них есть свой почтовый ящик, они запрашивают и отправляют товары в виде данных через красивую систему передачи сообщений.
Чтобы любая система была чем-то большим, чем просто сумма ее частей, и, конечно же, чтобы быть королевством, требуется особый дар — умение эффективно координировать свои действия. Для этого в королевстве есть то, что с любовью называют Планировщиком. Планировщик гарантирует, что все эти процессы работают максимально эффективно и максимально эффективно используют свои драгоценные природные ресурсы, ядра.
Однако это королевство не сказка. С течением времени отдельные Процессы сильно пострадали и потерпели неудачу, войдя в период, известный как Крах.
Чтобы поддерживать стабильность в королевстве, королевство создало обширную библиотеку взаимодействия систем и знаний, известную как OTP. От OTP пришло много даров величия. Она научила процессы, как зрело структурировать себя, и предоставила множество стандартов для хорошего поведения. OTP научил эти процессы тому, что иногда они должны вести себя как клиенты по отношению к другим, а иногда они должны становиться серверами. Эти процессы стали известны как GenServer, способные обслуживать обработку вызовов, кастинг и информацию.
Но было нечто, возможно, даже большее, чем усовершенствование этих процессов, данное королевству из OTP. Он стал символом отказоустойчивости как физического проявления.
Это дерево наблюдения.
Дерево Наблюдения — великое существо. Он расширяет свою обширную сеть корней, известных как Супервайзеры, для наблюдения за многими из наиболее важных процессов на земле. И если один из этих процессов поддастся Краху, Надзиратель может подарить этому процессу дар возрождения, чтобы он восстал из пепла подобно великому Фениксу. Таким образом, все другие процессы в царстве, которые взаимодействуют и зависят от этих процессов, могут развиваться мирно.
Тем не менее, несмотря на эти принципы и дары, королевство зависит от прихоти его дизайнеров, программистов OTP, архитекторов и людей, которые заставляют королевство работать.
Осень
Именно здесь, в этом королевстве, давным-давно был изобретен определенный Процесс. Этот Процесс был невинным в своем облике, но чудовищным по своему потенциалу, который имел в себе силу поставить все королевство на колени.
К несчастью для мира, этот процесс был во многих сообщениях в блогах. Его популярность привела к росту в нескольких производственных системах.
В те мрачные дни было обычной практикой использовать передачу сообщений для процесса, чтобы отправить себе напоминание. Это напоминание обычно было задачей, которую оно должно было выполнить в будущем. Чтобы узнать об этом шаблоне, мы должны придумать новый проект эликсира с деревом наблюдения:
$ mix new poison_pill --sup
Как и большинство популярных фреймворков elixir, наш проект находится под бдительным присмотром великолепного дерева наблюдения. Вот, внутри модуля poison_pill/lib/poison_pill/application.ex мы можем увидеть диспетчер приложения:
Этот процесс может сделать многое. В этой конкретной истории простым работником было хранить локальный кеш данных, собранных через API. Этот процесс был нелегким. Действительно, это был OTP GenServer. Когда он родился, этот GenServer отправил бы себе сообщение для запроса API и запланировал запрос на будущее. Начало этого GenServer было скромным:
$ cd poison_pill $ touch poison_pill/lib/poison_pill/worker.ex
Чтобы этот GenServer ожил, необходимо раскомментировать, сохранить и запустить рабочий процесс в файле application.ex:
$ mix run Compiling 1 file (.ex) HELLO WORLD
Эта история только начинает становиться интересной — мы должны отправить себе сообщение для синхронизации с API. Мы позволим себе отправить сообщение сразу, а затем будем обновлять наш кеш каждые 24 часа:
Создание нового процесса амбициозно и рискованно, мы должны сделать еще одну проверку, чтобы убедиться, что компилятор доволен, и что наш процесс ведет себя так, как ожидалось:
$ mix run Compiling 1 file (.ex) HANDLED ALL THAT INFO
На благо королевства и во славу нашего процесса пришло время получить все данные. Мы добавим http-клиент в наш файл mix.exs (примечание PS — яд в httptoison не имеет отношения к названию этого блога, а библиотека — просто супер). HTTP-клиент высокого уровня:
И скачать/установить пакет с:
$ mix deps.get Resolving Hex dependencies... Dependency resolution completed: ...blah ...blah
Теперь давайте включим наш сервер, выполнив синхронизацию:
Мы можем убедиться, что наше нерушимое, отказоустойчивое, нерушимое приложение OTP будет работать, запустив:
$ mix run --no-halt

ПОДОЖДИ СЕКУНДУ.
Почему наше приложение закрылось? Так что, если мы поместим опечатку в наш URL. Это не такая уж и плохая ошибка во время выполнения, разве эта система не должна «допустить сбоя»?
Как наш крошечный, невинный маленький процесс разрушил великое Древо Наблюдения королевства? Следует ли просто перезапустить процесс, чтобы повторить попытку?
Что ж, мы можем нанять волшебного детектива королевства (Программиста) для расследования.
Дочерней спецификацией по умолчанию, которую мы использовали в нашем файле application.ex, является стратегия наблюдения :one_for_one. Это означает, что каждый раз, когда рабочий выходит из строя, наш супервизор просто перезапускает этот отдельный процесс, не беспокоясь о корректировке каких-либо других процессов, на которые может полагаться королевство. Это кажется правильным, у нас даже нет других воркеров в нашем приложении.
Как эта злая ядовитая пилюля использует свою черную магию?
Давайте углубимся в огромную библиотеку знаний, которую мы накопили, известную как OTP.
Оказывается, самая простая реализация полагается на несколько значений по умолчанию. Например, если мы проверим elixir docs on Supervisor, они выберут атом :restart в качестве опции. Мы не передали его, но похоже, что по умолчанию используется значение :permanent. Это также похоже на то, что мы хотим: это сообщит великому диспетчеру приложений, чтобы он продолжал попытки воскресить нашего великого Феникса процесса.
Какая темная тайна продолжает скрываться в этих глубинах?
Ах Ха! Похоже, что наш диспетчер приложений при запуске применяет еще больше значений по умолчанию. Согласно этим документам, он принимает аргументы :max_restart и :max_seconds. По умолчанию наш супервизор будет пытаться вернуть наш процесс максимум 3 раза каждые 5 секунд.
Действительно, если мы проследим наши журналы, мы увидим, что ошибка времени выполнения :econnrefused произошла в четвертый раз прямо перед тем, как все приложение рухнуло.
Виновником, похоже, является наша невинная функция schedule_work(:now). Он пытается синхронизироваться с API сразу при загрузке, сразу же падает, промывается и повторяется, пока не потянет за собой все наше приложение.
Это еще более коварно, если учесть, что независимо от того, насколько далеко вниз по дереву надзора находится этот процесс, многоуровневый в недрах наблюдателей, они могут каскадно потерпеть неудачу, пытаясь перезагрузить ядовитую таблетку.
Это подводит нас к морали этой мрачной сказки.
«Пусть он рухнет» может быть забавно сказать или броситься на языки, которые требуют мучительного количества явной обработки ошибок. Но в конечном итоге Эликсир предлагает двигаться быстро и НЕ ломать вещи.
В связи с этим мы должны быть очень осторожны при проектировании наших прекрасных OTP-процессов, чтобы не допуститьбыстрого сбоя, особенно когда процесс инициализируется или находится на ранней стадии. его жизненный цикл.
В частности, если мы вызываем методы с символом взрыва (!) как вызывающая сторона, мы должны быть очень осторожны, чтобы понимать, что они могут возвращать исключительное исключение, и заключать их в блок try/research.
Если мы используем поток управления сопоставлением шаблонов для возвращаемых значений, таких как {:ok, value} / {:error, value} возвращать кортежи, мы должны быть очень осторожны, так как вызывающие функции сопоставляются с образцом для каждого возможного возвращаемого типа, чтобы избежать исключения совпадения.
Если мы не можем избежать быстрого выполнения работы в жизненном цикле процесса, мы должны также подумать о поведении нашего процесса и подумать, нужно ли нам более тщательно выбирать поддерживающее его поведение супервизора.
Разработчики этой ядовитой таблетки могут сделать ее неэффективной, если позаботятся о HTTPoison.get!. Действительно, мы хотим предпринимать разные действия в зависимости от возвращаемого значения вызова. Это означает, что мы вводим поток управления.
Блоки Try / Rescue обычно не предназначены для использования в качестве управления потоком в Эликсире, скорее они были созданы для защиты Процесса от действительно исключительных катастроф. Вместо этого мы можем использовать не-банговую версию HTTPoison.get и обернуть ее оператором case:
С восстановленным дизайном нашего королевства мы назовем старый процесс ядовитой пилюли анти-шаблоном, чтобы другие знали, что его нельзя повторять.
Но мир темен и страшен. И как бы мы ни были осторожны, королевство может жестоко рухнуть.
Помня об этом, готовясь противостоять тьме, королевство отправилось в великое путешествие к Распределению.
Продолжение следует…