Как выбрать в Python подходящий конкурентный API

Меня зовут Алексей Некрасов (@znbiz), я лидер направления Python в МТС, программный директор направления Python и спикер профессии «Python-разработчик» в Skillbox. Сегодня предлагаю обсудить best practices подбора оптимального конкурентного API на Python с учётом поставленной задачи и аппаратных возможностей целевой платформы. Под катом — туториал на эту непростую тему, который я для вас перевел.

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

Проблема с API Python для реализации конкурентности 

В стандартной библиотеке Python (stdlib) предлагается три способа для конкурентного выполнения задач в программе — это модуль multiprocessing для конкурентности на уровне процессов, модуль threading для конкурентности на уровне потоков, а также модуль asyncio для конкурентности на основе корутин. Выбор такой широкий, что можно запутаться.

Но на самом деле все еще хуже, чем кажется:

  • Например, вы выбрали модуль — и что делать дальше: применить пул воркеров (pool of workers, далее в статье будет просто пул воркеров) либо запрограммировать конкурентную задачу самостоятельно?

  • Если вы выберете пул воркеров, то далее придется использовать Pools API или Executors API.

Даже бывалых Python-разработчиков может обескуражить такое разнообразие. 

Какие API для обеспечения конкурентности в Python следует использовать в проекте? 

Нужны эмпирические правила, которые помогли бы при выборе наиболее подходящих API для конкурентности. Прежде чем рассмотреть удобную процедуру, помогающую при таком выборе, давайте подробнее рассмотрим задачу и тщательно ее структурируем.

Какой конкурентный API для Python следует использовать? Рассмотрим задачу и тщательно ее структурируем

Итак, вы пишете на Python программу, в которой нужно реализовать конкурентность. Возникает проблема: какой API для этого выбрать? 

Не вы один с ней сталкиваетесь. Вероятно, это один из самых распространенных вопросов по поводу Python.

В Python существуют три основных API для конкуренции, вот они:

  • Корутиновый, предоставляемый в модуле asyncio.

  • Поточный, предоставляемый в модуле threading.

  • Процессный, предоставляемый в модуле multiprocessing.

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

Например:

  • Если вы решите, что вам требуется потоковая конкурентность, то воспользоваться ли пулом потоков или каким-то образом опираться на класс Thread?

  • Если вы решите, что вам требуется параллелизм на основе процессов, то следует ли воспользоваться пулом процессов или найти способ воспользоваться классом Process?

Далее, если вы решите реализовать конкурентность при помощи повторно используемых рабочих потоков, то и здесь у вас будет на выбор несколько вариантов.

Например:

  • Если вы решите, что вам нужен пул потоков, то каким классом воспользоваться — ThreadPool или ThreadPoolExecutor?

  • Если вы решите, что вам нужен пул процессов, то каким классом воспользоваться — Pool или ProcessPoolExecutor?

На следующем рисунке обобщено это дерево решений.

Как же во всем этом сориентироваться?

Процесс выбора API для реализации конкурентности на Python 

Один из возможных подходов — пожалуй, самый распространенный — выбирать API ситуативно. При разработке многие решения принимаются именно так, и обычно программа от этого работает ничуть не хуже. Но случается и иначе.

Рекомендуем подбирать конкурентный API Python для вашего проекта в три этапа.

Вот эти этапы:

  1. Шаг 1: CPU-Bound (опираемся на ЦП) или I/O-Bound (на операции ввода/вывода) (многопроцессность или многопоточность).

    1. Шаг 1.1 Выбор между AsyncIO и Threading.

  2. Шаг 2: Организация задач: много специальных или одна большая сложная? 

  3. Шаг 3: Пулы или исполнители?

Кроме того, вот вам удобная схема, на которой обобщены эти решения:

Далее давайте подробнее рассмотрим каждый из этапов и некоторые нюансы.

Шаг 1: CPU-Bound или I/O-Bound?

Первый шаг при выборе API для конкурентности на Python — обдумать ограничивающие факторы той задачи (тех задач), что вы собираетесь решать.

Будут ли эти задачи завязаны в основном на использование ЦП или на ввод/вывод?

Если с этим аспектом разобраться правильно, то все последующие ваши решения будут уже менее важны.

Рассмотрим эти варианты по очереди.

CPU-Bound задачи

CPU-Bound (вычислительными) называются такие задачи, которые требуют выполнения вычислений, но не связаны с вводом/выводом.

В этих операциях участвуют только данные, находящиеся в основной памяти, и над этими данными (или с их применением) выполняются вычисления.

В принципе, единственным ограничивающим фактором этих операций является скорость процессора. Вот почему их называют CPU-Bound.

Примеры таких задач:

  • Вычисление точек во фрактале.

  • Оценка числа π.

  • Разложение на простые множители.

  • Синтаксический разбор HTML, JSON и т. д., а также документов.

  • Обработка текста.

  • Выполнение симуляций.

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

Итак, получив представление о CPU-Bound задачах, давайте поговорим о задачах, предполагающих ввод/вывод. 

I/O-Bound задачи

Задачи, ориентированные на ввод/вывод, предполагают считывание данных или запись их на устройство, в файл или в сокет.

Соответственно, это операции над входящими и исходящими данными, и скорость этих операций ограничена возможностями устройства, скоростью чтения/записи с жесткого диска или скоростью сетевого соединения.

Современные ЦП реально быстрые. Процессор с частотой 4 ГГц способен выполнять 4 миллиарда команд в секунду, а на вашей машине процессор наверняка не один.

Выполнение ввода/вывода — очень медленная операция по сравнению со скоростью процессора.

При взаимодействии с устройствами, сокетными соединениями, при считывании и записи файлов требуется вызывать инструкции у вас в операционной системе (и в ядре) и при этом дожидаться, пока операция завершится. Если именно этой операцией в основном и занят ваш процессор — например, если она выполняется в главном потоке программы на Python, — то процессору придется ждать много миллисекунд, фактически простаивать. Поэтому операции ввода/вывода называют блокирующими, так как они ожидают действия либо от пользователей, либо от других компонент системы.

Так теряется время, за которое потенциально можно было бы выполнить миллиарды операций.

Вот примеры задач, ориентированных на ввод/вывод:

  • Считывание файла с жесткого диска или запись его на диск.

  • Считывание или запись в стандартный ввод, стандартный вывод или стандартный канал ошибки (stdin, stdout, stderr).

  • Печать файла.

  • Загрузка или выгрузка файла.

  • Запрос к серверу.

  • Запрос к базе данных.

  • Съемка фото или запись видео и многое другое.

Теперь познакомившись как с CPU-Bound задачами, так и с I/O-Bound задачами, давайте рассмотрим, какие API Python для реализации конкурентности следует использовать при их решении.

Выбираем API для реализации конкурентности в Python 

Напомню, что модуль multiprocessing предоставляет работу с конкурентностью на уровне процессов, а модуль threading обеспечивает конкурентность на уровне потоков в рамках одного процесса.

В принципе, следует использовать конкурентность на уровне процессов, если в решаемой задаче активно задействуется процессор, и конкурентность на уровне потоков, если ожидается много операций, связанных с вводом/выводом.  

Модуль multiprocessing подходит для решения задач, сводящихся к вычислению или подсчету неких сущностей, причем когда относительно невелик объем данных, разделяемых между задачами. Многопроцессная обработка не подходит для решения задач, при которых одни процессы активно обмениваются информацией с другими, так как в таком случае накапливаются большие вычислительные издержки, а данные, разделяемые между процессами, необходимо сериализовать.

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

