Одновременный синхронный запрос-ответ с JMS / ActiveMQ-паттернами / библиотеками?


У меня есть веб-приложение, в котором, когда пользователь отправляет запрос, мы отправляем сообщение JMS удаленной службе, а затем ждем ответа. (Есть также асинхронные запросы, и у нас есть различные тонкости, настроенные для воспроизведения сообщений и т. д., Поэтому мы предпочли бы придерживаться JMS вместо, скажем, HTTP)

В Как я должен реализовать ответ на запрос с помощью JMS?, ActiveMQ, по-видимому, не поощряет идею либо временных очередей на запрос, либо временных потребителей с селекторами на JMSCorrelationID, из-за накладных расходов, связанных с их вращением.

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

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

Эта страница ActiveMQ рекомендует Lingo , который не обновлялся с 2006 года, иCamel Spring Remoting , который был адски обработан моей командой за свои многочисленные ошибки gotcha.

Есть ли лучшее решение в виде библиотеки, реализующей этот шаблон, или в виде другого шаблона для моделирования синхронного запроса-ответа по JMS?


Связанный таким образом вопрос:

5 23

5 ответов:

В прошлом проекте у нас была похожая ситуация, когда запрос sync WS обрабатывался с помощью пары асинхронных сообщений req/res JMS. Мы использовали JBoss JMS impl в то время и временный destinations, где большие накладные расходы.

Мы закончили тем, что написали потокобезопасный диспетчер, оставив WS ждать, пока не придет ответ JMS. Мы использовали CorrelationID, чтобы сопоставить ответ с запросом.

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

BlockingMap

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

Я бы все равно подумал об использовании Camel и позволил ему обрабатывать резьбу, возможно, без пружинного удаления, но просто необработанные пластины ProducerTemplates.

Camel имеет хорошую документацию по этой теме и очень хорошо работает с ActiveMQ. http://camel.apache.org/jms#JMS-RequestreplyoverJMS

Что касается вашего вопроса о раскручивании потребителя на основе селектора и накладных расходов, то в документах ActiveMQ фактически говорится, что для этого требуется обратный путь к брокеру ActiveMQ, который может быть, на другом конце земного шара или в сети с высокой задержкой. Накладные расходы в этом случае-это время обратной связи TCP/IP с брокером AMQ. Я бы рассматривал это как вариант. Использовали его много раз с успехом.

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

Тем не менее, все это упражнение заставляет меня переосмыслить JMS vs HTTP... :)

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

Http://www.eaipatterns.com/RequestReplyJmsExample.html имеет решения основного потока tow с использованием replyToQueue или correlationID.

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

Мы реализовали очень похожий вариант использования с Hazelcast, являющимся нашим шасси для сканирующие междоузлия кластера. Суть состоит из 2 наборов данных: 1 распределенная карта для ответов, 1 "локальный" список ожидающих ответа (на каждом узле кластера).

  • каждый запрос (получающий свой собственный поток от Jetty) создает запись в карте локальные ожидатели; запись, очевидно, имеет корреляционный UID и объект, который будет служить семафором
  • затем запрос отправляется на удаленный сервер (REST/JMS), и исходный поток начинает ждать на семафоре; UID должен быть частью запроса
  • remote возвращает ответ и записывает его в карту ответов с коррелированным UID
  • карта ответов прослушивается; если UID вновь поступающего ответа найден на карте местных ожидающих, то это семафор получает уведомление, поток исходного запроса освобождается, забирая ответ из карты ответов и возвращая его клиенту

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