Какой лучший шаблон связи для приложений на основе EJB3? - PullRequest
2 голосов
/ 18 марта 2010

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

  • несколько компонентов, управляемых сообщениями, отвечающих за различные части архитектуры
  • в каждый MDB вставлен Session Bean, обрабатывающий бизнес-логику
  • пара сущностей, предоставляющих доступ к постоянному слою
  • связь между различными частями архитектуры через концепцию запроса / ответа через сообщения JMS:
    • MDB получает сообщение, содержащее запрос активности
    • использует свой сессионный компонент для выполнения необходимой бизнес-логики
    • возвращает объект ответа в сообщении исходному запросчику

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

К сожалению, у нас огромные проблемы с концепцией запрос-ответ. Транзакции 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 () заставляет TestService отправлять сообщение в очередь XYZ и запрашивать ответ
    • TestService использует управляемые бином транзакции
    • TestService устанавливает соединение и сеанс с JMS-брокером через фабрику совместных соединений при инициализации (@PostConstruct)
    • TestService фиксирует транзакцию после отправки, затем начинает другую транзакцию и ожидает ответа в течение 10 секунд
  • Сообщение попадает в MDB 2 'LocationController', который использует (локальную) SLSB 2 'LocationService' для бизнес-логики
  • LocationController.onMessage () заставляет LocationService отправлять сообщение back в запрошенную очередь JMSReplyTo
    • Та же концепция BMT, та же концепция @PostConstruct
  • все используют одну и ту же фабрику соединений для доступа к брокеру

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

Я пытался без сообщенияВыбрать, без изменений, все еще без получения сообщения.

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

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"  />

1 Ответ

1 голос
/ 23 марта 2010

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

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

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

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

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

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

...