Модуль threading лучше подходит для решения задач, которые сводятся к считыванию с устройства ввода/вывода или записи на такое устройства, а вычислений при решении такой задачи относительно немного. Если при решении задачи требуется сильно загружать вычислениями процессор, то многопоточная обработка здесь подойдет плохо. В Python используется глобальная блокировка интерпретатора (GIL) — механизм, не допускающий, чтобы в любой момент выполнялось более одного потока в Python. Как правило, GIL снимается только при выполнении блокирующих операций, например ввода/вывода, либо при работе со специфическими сторонними библиотеками, написанными на C, например с NumPy.   

Можно выполнять десятки, сотни или тысячи поточно-ориентированных задач, чтобы максимально задействовать возможности аппаратного обеспечения, поскольку при вводе/выводе большая часть времени тратится на ожидание.

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

Подробнее о выборе между потоками и процессами можно почитать в следующих статьях:

Далее давайте поговорим об AsyncIO и обсудим, в каких случаях уместен такой подход.

Шаг 1.1 Выбор между Threading и AsyncIO

Если ваши задачи ориентированы в основном на ввод/вывод, то у вас возникает еще одна развилка, на которой требуется принимать решение.

Необходимо выбрать между модулями threading и asyncio.

Напомним, что первый обеспечивает конкурентность на уровне потоков, а второй — конкурентность на уровне корутин в пределах одного потока.

В принципе, следует отдавать предпочтение конкурентности на основе корутин, если у вас много сокет-соединений (или если вы предпочитаете асинхронное программирование), а в других случаях выбирать конкурентность на основе потоков.

  • Много сокет соединений: пользуйтесь модулем asyncio для организации конкурентности на уровне корутин.

  • В иных случаях: пользуйтесь модулем threading для организации конкурентности на основе потоков.

Модуль asyncio делает акцент на конкурентном неблокирующем вводе/выводе для сокет-соединений. Например, если ваши задачи ввода/вывода предполагают работу с файлами, то уже по этой причине вариант с asyncio будет неподходящим.

В пользу корутин говорит то, что они легковеснее потоков. Следовательно, единственный поток может нести гораздо больше корутин, чем уместится управляемых потоков в одном процессе. Например, asyncio позволяет выполнять тысячи, десятки тысяч и даже больше корутин для ввода/вывода на основе сокетов. Для сравнения: потоковый API в таком случае может предоставить сотни или в лучшем случае несколько тысяч потоков.

Еще одно соображение — возможно, вы захотите или будете вынуждены применить при разработке программы парадигму асинхронного программирования, например async/await. Следовательно, это требование возобладает над всеми иными требованиями, которые выдвигаются на уровне задач.

Аналогично, вы можете испытывать неприятие к асинхронному программированию, и этот фактор также окажется весомее любых требований, предъявляемых на уровне задач.

Подробнее о том, чем различаются асинхронный подход и конкурентный на основе потоков, рассказано в следующей статье:

Шаг 2: Множество специальных задач или одна глобальная сложная задача?

На втором шаге нужно рассмотреть, требуется ли вам выполнять независимые задачи, обусловленные контекстом, либо перед вами стоит большая сложная задача.

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

Этот момент можно обдумать и в другом ключе. С одной стороны, перед вами может стоять всего одна сложная задача (или ограниченное количество таких задач) — например реализовать мониторы, планировщики или подобные задачи, которые могут оказаться долгоживущими и просуществовать до самого завершения программы. Такие задачи не назовешь разовыми, поэтому в их случае не будет особой пользы от пула переиспользуемых воркеров.

  • Короткоживущие и/или многочисленные специальные задачи: используем пул потоков или пул процессов.

  • Долгоживущие и/или сложные задачи: используем класс Thread или Process.

В случае, если вы решите работать с конкурентностью на уровне потоков, придется выбирать между пулом потоков и классом Thread.

В случае, если вы решите работать с конкурентностью на уровне процессов, придется выбирать между пулом процессов и классом Process.

Также потребуется учесть и иные соображения, в частности, такие:

  • Неоднородные или однородные задачи: пожалуй, пул более уместен для набора разнообразных (гетерогенных) задач, а класс Process/Thread лучше использовать в случаях, когда задачи являются однотипными (однородными).

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

  • Множество задач или одна задача: естественно, пул поддерживает множество задач, возможно, выдаваемых разными способами, тогда как класс Process/Thread поддерживает всего один тип задач, то есть задача решается от начала и до конца, как только она сконфигурирована или переопределена.

Чтобы обрисовать ситуации конкретнее, давайте приведем несколько примеров:

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

  • Фоновую задачу, наблюдающую за ресурсом, лучше реализовать при помощи класса Thread/Process, поскольку это долгоиграющая задача, и в ней может заключаться много сложного специализированного функционала, возможно, распределенного между вызовами многих функций.

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

  • При решении однократной задачи, в которой поддерживается состояние, можно было бы остановиться на классе Thread/Process, так как класс поддается переопределению: так, чтобы он хранил состояние в переменных экземпляра, а для реализации модульного функционала задействовал методы.

Следующий рисунок поможет выбрать, что лучше использовать: пул воркеров или класс Thread/Process.

Более подробное сравнение пула потоков/процессов и решения одной сложной задачи приводится в постах:

Далее давайте рассмотрим тип пула, который может нам понадобиться.

Шаг 3: пулы или исполнители?

На третьем шаге давайте подберем тип пула, которым будем пользоваться.

В данном случае существует два основных таких типа, вот они:

  • Пул: multiprocessing.pool.Pool и класс для поддержки потоков в multiprocessing.pool.ThreadPool.

  • Исполнители (Executors): класс concurrent.futures.Executor и два подкласса ThreadPoolExecutor и ProcessPoolExecutor.

Оба предоставляют пулы воркеров. Сходство между ними большое, а вот различия небольшие и тонкие.

Вот некоторые сходные черты:

  • Оба существуют в двух версиях: для работы с потоками и для работы с процессами.

  • Оба могут выполнять специальные задачи.

  • Оба поддерживают синхронное и асинхронное выполнение задач

  • Оба поддерживают проверку статуса и ожидание асинхронных задач.

  • Оба поддерживают функции обратного вызова для выполнения асинхронных задач.

Какой бы вариант вы ни выбрали, это особо не отразится на вашей программе.

Основное различие заключается в интерфейсе, предоставляемом каждым из них. Точнее, речь идет о небольших различиях в расстановке акцентов и в том, как именно обрабатываются задачи.

Например:

  • Исполнители позволяют отменять уже выданные задачи, а пул — нет.

  • Исполнители позволяют работать с коллекциями неоднородных задач, а пул — нет.

  • Исполнители не позволяют принудительно завершить все задачи, а пул позволяет.

  • Исполнители не предоставляют несколько параллельных версий функции map(), в отличие от пула.

  • Исполнители позволяют работать с исключениями, выброшенными в рамках задачи, а пул не позволяет.

Пожалуй, основная разница между этими пулами заключается в том, что Pool сосредоточен на конкурентных циклах for, работающих со множеством разных версий функции map(), например, если нужно применить к функции каждый аргумент, содержащийся в списке на перебор.

У исполнителей также есть такая возможность, но они делают акцент скорее на асинхронном выполнении задач и на управлении коллекциями задач.

Следующий рисунок резюмирует различия между пулами и исполнителями

Подведём итог:

