Отладка API Java Message Service с использованием Lightrun

API Java Message Service (JMS) был разработан Sun Microsystems во времена Java EE. Он предоставляет простые абстракции для обмена сообщениями, в том числе производителя сообщений (Message Producer), потребителя сообщений (Message Consumer) и т.д.

API JMS дает возможность помещать сообщение в “очередь” и получать сообщения, помещенные в указанную очередь. Это полезно для систем с высокой пропускной способностью: вместо того чтобы тратить время пользователя на выполнение медленной операции в режиме реального времени, корпоративное приложение может отправить сообщение. Такой неблокирующий подход обеспечивает высокую пропускную способность, поддерживая надежность в большом масштабе.

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

Мы можем рассматривать обмен сообщениями в некотором роде как почтовую рассылку по сообществу. Вы отправляете сообщение на адрес электронной почты, который олицетворяет собой некий список. Каждый, кто подписывается на этот список, получает данное сообщение. В этом случае тема сообщения  —  это адрес списка рассылки сообщества. Вы можете отправить ему сообщение, и обработчик Java Message Service с помощью прослушивателя сообщений получит указанное событие.

Важно отметить, что в JMS существуют две модели обмена сообщениями: модель публикации и подписки (которую мы обсуждали здесь), а также обмен сообщениями “точка-точка”, который позволяет отправлять сообщение определенному адресату.

Рассмотрим короткий пример.

Простая демонстрация

Чтобы отладить вызовы Java Message Service, я создал простое демонстрационное приложение, исходный код которого можно найти здесь.

Эта демонстрация JMS представляет собой простой API ведения журнала базы данных  —  это микросервис, который вы можете использовать для помещения в журнал записи, которая затем асинхронно записывается в базу данных. RESTful-приложения могут затем воспользоваться этим API логирования в базу данных для того, чтобы дополнять журнал базы данных без дополнительных затрат на доступ к самой базе.

Этот код реализует основной веб-сервис:

@RestController
@RequiredArgsConstructor
public class EventRequest {
private final JmsTemplate jmsTemplate;
private final EventService eventService;
private final Moshi moshi = new Moshi.Builder().build();

@PostMapping("/add")
public void event(@RequestBody EventDTO event) {
String json = moshi.adapter(EventDTO.class).toJson(event);
jmsTemplate.send("event", session ->
session.createTextMessage(json));
}

@GetMapping("/list")
public List<EventDTO> listEvents() {
return eventService.listEvents();
}
}

Обратите внимание на метод event(), который отправляет сообщение в тему события. До сих пор я не упоминал тела сообщений, чтобы упростить задачу, но в этом случае я просто передаю в качестве тела строковый JSON. Хотя JMS поддерживает сериализацию объектов, ее использование имеет свои сложности, и я хочу, чтобы мой код оставался простым.

Чтобы дополнить основной веб-сервис, нужно создать прослушиватель, который обрабатывает входящее сообщение:

@Component
@RequiredArgsConstructor
public class EventListener {
private final EventService eventService;

private final Moshi moshi = new Moshi.Builder().build();

@JmsListener(destination = "event")
public void handleMessage(String eventDTOJSON) throws IOException {

eventService.storeEvent(moshi.adapter(EventDTO.class).fromJson(eventDTOJSON));
}
}

Прослушиватель вызывается отправленной к нему строкой JSON: мы анализируем ее и пересылаем в сервис.

Отладка скрытого кода

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

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

Вы можете размещать действия Lightrun (снапшоты, логирование и т.д.) непосредственно внутри API платформы и реализации сервисов обмена сообщениями. Это позволяет определить, работают ли селекторы сообщений должным образом и запущен ли прослушиватель сообщений.

Со Spring и поддержкой JMS, как показано выше, мы можем открыть JmsTemplate и добавить снапшот непосредственно в методе execute:

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

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

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

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

Заключение

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

Lightrun позволяет поместить действия на разных уровнях приложения. Это помогает сузить круг потенциальных проблем независимо от стандарта обмена сообщениями и платформы. И все это  —  на бесплатном аккаунте.

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Shai Almog: Debugging the Java Message Service (JMS) API using Lightrun

Предыдущая статья25 основных вопросов для собеседования с Android-разработчиком. Часть 1
Следующая статья9 проектов, которые помогут стать фронтенд-мастером в 2023 году