Каков наилучший шаблон связи для приложений на основе EJB3?

Я начинаю проект Java EE, который должен быть сильно масштабируемым. До сих пор концепция была такой:

  • несколько компонентов, управляемых сообщениями, отвечающих за разные части архитектуры
  • каждый MDB имеет внедренный сеансовый компонент, обрабатывающий бизнес-логику
  • пара Entity Beans, обеспечивающая доступ к слою сохранения
  • communication between the different parts of the architecture via Request/Reply concept via JMS messages:
    • MDB receives msg containing activity request
    • использует свой сессионный компонент для выполнения необходимой бизнес-логики
    • возвращает объект ответа в msg исходному запрашивающему

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

К сожалению, у нас серьезные проблемы с концепцией запрос-ответ. Transaction Mgmt, кажется, мешает нам. Похоже, сеансовые компоненты не должны потреблять сообщения?!

Чтение http://blogs.oracle.com/fkieviet/entry/request_reply_from_an_ejb и http://forums.sun.com/message.jspa?messageID=10338789 , я чувствую, что люди на самом деле рекомендуют против концепцию запроса/ответа для EJB.

Если это так, то как сделать обмен данными между вашими EJB? (Помните, мне нужна масштабируемость)

Подробная информация о моей текущей настройке:

  • MDB 1 «TestController», использует (локальный) SLSB 1 «TestService» для бизнес-логики
  • TestController.onMessage() makes TestService send a message to queue XYZ and requests a reply
    • TestService uses Bean Managed Transactions
    • TestService устанавливает соединение и сеанс с брокером JMS через совместную фабрику соединений при инициализации (@PostConstruct)
    • TestService фиксирует транзакцию после отправки, затем начинает другую транзакцию и ждет ответа 10 секунд.
  • Сообщение попадает в MDB 2 «LocationController», который использует (локальный) SLSB 2 «LocationService» для бизнес-логики.
  • LocationController.onMessage() makes LocationService send a message back to the requested JMSReplyTo queue
    • Same BMT concept, same @PostConstruct concept
  • все используют одну и ту же фабрику соединений для доступа к брокеру

Проблема: первое сообщение отправляется (SLSB 1) и принимается (MDB 2) нормально. Отправка ответного сообщения (посредством SLSB 2) также работает нормально. Однако SLSB 1 никогда ничего не получает — просто истекает время ожидания.

Я пробовал без messageSelector, без изменений, по-прежнему не получаю сообщения.

Разве нельзя использовать сообщение сеансовым компонентом?

SLSB 1 — TestService.java

@Resource(name = "jms/mvs.MVSControllerFactory")
private javax.jms.ConnectionFactory connectionFactory;

@PostConstruct
public void initialize() {
    try {
      jmsConnection = connectionFactory.createConnection();
      session = jmsConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
      System.out.println("Connection to JMS Provider established");
    } catch (Exception e) { }
}

public Serializable sendMessageWithResponse(Destination reqDest, Destination respDest, Serializable request) {
    Serializable response = null;

    try {
        utx.begin();
        Random rand = new Random();
        String correlationId = rand.nextLong() + "-" + (new Date()).getTime();

        // prepare the sending message object
        ObjectMessage reqMsg = session.createObjectMessage();
        reqMsg.setObject(request);
        reqMsg.setJMSReplyTo(respDest);
        reqMsg.setJMSCorrelationID(correlationId);

        // prepare the publishers and subscribers
        MessageProducer producer = session.createProducer(reqDest);

        // send the message
        producer.send(reqMsg);
        System.out.println("Request Message has been sent!");
        utx.commit();

        // need to start second transaction, otherwise the first msg never gets sent
        utx.begin();
        MessageConsumer consumer = session.createConsumer(respDest, "JMSCorrelationID = '" + correlationId + "'");
        jmsConnection.start();
        ObjectMessage respMsg = (ObjectMessage) consumer.receive(10000L);
        utx.commit();

        if (respMsg != null) {
            response = respMsg.getObject();
            System.out.println("Response Message has been received!");
        } else {
            // timeout waiting for response
            System.out.println("Timeout waiting for response!");
        }

    } catch (Exception e) { }

    return response;
}