В Python для решения конкурентных задач есть три разных реализации:

  1. Многопроцессная — реализованная в модуле multiprocessing. Этот подход стоит выбирать, если у вас CPU-Bound-задачи.

  2. Многопоточная — реализованная в модуле threading. Этот подход стоит выбирать для I/O-Bound-задач.

  3. На основе корутин, реализованных в модуле asyncio. Этот подход стоит выбирать для I/O-Bound-задач. Стоит применять когда вы:

    3.1 Явно хотите или должны использовать парадигму асинхронного программирования.

    3.2 Вам нужно выполнить более 1000 I/O-Bound-задач.


ссылка на оригинал статьи https://habr.com/ru/company/skillbox/blog/685682/

От взлома протокола в старом «железе» до разработки программ

Как-то раз получил задачу — «взломать» протокол передачи данных внутри торгового автомата. Торговый автомат по продаже охлаждённых газированных напитков, включает несколько цифровых плат, объединенных сетью RS-485 — судя по наличию чипов MAX485 на платах.

Заказчик хотел заменить старую плату индикации режима работы на Raspberry Pi, при этом протокола передачи данных у него не было и схем тоже. Сам заказчик и его громоздкое оборудование находилось очень далеко от нас, и чтобы не тратить лишнее время и деньги, предложили сфотографировать цифровые платы и купить дешевый китайский логический анализатор.

Cовместимый с программой Saleae Logic — очень удобная программа для такого взлома. На фото плат, сделанных заказчиком, нарисовали, куда подключиться на индикаторной плате, а именно на вход передатчика и выход приемника чипа MAX485 — чтобы видеть, что приходит на плату индикации и что она отправляет в ответ — если вообще отправляет.

Затем вместе удаленно, через Teamviewer, сделали записи потоков данных в различных режимах. Внимание! Teamviewer уже бесполезен, события давно происходили.

Примеры режимов в терминах платы индикации — всё исправно, идёт охлаждение сиропа, сироп закончился, общая неисправность, требуется обслуживание, и т.п. Всё это для двух независимых контейнеров с разным сиропом. На плате индикации все возможные состояния отображались 6-ю светодиодами.

В итоге получили более 16 файлов в формате Saleae Logic 1.x с записями во всех режимах, которые смог обеспечить заказчик на своем оборудовании. В программе сразу было видно, что скорость примерно 38925 бод ( ближайшая стандарная 38400 ). Все остальные параметры передачи байта — 8 бит, 1 стоповый бит, без бита четности и т.п.

Программа Logic 1.x может показывать сразу расшифрованные байты, если задать параметры протокола передачи байта. По паузам получилось выделить отдельные сообщения. В итоге, после просмотра записи, получили представление о том, когда и какие байты передаются в различных ситуациях. Например, каждое сообщение начинается с байта 0xC0 и заканчивается байтом 0xC1:

Пауза 3с после включения Байты принимаемые платой индикации: 0xC0, 0x01, 0x7D, 0xE0, 0x01, 0x40, 0xF7, 0xFA, 0xC1  Пауза 0.25мс Байты принимаемые платой индикации: 0xC0, 0x01, 0x40, 0x01, 0x7D, 0xE0, 0xF6, 0x0A, 0x03, 0x01, 0x55, 0xC1  Пауза ~95 мс Байты, принимаемые платой индикации: 0xC0, 0x02, 0x00, 0x01, 0x40, 0xF7, 0x8D, 0xC1, 0xC0, 0x01, 0x40, 0x02, 0x00, 0xF6, 0x28, 0x03, 0x00, 0xAB, 0xC1,  Пауза ~95 мс Байты, принимаемые платой индикации: 0xC0, 0x02, 0x00, 0x01, 0x40, 0x13, 0x02, 0x83, 0xC1,   0xC0, 0x01, 0x40, 0x02, 0x00, 0x12, 0x02, 0x02, 0x02, 0x01, 0x01, 0x27, 0xC1,  Пауза 94 мс Байты, принимаемые платой индикации: 0xC0, 0x01, 0x80, 0x01, 0x40, 0xF7, 0x1A, 0xC1, 0xC0, 0x01, 0x40, 0x01, 0x80, 0xF6, 0x20, 0x03, 0x04, 0x7D, 0x5D, 0xC1,  Пауза 95 мс Байты, принимаемые платой индикации: 0xC0, 0x01, 0x80, 0x01, 0x40, 0x17, 0x02, 0x01, 0x00, 0x28, 0x60, 0x32, 0x00, 0x28, 0x60, 0x32, 0x00, 0x1D, 0x4C, 0x32, 0x00, 0x1D, 0x4C, 0x32, 0xF6 0xC1, 0xC0, 0x01, 0x40, 0x01, 0x80, 0x16, 0x20, 0xC1,  Пауза 1186 мс Байты, принимаемые платой индикации: 0xC0, 0x01, 0x80, 0x01, 0x40, 0x15, 0x00, 0x00, 0x00, 0x00, 0x7A, 0xC1,  0xC0, 0x01, 0x40, 0x01, 0x80, 0x14, 0x00, 0x35, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x64, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0xC1  Пауза 49 мс Байты, принимаемые платой индикации: 0xC0, 0x02, 0x00, 0x01, 0x40, 0x11, 0xB9, 0xC1  Пауза 9.3 мс Байты, принимаемые платой индикации: 0xC0, 0x01, 0x40, 0x02, 0x00, 0x10, 0x00, 0xFA, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xC1-  Пауза 33.3 мс Байты, принимаемые платой индикации: 0xC0, 0x01, 0x7D, 0xE0, 0x01, 0x40, 0x23, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x8E, 0xC1  #-------- в этом сообщении есть байты, меняющиеся в зависимости от состояния устройства  Пауза 0.23 мс Байты, передаваемые платой индикации:  0xC0, 0x01, 0x40, 0x01, 0x7D, 0xE0, 0x22, 0x64, 0xC1  

После изучения всех 16-ти записей стало ясно, что данные о состоянии устройства есть в сообщении, начинающимся с байтов 0xC0, 0x01, 0x7D, и после этого сообщения сразу следует сообщение от платы индикации.
Информации стало достаточно, чтобы заменить плату индикации на одноплатный миникомпьютер Raspberry Pi с адаптером RS-485.

На своей стороне собрали имитатор передаваемых сообщений с выходом RS-485, добавили в него записанные сообщения. Подключили к своей Raspberry Pi через адаптер, написали и отладили простую программу на Python с выводом в консоль принимемых сообщений.

Заказчик закупил всё необходимое, отключил плату индикации и подключил Raspberry Pi через адаптер RS-485, согласно нашей схеме подключения. Предоставил нам удалённый доступ по SSH к подключенному Raspberry Pi. После подачи питания на устройство раздался возмущённый писк из торгового автомата, по документации — сигнализация о поломке.

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

Данные о состоянии оборудования принимались правильно и стабильно.

Писк оказался проблемой. После включения питания торговый автомат немного пищал, пока запускалась программа на Raspberry. Иногда неожиданно автомат мог пискнуть, правда, это происходило крайне редко и не каждый день. Как выяснилось, время ожидания ответа менее 300мкс. Сначала переписали программу приема данных на Си и запустили её отдельным процессом, потом разработали свою плату адаптера RS-485 Raspberry Pi с микроконтроллером, единственной задачей микроконтроллера было разпознавать запрос и оперативно на него отвечать. После этого писк после подачи питания исчез, так как микроконтроллер запускается почти мгновенно по сравнению со временем загрузки ОС в Raspberry Pi.

Симулятор с макетом нового адаптера
Симулятор с макетом нового адаптера

Первая версия серийного адаптера с микроконтроллером:

