Pergunta

Estou iniciando um projeto Java EE que precisa ser fortemente escalável.Até agora, o conceito era:

  • vários Message Driven Beans, responsáveis ​​por diferentes partes da arquitetura
  • cada MDB possui um Session Bean injetado, cuidando da lógica de negócio
  • alguns Entity Beans, fornecendo acesso à camada de persistência
  • comunicação entre as diferentes partes da arquitetura via conceito de Solicitação/Resposta via mensagens JMS:
    • MDB recebe mensagem contendo solicitação de atividade
    • usa seu bean de sessão para executar a lógica de negócios necessária
    • retorna objeto de resposta em mensagem para o solicitante original

A ideia era que, ao desacoplar partes da arquitetura umas das outras através do barramento de mensagens, não houvesse limite para a escalabilidade.Basta iniciar mais componentes - desde que estejam conectados ao mesmo barramento, podemos crescer cada vez mais.

Infelizmente, estamos tendo enormes problemas com o conceito de solicitação-resposta.O Transaction Mgmt parece estar bastante no nosso caminho.Parece que os beans de sessão não deveriam consumir mensagens?!

Leitura http://blogs.oracle.com/fkieviet/entry/request_reply_from_an_ejb e http://forums.sun.com/message.jspa?messageID=10338789, tenho a sensação de que as pessoas realmente recomendam contra o conceito de solicitação/resposta para EJBs.

Se for esse o caso, como fazer você se comunica entre seus EJBs?(Lembre-se, escalabilidade é o que procuro)

Detalhes da minha configuração atual:

  • MDB 1 'TestController', usa SLSB 1 'TestService' (local) para lógica de negócios
  • TestController.onMessage() faz TestService enviar uma mensagem para a fila XYZ e solicitar uma resposta
    • TestService usa transações gerenciadas por Bean
    • TestService estabelece uma conexão e sessão com o corretor JMS por meio de uma connection factory conjunta na inicialização (@PostConstruct)
    • TestService confirma a transação após o envio, inicia outra transação e aguarda 10 segundos pela resposta
  • A mensagem chega ao MDB 2 'LocationController', que usa SLSB 2 'LocationService' (local) para lógica de negócios
  • LocationController.onMessage() faz com que LocationService envie uma mensagem voltar para a fila JMSReplyTo solicitada
    • Mesmo conceito BMT, mesmo conceito @PostConstruct
  • todos usam a mesma connection factory para acessar o broker

Problema:A primeira mensagem é enviada (pelo SLSB 1) e recebida (pelo MDB 2) ok.O envio da mensagem de retorno (pelo SLSB 2) também está correto.No entanto, SLSB 1 nunca recebe nada - simplesmente expira.

Tentei sem o messageSelector, sem alteração, ainda sem recebimento de mensagem.

Não é correto consumir mensagens por um bean de sessão?

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 (apenas o método de resposta, o restante é igual ao acima)

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;
}

recursos solares.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"  />
Foi útil?

Solução

O padrão de solicitação/resposta, mesmo usando JMS, ainda é síncrono em essência.O chamador envia uma mensagem e aguarda a resposta.Isto não só é complicado por causa das transações distribuídas, mas também significa que enquanto se espera pela resposta, um ou vários recursos (pelo menos neste caso o thread) são alocados e desperdiçados.Você não pode escalar desta forma:você está inerentemente limitado pelo número de threads.

Para ter uma arquitetura JMS verdadeiramente escalável, tudo deve ser assíncrono.Em outro termo:você nunca deve esperar.A mensagem enviada e recebida deve passar as informações necessárias para acionar a próxima atividade.

Se o tamanho da mensagem for muito grande, você poderá armazenar apenas um identificador e armazenar os dados correspondentes em um banco de dados.Mas então o banco de dados se torna novamente um ponto de discórdia.

Se mensagens diferentes precisarem saber de qual processo de longa duração elas participam, você também poderá usar identificadores de correlação.Quando uma mensagem é recebida, o receptor pode “retomar” a atividade de longa duração usando o identificador de correlação.Esse é um padrão tradicional do BPEL.A principal diferença entre solicitação/resposta síncrona e mensagem assíncrona com identificador de correlação é que os recursos podem ser liberados entre cada etapa.Você pode escalar com o último, mas não com o primeiro.

Para ser sincero, fiquei confuso com sua longa postagem e não entendi se seu design era bastante assíncrono (e correto) ou síncrono com solicitação/resposta (e problemático).Mas espero ter fornecido algum elemento de resposta.

De qualquer forma, visite o site Padrões de integração empresarial, é uma fonte valiosa de informações.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top