Разработка обработчиков Camel
Обработчик — класс с публичным конструктором без аргументов и одним или несколькими методами, отмеченными @From.
Класс необходимо отдельно зарегистрировать в camel.handler.<n>.class.
Минимальный обработчик
class OrderHandler {
@From(endpoint = "amq:queue:orders")
fun process(message: String) {
OrderService().process(message)
}
}
Метод должен иметь ровно один аргумент. Методы без аргументов и методы с двумя или более аргументами пропускаются при построении маршрутов. Метод должен быть доступен для рефлексивного вызова; на практике следует объявлять его публичным.
@From: входной endpoint
@From — единственная обязательная аннотация метода.
Endpoint можно записать готовой строкой:
@From(endpoint = "amq:queue:orders")
fun process(message: String) {
OrderService().process(message)
}
Поле endpoint сохранено для совместимости и помечено в исходном коде как устаревшее.
Для нового обработчика предпочтительна составная запись:
@From(
type = ComponentType.ACTIVEMQ,
path = [
From.PathParameter("queue"),
From.PathParameter("orders")
]
)
fun process(message: String) {
OrderService().process(message)
}
type-
Тип компонента. Сейчас доступен только
ComponentType.ACTIVEMQ, который создаёт схемуamq. path-
Части пути, соединяемые двоеточием. Пример выше создаёт
amq:queue:orders. query-
Параметры после
?, соединяемые через&.
Если одновременно заполнены endpoint и составные поля, используется endpoint.
Контекстные переменные endpoint
Входной endpoint обрабатывается шаблонизатором ERP.
Плагин передаёт переменную server:instanceId, что позволяет создать отдельную durable-подписку для каждого экземпляра:
@From(
type = ComponentType.ACTIVEMQ,
path = [
From.PathParameter("topic"),
From.PathParameter("notifications")
],
query = [
From.QueryParameter("clientId", "{{server:instanceId}}"),
From.QueryParameter("durableSubscriptionName", "{{server:instanceId}}")
]
)
fun process(message: String) {
NotificationService().process(message)
}
В результате получится endpoint вида:
amq:topic:notifications?clientId=erp-1&durableSubscriptionName=erp-1
Шаблонизация выполняется только для @From.
Значение @To.endpoint передаётся Camel без обработки контекстных переменных ERP.
Аргумент метода
Перед вызовом тело сообщения преобразуется в тип единственного аргумента.
| Тип аргумента | Поведение |
|---|---|
|
Строковое тело передаётся без JSON-десериализации. |
Типизированный объект |
Строка или массив байтов десериализуется выбранным transformer. |
Параметризованный тип |
Используется полный reflection-тип аргумента, поэтому поддерживаются, например, списки DTO. |
|
В метод передаётся исходный Camel Exchange без преобразования. |
Для обычного transformer входное тело должно быть строкой или массивом байтов. Другие типы приводят к ошибке преобразования.
Обработчик запускается внутри серверного контекста служебного пользователя ERP. Если для работы требуется соединение с БД, сервис или DAO должен получить его принятым в проекте способом; Camel сам транзакцию не открывает.
@To: выходной endpoint
@To отправляет возвращённое значение метода в указанный endpoint:
@From(endpoint = "amq:queue:uppercase")
@To(endpoint = "amq:topic:lowercase")
fun process(message: String): String = message.lowercase()
Метод с @To обязан возвращать значение.
Если возвращаемый тип — void/Unit, метод пропускается при регистрации маршрутов.
Без @To возвращённое значение игнорируется.
Перед отправкой результат сериализуется тем же transformer, который использовался для чтения.
Результат null устанавливает пустое тело сообщения.
@Transform: преобразование тела
Без аннотации используется GsonTransformer.
Он сериализует и десериализует JSON через общий экземпляр Gson.
Для Kotlin DTO и моделей с настройками Jackson следует указать:
@Transform(transformer = JacksonTransformer::class)
JacksonTransformer:
-
игнорирует неизвестные свойства;
-
поддерживает Kotlin-классы;
-
подставляет пустые коллекции и map для
null; -
сериализует даты как числовые метки времени;
-
использует часовой пояс JVM.
Можно реализовать собственный наследник Transformer.
У его класса должен быть публичный конструктор без аргументов.
@Throttle: ограничение скорости
@Throttle(messages = 100, period = 1, timeUnit = TimeUnit.SECONDS)
messages-
Максимальное количество сообщений за период.
period-
Размер периода. По умолчанию
1000. timeUnit-
Единица периода. По умолчанию миллисекунды.
Аннотация преобразуется в стандартный Camel throttler и применяется перед вызовом метода.
@Redelivery: повторная доставка и DLQ
@Redelivery(
attempts = 3,
deadLetterEndpoint = "amq:queue:orders.DLQ",
delayMillis = 1000,
backoffMultiplier = 2.0
)
| Параметр | По умолчанию | Назначение |
|---|---|---|
|
Обязательный |
Число дополнительных доставок после первой неудачной обработки. |
|
Обязательный |
Endpoint, куда Camel отправит сообщение после исчерпания повторов. |
|
|
Задержка между повторами в миллисекундах. |
|
|
Коэффициент увеличения задержки. |
|
Пустая строка |
Паттерн задержек Camel, например |
Перед отправкой в DLQ к сообщению добавляется заголовок error вида:
TransformationException: Deserialization failed: class java.lang.String -> class example.Order
Исключение, выброшенное методом, разворачивается из InvocationTargetException для текста заголовка и журнала.
Текущая реализация задаёт backoffMultiplier, но явно не включает режим exponential backoff. Если рост задержки критичен для сценария, его следует проверить интеграционным тестом на используемой версии Camel.
|
Порядок применения аннотаций
Маршрут строится в фиксированном порядке:
From → Redelivery → Throttle → Transform и вызов метода → To
Порядок аннотаций в исходном коде на это не влияет.
@Parameter
Аннотация @Parameter присутствует в пакете плагина, но текущий parser и route builder её не читают.
Она не влияет на аргумент метода или endpoint и не должна использоваться в новых обработчиках.
Регистрация и проверка
После добавления класса:
-
добавьте
camel.handler.<n>.classв конфигурацию Camel; -
сохраните конфигурацию либо перекомпилируйте зарегистрированный динамический класс;
-
проверьте строку
Добавлен обработчик: <полное имя класса>в журнале; -
отправьте валидное сообщение и проверьте побочный эффект;
-
отправьте ошибочное сообщение и проверьте повторную доставку и DLQ, если они настроены;
-
проверьте метрики
camel_processing_count_totalиcamel_processing_duration_milliseconds.
Аннотационные unit-тесты покрывают построение входного endpoint и подстановку server:instanceId.
Для нового прикладного обработчика необходимо отдельно тестировать разбор DTO, бизнес-логику и ошибочный сценарий.