Следующим этапом стало добавление функций в программу на Raspberry Pi, а именно запуск большого рекламного видео на мониторе, отображение
аварийных состояний иконками справа и слева, отправка телеметрии на web сервер.

Торговый автомат после всех доработок, с монитором и Raspberry Pi внутри. Работает моя программа. Желтые иконки внизу показывают, что сироп закончился:

Далее разрабатывали почти заново сайт Заказчику, добавляли удаленную загрузку видео с этого сайта на торговый автомат, писали ему кучу самых разных программ и многое-многое другое. Но это уже совсем другая история.


ссылка на оригинал статьи https://habr.com/ru/post/685688/

Как ГЛОНАСС испортил мне кровь и причем тут BI

Привет, Хабр! Этот будет пост о том, как тяжело может идти принятие решения об импортозамещении популярной западной системы. Речь пойдет о замене для PowerBI, который мы только-только начали использовать. Под катом — история с самого начала (в кратком изложении), а также много моих личных страданий и размышлений. Если вы тоже меняли BI, поделитесь своим опытом — может быть он поможет мне или кому-то еще.

Меня зовут Антон, и, если честно, я давно хотел что-то написать здесь. Но никак не доходили руки, не хватало времени или мотивации. Но вот свершилось! Я окончательно замучался с выбором российской BI-платформы. Это и сподвигло написать пост. Я даже нарисовал несколько картинок, чтобы все это было веселее, так что не судите строго за кустарное творчество. 

Но поехали. Когда-то я программировал на C#, но уже несколько лет работаю на стыке ИТ и бизнеса и в основном занимаюсь тем, что перевожу с языка руководства на язык ИТ-шников в нескольких компаниях. С той фирмой, о которой речь пойдет сегодня мы успешно прошли этот путь и поняли, что хотят видеть шефы на месте корпоративного портала и системы Service Desk. И вот, наконец, дело дошло до системы BI.

Как хорошо все начиналось…

Приходит начальник и говорит: 

— Нам, Антон, нужна такая система, которая будет нам рисовать графики красивые.

— Так у вас же Иван Павлович (имя изменено), уже есть Microsoft Excel…

— Нет, ты не понял…

И вот мы приступили к выбору продукта. Но выбирали недолго. Вместо Microsoft Excel взяли Microsoft Power BI. Дело оставалось за малым (ха-ха) — объяснить разработчикам, передачу каких данных мы настраиваем в PowerBI, а также наладить контакт с теми сотрудникам, которые теперь называются аналитиками.

Хорошо, когда практику BI выстраивает какой-то опытный человек. Но когда компания решает использовать уже имеющиеся кадры, сделав из них аналитиков, все получается намного сложнее.

Директор: — Я хочу видеть ежедневный отчет по выполнению плана во всех подразделениях у себя на рабочем столе (желание хорошее, не спорю).

New-born-Аналитик — Антон, как мне это сделать?

После этого Антон идет разбираться с языком запросов DAX (зачеркнуто) уговаривать руководство, что вместе с Power BI нужен еще и человек, который рубит в этом деле. К счастью, уговоры возымели свои результаты и на испытательный срок был взят настоящий аналитик.

В этот момент я становлюсь еще и переводчиком между начальством и аналитиком.

— Герман (имя изменено), нам нужен такой красивый дашборд, чтобы по нему было понятно, что у нас в филиалах.

— Иван Палыч, что именно должно быть на дашборде?

— Эффективность сотрудников, выполнение плана, цифры из бухгалтерии — все!

— На одном дашборде или на нескольких?

— Я не знаю, спроси Антона, он у нас спец по ИТ…

В результате мы построили несколько дашбордов на PowerBI и наладили интеграцию между платформой и рядом систем — бухгалтерией, складской системой, CRM и немного ERP. Руководство осталось довольно полученным результатом, хотя у меня создалось впечатление, что до полного раскрытия потенциала BI в компании еще далеко. Все-таки, запросы на создание дашбордов у нас происходили в формате “Дайте нам всю информацию по отгрузкам”. Мне кажется, хорошо бы получать задачи с более точными целями, например “построить динамику отгрузок по месяцам и визуализировать отклонения”. Если у кого-то есть понимание, как именно помочь руководителю разобраться с этим, поделитесь какими-нибудь курсами или литературой, пожалуйста.

Тем не менее, результаты были оценены руководством по достоинству:

  1. Мы стали узнавать о различного рода простоях практически сразу, на не в конце месяца, а значит — получили возможность что-то с этим делать.

  2. BI-платформа помогла выявить простаивающие ресурсы (в том числе человеческие).

  3. Сразу вылезли на поверхность слишком затратные и не окупающиеся проекты и инициативы (и были свернуты).

  4. На рабочем столе Ивана Павловича появились красивые картинки, которые радуют его каждый день. 

Тут можно было бы завернуть мысль о том, что BI — не такая уж простая штука, и чтобы ей пользоваться нужно самим руководителям понять, как именно они собираются использовать полученные данные. Но тут случилось 22.02.2022, и оказалось, что пользоваться дальше Power BI мы не можем. Точнее может и можем (например. через VPN), но руководство не хочет отвечать за всяческие риски, а к наличию BI уже все привыкли.

Так много русского BI…

Естественно, выбирать российскую платформу для дальнейшего развития BI отправили меня…и тут выяснилось неожиданное. Оказалось, что отечественные разработчики создали множество BI-платформ, которые теоретически (подчеркиваю, теоретически) могут заменить Power BI в цикле сбора данных, аналитики и визуализации. У нас есть даже свой Институт Бизнес-Аналитики, в котором проходят тренинги и подготовка аналитиков для работы с разными системами. Чудеса да и только.

Впрочем, первое, что мне попалось на глаза — это исследование с очень забавным названием: Круг Громова. Ну почему бы и нет: вместо Квадрата Гарнтера — Круг Громова.  Импортозамещению подвергается не только Гартнер, но и сам квадрат. 

Сначала мне попался выпуск за 2021 год. Читаю исследование. Оказывается, что у нас в России как минимум 9 BI-платформ корпоративного (Enterprise) уровня и еще 5 платформ категории SaaS. Не говорю уже о “нишевых” решениях, которых в исследовании Громова приведено еще штук 20 — даже считать не стал. 

Уже избалованное красивыми графиками, сравнениями и диаграммами начальство хочет видеть у себя сравнение всех этих 14 платформ. К счастью, в самом Круге Громова есть сравнительная табличка, которая помогла мне, честно, спасибо г-ну Громову, как-то ранжировать системы и сразу же обоснованно отсечь неподходящие нам решения.

В итоге было решено рассмотреть те платформы, у которых нет оценок “1” и “2”. А это — Форсайт, Visiology, а также Polymatica и Luxms BI, у которых “кол” стоит за “расширенное обнаружение данных”. Но мы, собственно, ничеого такого не планируем и в итоге начали рассматривать 4 системы.

Чуть позже мне подвернулся материал эксперта по BI Евгения Стучалкина, который раньше работал с Qlik Sense. Он говорит, что в сущности не важно, какую BI-платформу вы используете — главное перейти к нормальной модели данных и внедрить Loginom для работы с данными. Концепция интересная, но она одновременно наводить на мысль, что Loginom сам по себе не очень в качестве BI-платформы. А кроме того наше руководство больше всего любит BI именно за визуализацию и графику. Так что мы, вероятно, правильно не взяли его в сравнение. 

