Мониторинг
Продолжаем разговор о том, что в NiFi делать можно и нужно, а что можно, но лучше не стоит. Если вы пропустили первую часть разговора, то вам сюда. Там про улучшение читаемости схем и повышение производительности (ну почти). Здесь же пойдет речь о том, как проводить мониторинг бизнес-части схемы, чтобы всем было хорошо (ну или чтобы не было плохо), ну и немного о переносимости процессоров. Поехали!
Есть мнение, что хуже всего — не вести мониторинг бизнес-части схемы совсем, используя популярный подход «и так сойдет!». Но если подумать, есть одна вещь хуже отсутствия мониторинга — неправильный мониторинг. Типовой «временный костыль»: добавляем отключенную\остановленную группу процессоров, переносим туда поток ошибок, дебажим…. и забываем. Что случается потом? Да-да, очередь переполнилась, backpressure остановил процессор и «Все пропало, шеф!!!». Не надо так. Если уж делаем, то делаем правильно. А как это — правильно? Не претендуя на премию Нобеля в сфере мониторинга, приведу с десяток непрошенных советов.
Рисунок 1. Определение FlowFile Expiration
Выставляем FlowFile Expiration на «разумно достаточное» время и получаем простой и удобный механизм оперативной отладки, не сильно рискуя остановить все и насовсем. См. Рис. 1.
Коли речь зашла об очередях, попробуем быстро и просто наладить мониторинг очередей самым простым способом. Выгружаем состояние всех очередей, в случае возникновения backpressure в любой из них – бежим разбираться, что именно сломалось. Для передачи данных воспользуемся встроенным в NiFi Site-to-Site Reporting Task, результат работы которого передадим самим себе через Remote Process Group и уже там обработаем – см. Рис. 2.
Рисунок 2. Site-to-Site Reporting Task
Обратите внимание, что хотя в настройках destinationUrl и указан :8080/nifi (Интерфейс), он используется только для согласования соединения, сама передача идет по порту 10000.
Рисунок 3. Фильтрация отчета с помощью queryrecord
Для парсинга используем процессор QueryRecord (никакого split-evaluate-route!). А с результатом в виде [{"sourceId":"cbffbfdc-017d-1000-9942-e797792a97a9","sourceName":"GenerateFlowFile"}], указывающем, что-где сломалось, уже можно работать.
3. Тем же образом можно настроить SiteToSiteBulletinReportTask – он будет направлять нам КАЖДОЕ сообщение об ошибках обработки (помните эти красные квадратики в углу, которые вечно не успеваешь заскриншотить?) с атрибутами flow-файла. Это на нагруженных (кривых) инстансах генерирует изрядно трафика, но тем больше у авторов flow поводов все быстрее починить.
Но все это, разумеется, «мониторинг с высоты птичьего полета», т.е. кластера целиком. А хотелось бы обработки ошибок на уровне самой группы процессоров. Ведь прежде чем сообщать об ошибке, необходимо попробовать её обработать на месте. Т.е. тихо дропнуть failure-ветку — таки нехорошо, да и сразу бить тревогу – не совсем правильно. А как правильно? Смотрим пункт 4.
4. Тот же InvokeHTTP помимо Failure предоставляет отдельный результат Retry. Это так называемый «восстановимый» класс ошибок. Конечно, если сервер вернул тебе 400 – «кривой запрос», то сам по себе он «прямым» не станет, а в случае ответа «50(0|2|3)» через N-секунд (перезапуск сервиса, запрос другому инстансу и пр.) результат может быть уже совсем другим. Проще (но не правильней) завернуть очередь Retry на сам процессор, но в случае со «сбоем по нагрузке» результат бесконечного числа обращений к сервису может оказаться далеким от желаемого. Тут на помощь приходит процессор ControlRate – смотрим на Рис. 4.
Рисунок 4. Простейшая обработка ошибок
Ждем N-секунд и повторяем отправку N flow-файлов. Для того, чтобы избежать бесконечного зацикливания кривых запросов, задаем параметр “FlowFile Expiration” для очереди. Минимум накладных расходов + условно приличный результат. Почему «условно»? Потому что если упало 100500 кривых запросов, возможен вариант, при котором вся очередь ошибок не успевает обработаться с учетом времени жизни flow-файлов и задержки. Т.е. при MaximumRate=10, Schedule=10 sec и FlowFile Expiration=60 sec мы обработаем 60 ошибок, все остальное тихо умрет. Разумеется, эти параметры можно и нужно двигать, но при данном подходе указанный риск будет всегда. Плюс в этой схеме мы никак не узнаем о том, что выполнение действия закончилось ошибкой. Если вам нужна более строгая обработка, то смотрим пункт 5.
Рисунок 5. Более сложный вариант
5. В UpdateAttribute ставим счетчик вида ${counter:replaceNull(0):plus(1)}, в RouteOnAttribute условие ${counter:ge(5)}. На выходе получаем фиксированное количество повторов для каждого flow-файла с генерацией аларма по неуспешности. Усложнение достаточно значительное (меньше – лучше, да?) но с учетом (в норме!) небольшого количества ошибок, оправданное. В случае большого потока, чтобы избежать backpressure со стороны потока обработки ошибок, возможно, имеет смысл увеличить размер соответствующих очередей.
А, да. Важно: 100% покрытие обработкой ошибок, как правило, не требуется примерно нигде. Делать 10 повторов через 10 секунд в случае, если пользователь ждет ответа на свой запрос в течение дефолтного таймаута в 60 секунд, не надо