Недавно я имел удовольствие включить в продукт модель расширяемости. Это не первое в своем роде; многие программные продукты имеют похожую концепцию. Основной поток состоит в том, что задача требует выполнения, мы находим обработчик (некоторый код), а затем запускаем задачу, результат которой определяет, что запускать дальше. Это продолжается до тех пор, пока не останется ничего, что можно было бы запустить. Такая система хорошо моделирует бизнес-поток, у вас есть:

  • Задания
  • Отношения
  • Условия / филиалы
  • Есть место для расширяемости и повторного использования кода.

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

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

Хотя некоторые из обработчиков задач являются основными и ими можно управлять, конечная цель - дать клиентам возможность встраивать большую часть своего собственного кода. Это вызовет недоумение у любого разработчика, и это справедливо; нам нужно убедиться, что клиенты не причиняют себе вреда и безопасно создают отличные дополнения к продукту.

Давайте рассмотрим некоторые меры, которые мы предприняли для создания прозрачной системы.

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

  • Задача и обновленная задача, имея неизменяемую задачу перед выполнением обработчика и обновленную задачу после обработчика, мы можем выполнить различие некоторых обновленных свойств, которые нам, возможно, понадобятся для выполнения дальнейших действий;
  • Ошибки, любые исключения / ошибки, которые могут возникать изнутри обработчика, могут быть перехвачены и отслежены в контексте;
  • Журналы, любая информация, связанная с выполнением обработчика;
  • Контрольно-пропускной пункт, мы расскажем об этом подробнее позже

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

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

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

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

С учетом сказанного, на ранней стадии разработки гораздо полезнее иметь изобилие информации, чем ничего.

Как и при регистрации сбоев, не менее важно понимать, какие области системы вызывают проблемы с производительностью. Основной движок, который запускает обработчики задач, должен быть максимально эффективным и должен максимально охватывать асинхронное программирование, есть несколько обработчиков задач, выполняемых параллельно, и мы должны совместно уступать, когда мы ждем. для ввода / вывода. Это хорошо покрывается Node.js, но другие фреймворки и языки имеют аналогичную поддержку. Существует несколько проектов сообщества по обнаружению блокировки асинхронных методов:

Для этого у нас есть методы (возможно, экспериментальные?) Для обнаружения асинхронной блокировки. В дополнение к этому, простой таймер, обернутый вокруг вызова обработчика, имеет большое значение для определения длительно выполняемых задач. Это не обвинение обработчика или то, что «код должен быть плохим!»; у него могли быть реальные причины для бега на такую ​​длину.

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

Не рекомендуется сильно отменить выполнение обработчика, Эндрю Арнотт хорошо описывает это в своем блоге о Шаблонах отмены.

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

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

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

  1. Получите блокировку от клиента (мы не хотим, чтобы несколько обработчиков работали против одного клиента)
  2. Запустить обработчик
  3. Действие результат обработчика (обновление базы данных)
  4. Снять блокировку

Проблема возникает, когда что-нибудь может случиться после шага 2. Мы запустили код пользователя, мы абсолютно не хотим, чтобы это повторялось снова. Решение заключалось в том, чтобы отслеживать ход выполнения в контексте, и если что-то пойдет не так, в нисходящем потоке, то всплывет конкретная ошибка, которая сохранит состояние там, где это возможно. Это может быть связано с шиной обмена сообщениями, записано где-то на диске и т. Д.

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

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