- Оригинальный адрес:As bad as anything else: Part 1
- Оригинальный автор:Fred T-H
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:steinliber
- Корректор:kasheemlew, SergeyChang
так же плохо, как и все остальное
Дзен Эрланга
Меня пригласили выступить на конференции ConnectDev'16, организованной Genetec, и это вольная расшифровка (или, скорее, более длинный пересказ) той части, которую я представил.
Я предполагаю, что большинство людей здесь никогда не использовали Erlang, возможно, слышали о нем или просто знают его название. В этом случае это введение будет охватывать только идеи высокого уровня в Erlang и использовать их, чтобы говорить о вещах, которые помогут вам в вашей работе или побочных проектах, даже если вы никогда не касались этого языка.
Если вы когда-либо знали Erlang раньше, вы, вероятно, слышали пословицу «пусть он рухнет». Когда я впервые столкнулся с этой фразой, я подумал, что это за чертовщина. Предполагается, что Erlang имеет хорошую поддержку параллелизма и отказоустойчивости, но мне сказали просто позволить ему рухнуть, что является полной противоположностью тому, что я хочу сделать с этой системой. Это утверждение невероятно, но дзен Эрланга идет рука об руку с ним.
В некотором смысле, использование фразы «просто дайте ему рухнуть» в Erlang так же забавно, как использование «просто дайте ему взорваться» в ракетостроении. «Просто дайте ему взорваться» — это, вероятно, последнее, чего вы хотите в ракетостроении. Катастрофа «Челленджера» — яркое напоминание. Если посмотреть на это по-другому, ракета и весь ее двигательный механизм работают с опасными и взрывоопасными горючими веществами (вот где опасность), но они используются контролируемым образом. Эта энергия используется для запуска космических путешествий или отправки полезных грузов на орбиту.
Суть здесь в контроле; вы можете попытаться думать о ракетостроении как о том, как правильно использовать взрыв — или, по крайней мере, содержащуюся в нем энергию — для выполнения того, что мы хотим. То же самое верно и для того, чтобы дать ему сбой с той же точки зрения: все дело в отказоустойчивости. Идея состоит не в том, чтобы повсюду иметь неконтролируемые ошибки, а в том, чтобы превратить сбои, исключения и сбои в инструменты, которые мы можем использовать.
Воспоминания и контролируемое горение - это реальные примеры пожаротушения. В Сани-Сент-Джон, Канада, где я родился, поля черники регулярно сжигают контролируемым образом, чтобы поддержать и увековечить их последующий рост. Чтобы предотвратить лесные пожары, общепринятой практикой является использование огня для удаления мертвых частей леса, чтобы можно было должным образом регулировать и контролировать лес. Основная цель здесь — удалить горючий материал таким образом, чтобы настоящий огонь не мог распространиться дальше.
Во всех этих случаях разрушительная сила огня на посевах или лесах используется для обеспечения здоровья посевов или для предотвращения более крупных неконтролируемых пожаров в лесных массивах.
Я думаю, что это и означает "просто дайте ему разбиться". Если мы сможем обрабатывать сбои, сбои и исключения очень хорошо контролируемым образом, это не страшные вещи, которых следует избегать, а мощные строительные блоки для создания больших и надежных систем.
Таким образом, вопрос заключается в том, чтобы найти способ убедиться, что аварии являются посредниками, а не разрушителями. Для этого Erlang использует процессы в качестве своей основной пешки. Процессы Erlang полностью независимы, и между процессами нет ничего общего. Ни один процесс не может получить доступ к памяти другого процесса или повлиять на его работу, изменяя данные, с которыми он работает. Это здорово, потому что это означает, что мы можем гарантировать, что умирание процесса только сохранит проблему внутри процесса, что приведет к очень надежной изоляции сбоев в вашей системе.
Процессы Erlang также очень легковесны, и это не проблема, если вы запускаете их тысячи. Идея здесь состоит в том, чтобы использовать столько процессов, сколько вам нужно для запуска, а не столько процессов, сколько вы можете. Обычное сравнение здесь состоит в том, чтобы сказать, что если у вас есть объектно-ориентированный язык, который может иметь только 32 объекта в любой среде выполнения, вы быстро обнаружите, что создание программ на этом языке чрезмерно ограничительно и очень смешно. Наличие большого количества небольших процессов обеспечивает большую степень детализации при разделении вещей, и в мире, где мы хотим использовать силу этих сбоев, это здорово!
Теперь может быть странно представить, как процессы работают в Erlang таким образом. Когда вы пишете программу на C, у вас есть большойmain()
Функции делают много вещей. Эта функция является точкой входа в вашу программу. В Эрланге такого нет. Ни один процесс не является основным процессом, назначенным этой программой. Каждый процесс выполняет функцию, и эта функция играет роль в соответствующем отдельном процессе.main()
Роль функции.
Теперь у нас есть колония пчел, но если они никак не могут общаться, может быть сложно управлять ими и укреплять улей. Пчелы общаются с помощью танца, а Erlang обрабатывает сообщения.
В параллельной среде обмен сообщениями между процессами является наиболее интуитивно понятной формой связи. Это самая старая форма связи, над которой мы когда-либо работали, начиная с тех дней, когда мы начали отправлять письма по назначению верхом с помощью почтальона, и до наполеоновской сигнальной башни, показанной в этом слайд-шоу. В этом случае вам просто нужно взять группу людей в башню, передать им сообщение, и они будут махать флагом, чтобы передать сообщение на расстояние быстрее, чем на лошадях, но это очень утомительно. В конце концов их заменил телеграф, а затем телефон и радио, и теперь у нас есть все эти замечательные технологии для доставки сообщений, и они могут распространяться очень далеко и очень быстро.
Одним из ключей ко всем этим методам доставки сообщений, особенно в прошлом, является то, что они являются асинхронными, а передаваемые сообщения реплицируются. Никто не стоит днями на крыльце, ожидая возвращения посыльного, и никто (по крайней мере, я так думаю) не сидит на вышке сотовой связи, ожидая ответа. Вы просто отправите сообщение и вернетесь к своей основной работе, и в конце концов кто-то скажет вам, что есть ответ.
Это имеет смысл, потому что, если другая сторона не отвечает, вы ничего не делаете, а просто сидите на крыльце, пока не умрете. И наоборот, если вы умрете, получатели на другой стороне канала обмена сообщениями не смогут чудесным образом сразу же получить или изменить ваше сообщение. Данные __должны__ быть скопированы перед отправкой. Эти два принципа гарантируют, что сбои в процессе связи не приведут к повреждению или невосстановимому состоянию. Erlang реализует оба этих принципа.
Чтобы иметь возможность читать сообщения, у каждого процесса есть почтовый ящик. Любой процесс может записывать сообщения в почтовый ящик процесса, но только процесс, которому принадлежит почтовый ящик, может его просматривать. Эти сообщения читаются по умолчанию в том порядке, в котором они приходят, но также можно использовать такие функции, как сопоставление с шаблоном [мы обсуждали это в предыдущем выступлении], чтобы временно сосредоточить процесс только на одном, тем самым управляя потоком сообщений. разного приоритета порядок выполнения.
Некоторые из вас заметят некоторые из вещей, которые я только что упомянул; я повторил, что изоляция и независимость хороши, так как компоненты системы могут умирать и давать сбой, не затрагивая другие компоненты, в то же время я также упомянул связь между несколькими процессами или агентами.
Всякий раз, когда два процесса начинают взаимодействовать, мы создаем неявную зависимость между ними. В системе будет какое-то неявное состояние, которое связывает их вместе. Если процесс A отправляет сообщение процессу B, но процесс B умер, не ответив A, то все, что может сделать процесс A, — это либо ждать вечно, либо через некоторое время отказаться от связи с процессом B. Последнее является эффективной стратегией, но это также и очень расплывчатая стратегия: она не знает, умер ли удаленный процесс или просто обрабатывается долго, а сообщение от удаленного процесса отправляется вам после отвязки». почтовый ящик.
Вместо этого Erlang предоставляет нам два механизма для решения этой ситуации: мониторы и ссылки.
Все, что делает монитор, — это действует как наблюдатель, альпинист. Вы решаете следить за процессом, и если этот процесс по какой-либо причине умирает, вы можете получать новости о нем в свой почтовый ящик. Затем вы можете реагировать на это и принимать решения на основе вновь обнаруженной информации. Другие процессы никогда не узнают, что вы с ним сделали. Мониторы могут быть отличными инструментами, если вы являетесь наблюдателем или следите за состоянием одноранговых процессов.
Ссылка является двунаправленной, и установление связи свяжет судьбу двух процессов, к которым она подключена. Когда один из них умирает, любые связанные с ним процессы получают сигнал выхода, и этот сигнал выхода убивает эти процессы.
Теперь это становится действительно интересным, потому что мы используем мониторы для быстрого обнаружения сбоев, и я также могу использовать цепочку как архитектурную конструкцию, чтобы связать несколько процессов вместе как общую единицу сбоя. Всякий раз, когда мои независимо созданные модули начинают иметь зависимости друг от друга, я могу начать записывать эти зависимости в свой код. Это полезно, потому что предотвращает случайный сбой моей системы в нестабильном локальном состоянии. Цепочка — это инструмент, который позволяет разработчикам гарантировать, что в случае сбоя одного из этих элементов он полностью выйдет из строя и оставит пустую доску, не затрагивая компоненты, которые не участвуют в этом запуске.
Для этого слайд-шоу я выбрал фотографию альпинистов, связанных веревками. Вот если бы у альпинистов была только эта связь, они были бы в плохой ситуации. Каждый раз, когда альпинист в вашей группе поскользнется, остальная часть группы поскользнется и немедленно погибнет. Это не лучший способ делать вещи.
Вместо этого Erlang позволяет указать, что определенные процессы являются особыми и что эти процессы будут использоватьtrap_exit
варианты в качестве маркеров. Затем они могут принимать сигналы выхода, отправленные по ссылке, и превращать их в сообщения. Это позволяет им восстановиться после ошибок и, возможно, запустить новый процесс, чтобы выполнить работу ранее мертвого процесса. В отличие от альпинистов, этот конкретный процесс не может предотвратить сбой однорангового процесса; одноранговый процесс несет ответственность за то, чтобы не допустить сбоя, например, с помощьюtry ... catch
выражение. Процесс, получивший сигнал выхода, по-прежнему не может войти в память другого процесса и сохранить эту память, но он может избежать смерти из-за этого.
Это становится ключевой особенностью внедрения супервизоров. Если вы никогда не слышали о них, мы скоро до них доберемся.
Прежде чем мы перейдем к части Overseer, нам все еще нужно немного специй, чтобы успешно испечь систему, которая использует сбои в своих интересах. Один из них связан с планированием процессов. Я хотел бы упомянуть в этой связи реальный пример — программу посадки на Луну корабля «Аполлон-11».
Аполлон-11 был миссией по высадке на Луну в 1969 году. В этом слайд-шоу мы видим лунный модуль Базза Олдрина и Нила Армстронга, это фото, я думаю, было сделано Майклом Коллинзом, который оставался в командном модуле для этой миссии.
На пути к Луне лунный модуль будет управляться Apollo PGNCS (Основная система управления, навигации и управления). В системе наведения выполняется несколько задач, и количество их циклов тщательно продумано. НАСА также указало, что все задачи выполняются только на 85% мощности процессора, оставляя 15% свободного места.
Теперь, на случай, если программу придется прекратить, у астронавтов должен быть надежный запасной план. Поэтому они также запустили радар рандеву с процессором на всякий случай, если он пригодится, что израсходует значительную часть оставшейся мощности процессора. Когда Базз Олдрин ввел команду, появилось множество сообщений об ошибках о переполнении и исчерпании емкости. Если система управления выйдет из-под контроля, она не будет работать должным образом и убьет двух астронавтов.
Во многом это связано с известной аппаратной ошибкой в радаре, из-за которой он работал с несовпадением с командным компьютером, что приводило к краже большего количества циклов, чем должно было быть. Конечно, люди в НАСА тоже не идиоты, и в этой критически важной миссии они повторно использовали компоненты, которые, как они знали, раньше редко выходили из строя, вместо того, чтобы разрабатывать новую технологию. Но что еще более важно, они разработали приоритетное планирование.
Это означает, что даже если процессор перегружен из-за этого радара или команд ввода, если их приоритет выполнения низок по сравнению с опасными для жизни вещами, тогда эти задачи будут уничтожены, что даст процессору время для выполнения. нуждается в этом. Это было в 1969 году; сегодня все еще существует множество языков или фреймворков, которые дают вам __просто__ совместное планирование и ничего больше.
Erlang не является языком для создания жизненно важных систем — он подчиняется только мягким ограничениям реального времени, а не ограничениям реального времени, поэтому не рекомендуется использовать его в этих сценариях. Но Erlang предоставляет вам упреждающее планирование и соответствующие приоритеты процессов. Это означает, что вам как разработчику или проектировщику системы не нужно заботиться о том, чтобы все тщательно измеряли загрузку процессора всеми своими компонентами (включая используемые ими библиотеки), чтобы избежать замедления работы всей системы. У них нет этой способности. И если вам нужно, чтобы какая-то важная задача всегда выполнялась, когда она должна выполняться, вы тоже можете это сделать.
Это не кажется большим или общим требованием, и люди все еще могут разрабатывать действительно успешные проекты с совместными параллельными задачами, но это действительно ценно, потому что изолирует вас от ошибок других и вас самих. Он также предоставляет механизмы реализации для таких вещей, как автоматическая балансировка нагрузки, наказание и вознаграждение хороших или плохих процессов или предоставление более высокого приоритета процессам, которые выполняют много работы. Эти вещи в конечном итоге помогут вашей системе лучше адаптироваться к производственной нагрузке и обрабатывать непредвиденные события.
Последний компонент, который я хочу обсудить для достижения изящной отказоустойчивости, — это осведомленность о сети. В любой долгоживущей системе, которую мы разрабатываем, наличие нескольких компьютеров, быстро запускающих систему, является предварительным условием. Вы не хотите сидеть рядом с золотой машиной, запертой внутри титановой дверью, и не можете терпеть любые нарушения, вызванные любыми средствами, влияющими на ваших пользователей.
Итак, в конце концов вам нужно два компьютера, чтобы один мог продолжать работать, пока другой сломан, и если вы хотите выполнить развертывание, пока сломанный компьютер все еще является частью вашей системы, вам, вероятно, понадобится третий.
Самолет на этом слайд-шоу — F-82 Twin Mustang, самолет, разработанный во время Второй мировой войны для сопровождения бомбардировщиков за пределы досягаемости, до которой не могли дотянуться другие истребители. Он имеет две кабины, так что со временем один пилот может заменить другого, когда другой устанет; в некоторых случаях они также могут взаимодействовать друг с другом, пока один из них летит, другой может использовать радар в роли перехватчика. человек. Современные самолеты все еще делают что-то подобное; у них есть бесчисленное множество альтернатив, и часто экипаж спит в пути во время полета, чтобы всегда был кто-то бдительный и готовый управлять самолетом.
Когда этот термин применяется к языкам программирования или средам разработки, большинство из них предназначены для полного игнорирования распространения, хотя известно, что если вы пишете серверный стек, вам нужно более одного сервера. Однако, если вы собираетесь использовать файлы, они становятся языками, у которых есть стандартная библиотека, которая сделает это за вас. Большинство языков делают еще один шаг вперед, предоставляя вам библиотеку сокетов или HTTP-клиент.
Erlang знает об этом факте распространения и предоставляет вам документированную и прозрачную реализацию. Это позволяет настроить желаемую логику аварийного переключения или взять на себя аварийное приложение, чтобы обеспечить большую отказоустойчивость, и даже позволяет другим языкам создавать полигональные системы, притворяясь, что они являются узлами Erlang.
Все это основные ингредиенты рецепта Zen of Erlang. Весь смысл этого языка в том, чтобы получить сбои и сбои и сделать их настолько управляемыми, чтобы их можно было использовать в качестве инструментов. Как только это сломается, это обретет смысл, большинство представленных здесь принципов можно повторно использовать в качестве вдохновения в системах, отличных от Erlang.
Как совместить их — следующая задача.
Дерево супервизии описывает, как реализовать архитектуру вашей программы на Erlang. Они основаны на простой идее наличия супервайзера, единственной задачей которого является запуск процессов, наблюдение за их выполнением и перезапуск в случае сбоя. Между прочим, супервизор является одним из основных компонентов «OTP», широко используемой среды разработки под названием «Erlang/OTP».
Целью этого является создание иерархии, в которой все важные вещи, которые должны стабильно работать, приближаются к корню дерева, а все летучие или движущиеся части накапливаются на листьях. На самом деле, именно так выглядит большинство деревьев в реальной жизни: листья не закреплены, на дереве будет много листьев, и все они опадут осенью, пока дерево еще живо.
Это означает, что когда вы создаете программы на Erlang, любые процессы, которые вы считаете уязвимыми и которые допускают их сбой, должны быть ниже по иерархии, а те, которые стабильны и надежны, должны быть перемещены вверх по иерархии.
Регуляторы делают это, используя цепочку и захват выходов. Они работают, запуская свои дочерние процессы по одному, сверху вниз, слева направо. Только когда дочерний процесс полностью запущен, он возвращается на предыдущий уровень, чтобы начать создание следующего дочернего процесса. Каждый дочерний процесс автоматически связывается.
Всякий раз, когда дочерний процесс умирает, есть три стратегии на выбор. Первая стратегия на этом слайде — «один к одному», реализованная путем замены мертвых дочерних процессов. Это стратегия, используемая, когда все дочерние процессы супервизора независимы друг от друга.
Вторая стратегия — «один — это все». Эта стратегия используется для взаимозависимостей между дочерними процессами. Когда любой из них умирает, супервизор убивает все остальные дочерние процессы, прежде чем перезапустить их все. Вы можете использовать эту стратегию, когда потеря определенного дочернего процесса оставляет другие процессы в неопределенном состоянии. Давайте представим три процесса, имеющих диалог, который заканчивается голосованием. Если один из процессов умирает во время голосования, то, возможно, мы не написали никакого кода для обработки этого. Замена мертвого процесса на новый приводит к появлению на столе нового компаньона, и все процессы в нем совершенно не представляют, что делать дальше.
Это несогласованное состояние может быть опасным, если мы на самом деле не определим, что делать, если во время голосования происходит сбой процесса. Вместо этого может быть безопаснее убить все процессы и начать заново с известного стабильного состояния. Делая это, мы ограничиваем масштаб ошибки: лучше выйти из строя раньше и вовремя, когда возникает ошибка, чем уничтожать данные медленно и в течение длительного периода времени.
Эта последняя стратегия обычно используется, когда между процессами существуют зависимости, основанные на порядке их запуска. Он называется «тот, который остался», и когда дочерний процесс умирает, процессы, запущенные после его уничтожения. Затем процесс перезапускается, как и ожидалось.
Каждый супервизор дополнительно имеет настраиваемые уровни контроля и допуска. Некоторые регуляторы могут допускать только один сбой в день перед отключением, в то время как другие могут допускать 150 сбоев в секунду.
Обычный комментарий, который люди придумывают после того, как я упомяну регуляторы, звучит так: «Но если мой файл конфигурации неверен, перезагрузка ничего не исправит!».
Это абсолютно правильно. Причина, по которой работают перезагрузки, связана с характером ошибок, возникающих в производственных системах. Чтобы обсудить это, я должен упомянуть термины «Борбаг» и «Гейзенбаг», введенные Джимом Греем в 1985 году (советую вам прочитать как можно больше статей Джима Грея, все они великолепны!).
По сути, боржук — это стабильный, наблюдаемый и воспроизводимый жук. Они, как правило, являются причиной проблемы, которая может быть легко обнаружена разработчиком. Вместо этого Heisenbug имеет ненадежное поведение, он не проявляется в детерминированных условиях, и они могут быть скрыты, если простое поведение просто используется для наблюдения за этими проблемами. Например, при использовании отладчика в системе, где каждая операция выполняется последовательно, ошибок параллелизма обнаружить не удается.
Heisenbugs — это неприятные ошибки, которые случаются только один раз на тысячу, миллион, миллиард или триллион ошибок. Когда вы видите, как кто-то печатает страницы и страницы кода и вставляет в них кучу разметки, вы знаете, что он уже некоторое время имеет дело с этим типом ошибок.
Теперь, когда эти термины определены, давайте посмотрим, как часто они должны появляться.
Здесь я перечисляю bohrbugs как повторяющийся тип ошибки и heisenbugs как временный тип ошибки.
Если у вас есть bohrbugs в основных функциях вашей системы, их должно быть легко найти до того, как система будет запущена в производство. Благодаря повторяемости и тому факту, что такого рода ошибки обычно находятся на критическом пути выполнения программы, вы должны рано или поздно столкнуться с ними и исправить их, прежде чем перейти к следующему этапу.
Ошибки, которые случаются с незначительными, редко используемыми функциями, больше похожи на напоминания и пропущенные вещи. Все признают, что исправление всех ошибок в программном обеспечении — это тяжелая битва с уменьшающейся отдачей; по мере того, как вы продолжаете писать код, может потребоваться все больше и больше времени, чтобы избавиться от мелких ошибок в нем. Как правило, этим второстепенным функциям уделяется меньше внимания не только потому, что их будет использовать меньше клиентов, но и потому, что их влияние на удовлетворенность не так важно. Или, может быть, их просто планируется исправить позже, и отодвигание графика в конечном итоге делает разработчикам менее важным заниматься этим.
В любом случае их довольно легко найти, у нас просто нет на это времени или ресурсов.
Heisenbugs почти невозможно обнаружить во время разработки. Отличные методы, такие как формальное доказательство, проверка моделей, исчерпывающее тестирование или тестирование на основе свойств, могут увеличить вероятность обнаружения некоторых или всех из них (в зависимости от используемого метода), но, честно говоря, если решаемая задача не является очень критической, в противном случае мало из нас используют эти методы. Проблема, которая возникает раз в миллиард, требует много тестов и проверок, чтобы найти ее, и если вы видели ошибку, скорее всего, вам снова не повезло с ней.
Для получения дополнительной информации см. вторую часть этой статьи:Дзен Эрланга: Часть II.
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из ИнтернетаНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллекти другие поля, если вы хотите видеть больше качественных переводов, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.