Тем временем, мне попадается Круг Громова 2022 года. Судя по синпсису, в нем серьезно изменилась методология, однако сохранилось количество систем, участвующих в тесте — их снова 14! Хотя часть “игроков высшей лиги” выбыла, а на их место были приняты другие платформы. Но, к счастью, наши 4 лидера по-прежнему там, и у них по-прежнему неплохие оценки. Вытираю пот со лба и иду к начальству без изменений в концепции по этому поводу. А наш аналитик…

…наш аналитик Герман, который уже полюбился начальству как и красивые графики, тоже подготовился и сообщает, что не будет работать ни с чем другим кроме DAX, потому что он привык писать запросы в такой форме. Не буду приводить разговор дословно, но смысл был в том, что при замене на несоответствующее решение, дашборды буду делаться медленнее, пострадает оперативность, а значит BI станет менее эффективным. 

“BI станет менее эффективныым!”

Эта фраза захватывает внимание начальства, и я начинаю думать, что поиск внешнего аналитика, который отлично владеет DAX-ом и уже умееет работать в PowerBI может быть была не такой хорошей идеей. Может быть стоило вырастить кадры внутри…но уже поздно.

Я захожу в поисковик, чтобы доказать, что это невозможно и с удивлением для себя нахожу, что с осени 2022 один из российских вендоров Visiology собирается поддерживать DAX. Эх, жаль, что не к осени 2021! Но новость интересная, воспринимается нашим руководством и аналитиком с большим энтузиазмом, и мы начинаем ждать новую версию платформы, чтобы попробовать ее в деле. 

Кажется, все успокоилось, мы поняли, куда переезжать с PowerBI, но тут в СМИ выходит новость: IconBoard (я так понял, учрежденная АО “Глонасс”)  щедро предлагает рынку свою BI-платформу, которая уже использована для трекинга автомобилей, подключенных к системе  ЭРА-Глонасс.  В пресс-релизе сказано, что это “редкий вид BI-систем Enterprise-уровня”, который позволяет делать все в реальном времени.

На меня начинают сыпаться все шишки: как же я недосмотрел, что у нас в России есть “уникальная BI-система корпоративного уровня. И от меня требуют добавить к сравнению и это решение. Но проблема в том, что я не нашел почти никаких подробностей про эту BI-платформу и про компанию, которая ее выпускает. Естественно, блога на Habr у нее нет. Про руководителя и идеологи известно, что он 20 лет работает в банковской сфере и цифровых платежах. А сама платформа, насколько я понял, была опробована только для одной задачи…может быть поэтому она “редкая”? 🙂 

Мои аргументы о том, что вряд ли IconBoard поддерживает DAX никого не убедили, и теперь мы снова находимся в точке, когда нужно выбрать BI-платформу, но какую именно? Вот скажите, доколе будут всплывать из-под плинтуса все новые и новые отечественные BI-решения?

Кстати, серьезно, если вы знаете еще 1-2 российских BI-платформы, подскажите, пожалуйста! Может быть я сразу добавлю их в сравнение, и мы проще пройдем этот этап, от успешного заверения которого зависит моя премия…


ссылка на оригинал статьи https://habr.com/ru/post/685690/

Почему KRaft заменил ZooKeeper

Зачем менять ZooKeeper на встроенный лог Apache Kafka® для управления метаданными? В этой статье вы узнаете, зачем нужна была эта замена, какие преимущества даёт протокол консенсуса на основе кворума, вроде Raft, и как работает контроллер кворума поверх протоколов KRaft.

Что не так с ZooKeeper?

Контроллер Kafka для репликации внутри кластера был реализован ещё в 2012 году и с тех пор почти не изменился: у каждого кластера есть один узел в роли контроллера, он выбирается вотчерами в ZooKeeper. Он хранит логи партиций, обрабатывает потребление/создание запросов, как другие брокеры, а ещё отвечает за метаданные кластера — ID и серверные стойки брокеров, топики, партиции, информацию о ведущих узлах и наборах синхронизированных реплик (in-sync replicas, ISR), конфигурации кластера и топиков, а также учётные данные для безопасности. Вся эта информация хранится в ZooKeeper как источник истины, в итоге почти все операции чтения и записи в ZooKeeper проходят через контроллер.

Иногда и другие брокеры, кроме контроллера, могут общаться с ZooKeeper напрямую, например, когда ведущий узел обновляет информацию о ISR. Контроллер регистрирует вотчеров в ZooKeeper в случае любого изменения метаданных. Метаданные могут меняться самим контроллером, другими брокерами или клиентами, которые записывают в ZooKeeper данные напрямую.

В большинстве случаев, когда такой вотчер срабатывает, контроллер обрабатывает его в однопоточном цикле и распространяет обновлённые метаданные по остальным брокерам. Если вы знакомы с историей Kafka, то в курсе, что раньше и другие клиенты, например, консьюмеры, могли общаться с ZooKeeper напрямую. Сейчас все проходит через брокеры, чтобы снизить нагрузку чтения/записи на серверы ZooKeeper. По большей части для доступа в ZooKeeper используется один контроллер, но когда брокеров и партиций в кластере становится больше, возникают узкие места, потому что ZooKeeper по-прежнему используется как источник истины для метаданных в Kafka.

Ограничения при масштабировании со старым контроллером: остановка брокера

Представьте себе остановку брокера при старом контроллере. Допустим, у нас есть одна партиция с тремя репликами на брокерах 1, 2 и 3. Все три реплики синхронизированы и находятся в списке ISR. Брокер слева сейчас ведущий, но собирается завершить работу. Он должен отправить запрос контроллеру. Контроллер посмотрит, какие партиции размещены в брокере, и попробует обновить метаданные. Затем нужно будет выбрать новый ведущий брокер и перенести в него партиции, которые размещались на старом. Обновленная информация об ISR записывается в ZooKeeper, и контроллер распространяет новые метаданные по остальным брокерам. В итоге контроллер отправляет два типа запросов: UpdateMetadata, чтобы обновить локальный кэш метаданных для всех брокеров, и LeaderAndISR для всех реплик в соответствующих партициях, чтобы изменить ведущий брокер и список ISR.

Когда контроллер удалит брокер 1 из всех партиций, которые на нём размещаются, он разрешит брокеру 1 завершить работу. В этом примере на брокере всего одна партиция, но на практике их могут быть тысячи, и контроллеру придётся записать в ZooKeeper много данных, чтобы изменить метаданные для каждой партиции. На это может уйти несколько секунд. Более того, контроллеру придётся передать новые метаданные остальным брокерам по одному за раз. Наконец, если клиенты пытаются узнать о новом ведущем узле у случайного брокера, они действуют наудачу — не факт, что на этом конкретном брокере информация уже обновилась. При таком раскладе у некоторых запросов может истекать время ожидания.

Ограничения при масштабировании со старым контроллером: смена контроллера при сбое

Теперь рассмотрим сценарий неожиданного сбоя контроллера. В этом случае срабатывает зарегистрированный вотчер ZooKeeper, все брокеры получают уведомление и сразу пытаются зарегистрироваться в ZooKeeper. Кто успеет первым, становится новым контроллером. Первым делом новый контроллер получает от ZooKeeper метаданные, в том числе информацию обо всех партициях в топиках по всем путям ZooKeeper. Затем он обновляет все метаданные партиций, которые размещались на прежнем контроллере, записывает новые метаданные в ZooKeeper и распространяет эти метаданные по остальным брокерам.

Самое заметное ограничение в этой процедуре — время, которое требуется контроллеру, чтобы получить метаданные от ZooKeeper. Оно зависит от общего количества партиций в кластере. До завершения инициализации новый контроллер не сможет обрабатывать административные запросы, например, на перебалансировку партиций. Это означает проблемы с доступностью.