SLSB 2 — LocationService.Java (только метод ответа, остальное такое же, как указано выше)

public boolean reply(Message origMsg, Serializable o) {
    boolean rc = false;

    try {
        // check if we have necessary correlationID and replyTo destination
        if (!origMsg.getJMSCorrelationID().equals("") && (origMsg.getJMSReplyTo() != null)) {
            // prepare the payload
            utx.begin();
            ObjectMessage msg = session.createObjectMessage();
            msg.setObject(o);

            // make it a response
            msg.setJMSCorrelationID(origMsg.getJMSCorrelationID());
            Destination dest = origMsg.getJMSReplyTo();

            // send it
            MessageProducer producer = session.createProducer(dest);
            producer.send(msg);
            producer.close();
            System.out.println("Reply Message has been sent");
            utx.commit();

            rc = true;
        }

    } catch (Exception e) {}

    return rc;
}

солнце-resources.xml

<admin-object-resource enabled="true" jndi-name="jms/mvs.LocationControllerRequest"  res-type="javax.jms.Queue"  res-adapter="jmsra">
    <property name="Name" value="mvs.LocationControllerRequestQueue"/>
</admin-object-resource>
<admin-object-resource enabled="true" jndi-name="jms/mvs.LocationControllerResponse"  res-type="javax.jms.Queue"  res-adapter="jmsra">
    <property name="Name" value="mvs.LocationControllerResponseQueue"/>
</admin-object-resource>

<connector-connection-pool name="jms/mvs.MVSControllerFactoryPool"  connection-definition-name="javax.jms.QueueConnectionFactory"  resource-adapter-name="jmsra"/>
<connector-resource enabled="true" jndi-name="jms/mvs.MVSControllerFactory" pool-name="jms/mvs.MVSControllerFactoryPool"  />

person Hank    schedule 18.03.2010    source источник
comment
Какую корпоративную служебную шину (ESB) вы используете? Стеклянная рыба? Как у вас все настроено в ESB?   -  person S.Lott    schedule 18.03.2010
comment
Пожалуйста, не комментируйте вопрос, которым владеете. Пожалуйста, ОБНОВИТЕ свой вопрос с дополнительными фактами. Тогда удалите свой комментарий. Пожалуйста, подробно опишите конфигурацию вашей стеклянной рыбы, так как это звучит как ваше узкое место.   -  person S.Lott    schedule 18.03.2010
comment
@Hank: Проблема: первое сообщение отправляется (SLSB 1) и принимается (MDB 2) в порядке. Отправка ответного сообщения (посредством SLSB 2) также работает нормально. Однако SLSB 1 никогда ничего не получает — просто истекает время ожидания. Так это реальный вопрос? Возможно, вам следует исправить заголовок и удалить предварительные данные и сосредоточиться только на этом вопросе, чтобы всем было очевидно, в чем ваша проблема.   -  person S.Lott    schedule 19.03.2010
comment
@Steven: Мой вопрос остается в соответствии с заголовком - какой лучший шаблон связи для больших масштабируемых приложений JEE. Я рад отказаться от части кода (и переместить ее в другую тему), но вы просили подробности... :) Я рад услышать ваши предложения.   -  person Hank    schedule 19.03.2010


Ответы (1)


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

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

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

Если разным сообщениям необходимо знать, в каком длительном процессе они участвуют, вы также можете использовать идентификаторы корреляции. Когда сообщение получено, получатель может «возобновить» длительную активность, используя идентификатор корреляции. Это традиционный шаблон для BPEL. Основное различие между синхронным запросом/ответом и асинхронным сообщением с идентификатором корреляции заключается в том, что ресурсы могут освобождаться между каждым шагом. Вы можете масштабировать с более поздним, но не с первым.

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

В любом случае посетите веб-сайт Шаблоны корпоративной интеграции, это ценный источник информации.

person ewernli    schedule 23.03.2010
comment
Спасибо, понял. И в этом случае я бы заставил MDB получить ответное сообщение (и определить, что это был за запрос, и продолжить оттуда), верно? Не сеансовый компонент, который заблокирован до тех пор, пока не получит ответ... - person Hank; 23.03.2010