Ограничения при масштабировании со старым контроллером: сводка

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

  • Контроллер записывает обновленные метаданные в ZooKeeper, чтобы сохранить их, и чем больше метаданных, тем больше времени на это уйдет.

  • У ZooKeeper есть и другие недостатки. Например, лимиты на размер znode и количество вотчеров, а также дополнительные проверки для брокеров, потому что набор метаданных на брокерах может различаться при переупорядочивании или задержке обновления.

  • Защита, апгрейд и отладка требуют больших усилий. Чем проще распределённая система, тем она долговечнее и стабильнее.

Чем KRaft лучше?

Наша цель — создать решение для тысяч брокеров и миллионов партиций, но сначала давайте поймём, что мы на самом деле храним в ZooKeeper.

На первый взгляд кажется, что мы храним текущие снапшоты метаданных из путей ZooKeeper. На самом деле у нас столько вотчеров и версий путей, что мы отслеживаем последовательность событий изменения метаданных, то есть лог метаданных. По сути, в ZooKeeper API все записанные данные тоже хранятся в виде лога транзакций.

Реализация лога метаданных

Зачем усложнять и прятать логи метаданных куда-то в Zookeeper, если можно хранить их в самой Kafka? В конце концов, логи — это специализация Kafka. Почему бы контроллеру не вести лог метаданных как простой топик Kafka? В этом случае операции, связанные с метаданными, можно естественным образом упорядочить по смещениям записей, а затем объединить и передавать вместе асинхронно для улучшения производительности.

Если нужно распространить изменения метаданных, брокеры реплицируют ченджлог, обходясь без RPC. Расхождения между брокерами больше не будут проблемой — локальное материализованное представление метаданных на каждом брокере будет в конечном итоге согласовано, ведь данные поступают из одного лога, а изменения определяются смещением в логе метаданных, с которым они синхронизируются.

Ещё одно преимущество — управлять логом метаданных контроллера можно независимо от остальных логов (управляющий уровень изолирован от пути передачи данных), с отдельными портами, очередями запросов, метриками, потоками и т. д.

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

Алгоритмы репликации: все узлы или кворум узлов

Как мы будем синхронизировать «лог логов» с репликами? Для логов данных Kafka использует алгоритм репликации с ведущим узлом (primary-backup), при котором все данные записываются на ведущий узел (Leader), а затем реплицируются по фолловерам (Follower-1, 2 и т. д.). Когда все фолловеры подтвердят получение данных, ведущий узел считает запись зафиксированной и отвечает клиенту. Для репликации лога метаданных можно использовать тот же алгоритм, то есть ждать подтверждения от всех реплик.

Есть ещё один популярный алгоритм репликации — репликация с кворумом. Ведущий узел по-прежнему один, но подтверждения он ждёт не от всех фоловеров, а только от большинства реплик, включая себя. Достигнув кворума, ведущий узел считает данные записанными и отвечает клиенту. В литературе по распределённым системам описаны разные алгоритмы консенсуса, которые работают по этой схеме. Например, Paxos и Raft.

Если мы ждём подтверждения не от всех реплик, а только от кворума, доступность повышается и репликация идёт быстрее. В Kafka используется режим отказа f + 1, то есть для устойчивости при f сбоев нам нужно минимум f + 1 реплик. При репликации с кворумом реплик должно быть 2f + 1.

KRaft — реализация Raft в Kafka

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

  • Мы можем позволить себе больше синхронизированных реплик для этого важного лога, чтобы повысить доступность.

  • При добавлении записей в лог метаданных задержки особенно нежелательны, иначе замедлится работа всего кластера.

Команда Kafka реализовала новый модуль репликации — KRaft. С одной стороны, он использует алгоритм Raft для репликации на основе кворума, а с другой — существующие возможности Kafka, вроде ограничения количества запросов (throttling) или сжатия. В итоге мы можем использовать для нового лога знакомые инструменты и методы устранения неполадок.

Раз ZooKeeper больше не выбирает новый ведущий брокер для лога метаданных, нужен отдельный протокол выбора. Причём этот протокол должен предотвращать ситуации, в которых будет выбрано нескольких ведущих брокеров или ни одного (из-за полной блокировки при определённых условиях).

Выбор ведущего брокера

Протокол KRaft использует имеющийся в Kafka механизм периода ведущего узла (leader epoch), который гарантирует, что в один период будет существовать только один ведущий брокер. У брокера в кластере может быть одна из трех ролей: ведущий (leader), голосующий (voter) или наблюдатель (observer). Ведущий брокер и другие голосующие образуют кворум и отвечают за согласованность реплицированного лога, а также за выбор новых ведущих при необходимости. Остальные брокеры в кластере — наблюдатели. Они пассивно читают реплицированный лог, чтобы синхронизироваться с кворумом. Каждая запись в журнале связана с конкретным периодом ведущего брокера.

При запуске все брокеры в настроенном кворуме инициализируются как голосующие и берут значение периода из локальных логов. На схеме ниже предполагается, что в кворум входит три голосующих брокера. У каждого в локальном логе есть по шесть записей из периода 1 (зеленый) и 2 (желтый).

Они ищут ведущий брокер, не находят, в итоге один из голосующих увеличивает значение периода на 1 и временно становится кандидатом в ведущие. Он отправляет всем брокерам в кворуме запрос, призывая проголосовать за него как за новый ведущий брокер этого периода.

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

1. Указанное в запросе значение периода не превышает его собственное значение периода.
2. Он уже проголосовал за этот период.
3. Его локальный лог длиннее, чем указанное смещение.

Если ничто из этого не истинно, он голосует за этого кандидата для этого периода. Голоса сохраняются локально, так что брокеры в кворуме не забудут об отданном голосе даже после начала периода. Когда кандидат получит большинство голосов кворума, включая себя, он считает выборы успешными.

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

С помощью проверки условий и механизма таймаута при голосовании мы гарантируем, что в конкретный период в KRaft будет выбран только один ведущий брокер, и этот брокер будет содержать все зафиксированные записи вплоть до периода, в который его выбрали.

Репликация логов

KRaft использует механизм репликации на основе pull (как Kafka), а не push (как исходный протокол Raft). На схеме ниже ведущий брокер Leader-1 содержит две записи (красный) для периода 3, а голосующий брокер Voter-2 получает (fetch) от него нужные данные.

Алгоритм такой же, как при получении реплик в Kafka: Voter-2 указывает в запросе fetch два значения: период, из которого нужны данные, а также лог и смещение. Leader-1 получает запрос и сначала проверяет период. Если всё в порядке, возвращает данные, начиная с указанного смещения. Брокер Voter-2 добавляет полученные данные в локальный лог и запрашивает новые данные с нового смещения. Тут ничего нового, стандартные процедуры получения реплик.

Давайте представим, что один из голосующих брокеров содержит записи, которых нет у остальных. На нашей схеме у брокера Voter-3, который бы ведущим в период 2, в локальном логе есть записи, не реплицированные на большинство брокеров в кворуме, а значит не зафиксированные. Брокер Voter-3 видит, что начался новый период и Leader-1 стал ведущим брокером, и посылает к Leader-1 запрос fetch, указав период 2, лог и смещение. Leader-1 получает запрос, видит, что период и смещение не соответствуют друг другу и возвращает код ошибки, говоря брокеру Voter-3, что в период 2 они дошли только до смещения 6. Voter-3 обрезает локальный лог до смещения 6.

Затем Voter-3 снова отправляет запрос fetch, на этот раз указав период 2 и смещение 6. Leader-1 возвращает брокеру Voter-3 данные из нового периода, и Voter-3 узнает о новом периоде, добавляя возвращенные данные в свой локальный лог.

Если брокеры Voter-2 и Voter-3 не получат ответ от Leader-1 в ожидаемое время, они начнут новый период и попробуют стать ведущим брокером на период 4. Как видите, запрос fetch не только извлекает данные, но и проверяет, жив ли ведущий брокер.

Механизм репликации: pull или push?

Исходный протокол Raft использует метод push, а в KRaft реализован механизм pull, который упрощает согласование данных — получив ответ на запрос fetch, голосующие брокеры могут обрезать лог до актуального смещения, прежде чем отправлять следующий запрос. В модели push придётся отправлять туда и обратно лишние запросы, ведь ведущий брокер должен определить, в какое место лога отправить данные.

Благодаря механизму pull протокол KRaft не так уязвим к деструктивному поведению брокеров, которые не знают, что их удалили из кворума, например, в результате переконфигурации. Если изгнанные брокеры продолжат отправлять ведущему узлу запросы fetch в модели pull, ведущий в ответ отправит код ошибки, чтобы сообщить брокеру, что его исключили из кворума и он может считать себя наблюдателем. При использовании Raft ведущий узел не знает, кто из удалённых голосующих брокеров продолжит считать, что входит в кворум. Удалённый сервер перестанет получать от ведущего узла данные и попытается стать новым лидером.

Ещё одно преимущество механизма pull в том, что Kafka уже применяет эту модель для репликации логов, так что мы можем повторно использовать имеющуюся реализацию.

Правда, ведущему брокеру понадобится отдельный API начала периода, чтобы уведомить кворум, а в Raft это уведомление поступало с данными, которые лилер отправлял фоловерам. Кроме того, чтобы зафиксировать записи на основе большинства серверов в кворуме, ведущему узлу приходится ждать от голосующих следующего запроса fetch для увеличения смещений. Пришлось пойти на компромисс, зато исключенные брокеры не мешают работе. Более того, эта модель уже реализована в Kafka, так что не пришлось изобретать велосипед и писать лишние тысячи строк кода.

Узнайте больше о других деталях реализации KRaft, например, снапшотах метаданных или API конечного автомата поверх лога KRaft: KIP-500, KIP-595 и KIP-630.

Как работает новый контроллер (контроллер кворума) без Zookeeper? Контроллер кворума создан поверх протокола KRaft. Теперь при запуске брокеров Kafka в кластере небольшая часть от общего количества брокеров образует кворум. Брокеры в кворуме выбирают себе ведущего по алгоритму KRaft, и этот ведущий брокер становится контроллером кворума.

Контроллер отвечает за регистрацию новых брокеров, обнаружение сбоев на брокерах и обработку всех запросов, которые изменят метаданные кластера. Все эти операции можно обрабатывать по очереди и упорядочивать по времени, когда соответствующие события изменений были добавлены в лог метаданных. Другие голосующие брокеры в кворуме активно реплицируют лог метаданных, чтобы новые записи зафиксировались.

Затем можно обновить конечный автомат, созданный поверх кластера и представляющий текущий снапшот метаданных в кластере. Брокеры, которые не входят в кворум, наблюдают за логом метаданных и просто получают от кворума зафиксированные записи, чтобы обновлять собственные кэши метаданных. При таком подходе все локальные снапшоты метаданных естественным образом синхронизируются с полученным смещением в логе, так что метаданные согласованы, а любые отклонения можно легко исправить.

Если в кластер Kafka входит всего один брокер, он же и будет контроллером. По мере добавления брокеров они сами будут находить контроллер и регистрироваться в нем, а контроллер будет записывать информацию о брокерах в лог метаданных. Контроллер может запретить доступ к брокерам, пока они не перенесут назначенные им партиции и не будут готовы обслуживать клиентские запросы. При таком подходе будет ниже риск того, что время ожидания запроса истечет, если, например, новые брокеры отстают.

Новый контроллер кворума: остановка брокера и смена контроллера при сбое

Помните, мы рассматривали сценарии остановки брокера и смены контроллера в старой модели? Теперь рассмотрим их в новой.

Контроллер кворума получает данные о работоспособности всех зарегистрированных брокеров с помощью heartbeat-сообщений. Когда существующий брокер останавливается, он может сообщить о своем намерении в heartbeat-запросе, чтобы контроллер удалил его из всех партиций, как обычно, а ещё объединил все события по перемещению партиций и добавил их в лог метаданных вместе. При таком подходе задержки при завершении работы брокеров будут значительно меньше.

При сбое контроллера один из голосующих может стать новым ведущим брокером и сразу фиксировать данные в новом периоде, потому что все зафиксированные записи вплоть до нового периода уже будут реплицированы, так что не придётся тратить время на инициализацию нового ведущего из лога метаданных.

Эксперимент: ZooKeeper vs. контроллер кворума

Преимущества контроллера кворума по сравнению с контроллером в ZooKeeper наглядно видны в эксперименте, который был проведен в кластере Kafka с 2 млн партиций. На схеме показана задержка для контролируемой остановки и неконтролируемого сбоя. Очевидно, что при использовании контроллера кворума задержка гораздо меньше.

Если хотите узнать больше, читайте статью Apache Kafka Made Simple: A First Glimpse of a Kafka Without ZooKeeper.

Два ключевых момента этой статьи:

  • Метаданные лучше хранить в виде лога событий, как мы делаем это с остальными данными в Kafka.

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

Обучение Apache Kafka от Слёрма

 Курс «Apache Kafka База»: познакомимся с технологией, научимся настраивать распределённый отказоустойчивый кластер, отслеживать метрики, равномерно распределять нагрузку.

— Видеокурс «Apache Kafka для разработчиков». Это углублённый интенсив с практикой на Java или Golang и платформой Spring+Docker+Postgres. Интенсив даёт понимание, как организовать работу микросервисов и повысить общую надежность системы.

Купить комплектом 2 курса выгоднее на 30%: https://slurm.club/3KzRtzM


ссылка на оригинал статьи https://habr.com/ru/company/southbridge/blog/685694/

Стресс, тревожность, депрессия. Как  выбраться из этого цикла?

Диагноз «депрессия» может поставить только психиатр. И только он может назначить курс лечения и поддерживающую терапию. Но иногда симптомы могут проявляться годами, медленно подтачивая личность. Этот материал — обзор того, чем является депрессия, вместе с чек-листом способов не дать тревожности и стрессу перерасти в нечто большее. 

Откуда берется депрессия? 

Усталость, отсутствие настроения, желание получить заботу и любовь — это не депрессия. Да, это плохое настроение, злость, раздражение. Но то, о чем мы говорим начинается с апатии, невозможности почувствовать удовольствие и расстройствами сна. К этому приводят следующие механизмы.

Физиологические факторы депрессии

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

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

  • Рецепторы недостаточно чувствительны к уровню вырабатываемого дофамина.

  • Или же организм недополучает прекурсоры дофамина, для его нормального синтеза.

Подробнее об этом можно узнать из потрясающего курса лекций Роберта Сапольски, преподавателя Стэнфордского Университета. Кстати, именно колебания уровня дофамина от высокого к низкому — это проявления того же биполярного расстройства, когда человек способен неделями маниакально преследовать поставленные цели, а потом резко выгорает, и неделями не встает с дивана.

Ситуативные факторы депрессии

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

  • Постоянный рост задач, при полном игнорировании наград за их решение.

  • Отсутствие отдыха. Особенно характерно для удаленки. Ведь раз ты можешь работать в любое время, то можешь работать постоянно.

  • Гонка за постоянно обесценивающимися показателями. Вот вроде вы достигли чего-то важного, а оно снова обесценивается. 

  • Фрустрация. Нет распорядка дня, приоритет задач постоянно меняется, результаты сложно отследить.

Все перечисленные факторы ведут к тому, что уровень стресса растет, при том что активные действия не приносят ровно никакого результата. И тогда жизнь словно выцветает, становясь черно-серой, с единичными проблесками выходных.

Стресс, тревожность, депрессия. Как я попал в эту ловушку, и что помогло выбраться

Главная иллюзия в том, что уж я-то никогда в депрессию не попаду. Тоже так думал, ведь за плечами магистратура на факультете психологии и 5 лет аспирантуры. Но, зарабатывая копирайтингом, столкнулся с накатывающими приступами тревоги и апатии. И смог из них выбраться. Вот как это было.

Первые звоночки

Разгар 2-го курса аспирантуры, учебу получалось совмещать с работой, заказов много, темп уверенный. Иногда прилетала и халтурка, когда приходилось засиживаться до 11 вечера, или забирать субботу/воскресенье. Все шло хорошо, ровно до одного вечера:

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

  • Стала расти раздражительность и легкая тревожность. Решено было добавить тренировки, чтобы сгонять злость, и месяца три это помогало.

  • Начались легкие проблемы со сном. Сложно уснуть, но при этом просыпался в 5 утра как по звонку будильника. При этом уснуть снова не получалось. Кстати, именно такая проблема со сном — первый симптом депрессии. 

  • После одного особо неприятного дня пошел в зал, и решил себя буквально «убить тренировкой», чтобы выспаться.

  • В итоге,проснулся через 15 минут с момента засыпания от того, что не могу дышать. Спазм в горле, ощущение просто невероятно жуткое. На следующий день пошел к врачу.

После осмотра и сданных анализов, врач предположил 2 варианта: легочная инфекция или невротические расстройства. Поскольку днем мне дышалось нормально, а спазмы донимали только в момент засыпания, то решил ориентироваться на второй вариант.

На счет спазмов. Фактически, чтобы заснуть, мне нужно было сделать 2-3 попытки, каждый раз просыпаясь от того, что невозможно сделать вздох. Неприятное ощущение, сопровождаемое страхом умереть.

Чек-лист поможет побороть стресс, тревожность и депрессию в зародыше

Это достаточно общие факты, вот только помогает их общий эффект. Фактически, это просто инструменты ведения нормальной жизни. Но я попробую раскрыть их смысл и механику работы.

Все перечисленные способы работают в двух направлениях. Одни из них помогают распутать кучу дел, определить приоритеты. Вторые же нацелены на то, чтобы у вас появился ресурс что либо делать
Все перечисленные способы работают в двух направлениях. Одни из них помогают распутать кучу дел, определить приоритеты. Вторые же нацелены на то, чтобы у вас появился ресурс что либо делать
  • Ведение дневника. Изучая направление когнитивно-поведенческой терапии, я часто сталкивался с тем, что клиент ведет дневник о том, что он чувствует, видит, воспринимает. Выгружая мысли на бумагу, ты еще раз озвучиваешь и сознательно обдумываешь их. А также ищешь способы решения конкретно озвученных вопросов.

  • Медитация. До этого я периодически практиковал медитации по 5-15 минут. Немного очищает голову от тревожных мыслей. Сравнимо, наверно, с мытьем рук после улицы. Дескать,пришли домой с работы, почистили руки, прочистите и мысли. Неплохо помогало.

  • Кардио. Отошел от силовых тренировок в пользу кардио. Работало по тому же принципы — выбегать из себя тревожность. Но с меньшим стрессом для организма.Многие, с кем я обсуждал тревожность в сети, выбирали бег, велосипед, ролики. Что-то, что погружало в мысли при монотонной активности организма.

  • Врач, при осмотре, назначил общий комплекс витамин. Их эффект расцениваю скорее как плацебо. То есть, раз я их пью, значит организм получает все, что ему нужно. И поводов для беспокойства, что я в дефиците каких-то жизненно важных элементов — нет. 

  • Чтение книг. Вообще чтение расслабляет. Но из книг я узнал про первичный и вторичный страхи. Первичный — работающий в моменте, как ответ на раздражитель. А вот вторичный, тот, который и перекрывал дыхание во сне, провоцируя рефлекторный спазм. 

  • Определение границ контроля. Разбираясь в себе, я анализировал работу. И внезапно обнаружил, что передо мной ставили задачи, результат которых я не мог контролировать. То есть, я волновался из-за вещей, на которые не мог оказать никакого влияния. И это подтачивало личность долгие годы. Подробнее об этом может рассказать стоицизм.

  • Время для ничегонеделания. На фоне всего вышеперечисленного удалось отойти от мысли о том, что нужно быть сверхпродуктивным 24/7. В погоне за высокими показателями и кроется постоянная фрустрация и страх ошибок. И как же приятно лежать на полу, чувствуя под спиной ковер, который задает стиль всей комнате)

  • Давление на себя. В процессе всего пути борьбы с тревожностью, выгоранием и полным упадком сил я давил на себя, стремясь избавиться от этого любой ценой. И переживал еще больше из-за того, что мне это не удавалось. Тем самым проваливаясь глубже в отчаяние. 

  • Принятие страха. Следует из предыдущего пункта. Не боритесь с этим. Просто примите как факт и позвольте этому произойти. Страх — это симптом, и нет смысла его глушить. Есть смысл выговориться, выплеснуть его, озвучить и посмотреть на то, что тебя пугает.

  • Если вы перфекционист, то это отдельный пласт для работы. В погоне за идеальным легко передавить на себя, допустить достижение цели путем выгорания и эмоциональной опустошенности. Отпустите попытки быть идеальным во всем. Вас все равно за это никто не похвалит.

  • Поддержка близких. Это прям основной момент, который мне помог с этим бороться. Помощь жены, её поддержка, принятие слабостей, время на выход из кризиса. Когда есть кто-то, кто вам помогает, это невероятный положительный момент в борьбе за свое психологическое благополучие.

  • Практика КПТ предусматривает вопрос к самому себе: «чем мне выгодна тревожность?». Ответ был прост: тревога не давала мне работать. Она оберегала меня от переработки и перенапряжения. Напрямую говорила: сделай перерыв, чувак. Ты переходишь за грань.

И самый главный момент. Если вы сейчас переживаете эмоциональное выгорание, апатию или буквально в депрессии — помните, вы это преодолеете. У вас уже есть план действий и немного ресурса. Неважно, сколько это займет. Вы сделаете 5, 10, 15 шагов, но избавитесь от этого.

 Ваш организм пытается докричаться, что что-то идет не так. Важна только ваша воля, стремление и вера в то, что изменить состояние реально.

Предупредить легче, чем лечить

Банально, но факт. Моя ситуация колебалась между общей тревожностью и депрессивным расстройством, так как приступы банально выматывали силы. Но все обошлось. Определенные факторы, везение, вовремя начал заниматься собой. 

Больше про баги в сознании и способы от них избавиться — читайте в Телеграмм канале. Возможно у вас есть свой рецепт, как справиться с эмоциональными проблемами. Напишите, пожалуйста, в комментарии.

Автор: Филипп Дончев. Редактор сообщества RISE


ссылка на оригинал статьи https://habr.com/ru/post/685696/