2 votes

Quel est le meilleur modèle de communication pour les applications basées sur EJB3 ?

Je lance un projet Java EE qui doit être fortement évolutif. Jusqu'à présent, le concept était :

  • plusieurs "Message Driven Beans", responsables de différentes parties de l'architecture
  • un Session Bean est injecté dans chaque MDB pour gérer la logique métier.
  • un couple d'Entity Beans, fournissant un accès à la couche de persistance
  • communication entre les différentes parties de l'architecture via le concept de demande/réponse par le biais de messages JMS :
    • La MDB reçoit un message contenant une demande d'activité
    • utilise son bean de session pour exécuter la logique commerciale nécessaire.
    • renvoie l'objet de la réponse dans le msg au demandeur original

L'idée était qu'en découplant les parties de l'architecture les unes des autres via le bus de messages, il n'y a aucune limite à l'évolutivité. Il suffit de lancer d'autres composants - tant qu'ils sont connectés au même bus, nous pouvons croître et croître encore.

Malheureusement, nous avons de gros problèmes avec le concept de demande-réponse. Transaction Mgmt semble être dans notre chemin beaucoup. Il semble que les beans de session ne sont pas censés consommer des messages ?!

Lecture http://blogs.oracle.com/fkieviet/entry/request_reply_from_an_ejb y http://forums.sun.com/message.jspa?messageID=10338789 j'ai l'impression que les gens recommandent contre le concept de demande/réponse pour les EJB.

Si c'est le cas, comment faire vous communiquez entre vos EJBs ? (N'oubliez pas que c'est l'évolutivité qui m'intéresse).

Détails de mon installation actuelle :

  • MDB 1 'TestController', utilise (local) SLSB 1 'TestService' pour la logique d'entreprise.
  • TestController.onMessage() fait en sorte que TestService envoie un message à la file d'attente XYZ et demande une réponse.
    • TestService utilise les transactions gérées par Bean
    • TestService établit une connexion et une session avec le broker JMS via une usine de connexion commune lors de l'initialisation (@PostConstruct).
    • TestService commet la transaction après l'envoi, puis commence une autre transaction et attend la réponse pendant 10 secondes.
  • Le message est transmis à la MDB 2 "LocationController", qui utilise le SLSB 2 "LocationService" (local) pour la logique commerciale.
  • LocationController.onMessage() permet au LocationService d'envoyer un message. arrière à la file d'attente JMSReplyTo demandée
    • Même concept BMT, même concept @PostConstruct
  • utilisent tous la même fabrique de connexion pour accéder au courtier

Problème : Le premier message est envoyé (par SLSB 1) et reçu (par MDB 2) sans problème. L'envoi du message de retour (par SLSB 2) est également correct. Cependant, SLSB 1 ne reçoit jamais rien - ça s'arrête.

J'ai essayé sans le messageSelector, aucun changement, toujours pas de réception de message.

N'est-il pas possible de consommer un message par un bean de session ?

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 (seulement la méthode de réponse, le reste est identique au précédent)

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

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

1voto

ewernli Points 23180

Le modèle de demande/réponse, même s'il utilise JMS, est toujours synchrone en substance. L'appelant envoie un message, puis attend la réponse. Cela n'est pas seulement compliqué à cause des transactions distribuées, mais cela signifie aussi que pendant l'attente de la réponse, une ou plusieurs ressources (le thread au moins dans ce cas) sont allouées et gaspillées. Vous ne pouvez pas évoluer de cette manière : vous êtes intrinsèquement limité par le nombre de threads.

Pour avoir une architecture JMS réellement évolutive, tout doit être asynchrone . En d'autres termes : vous ne devez jamais attendre. Le message envoyé et reçu doit transmettre les informations nécessaires pour déclencher l'activité suivante.

Si la taille du message est trop importante, vous pouvez ne conserver qu'un identifiant et stocker les données correspondantes dans une base de données. Mais la base de données devient alors à nouveau un point de discorde.

Si différents messages doivent savoir à quel processus de longue durée ils participent, vous pouvez également utiliser la fonction identificateurs de corrélation . Lorsqu'un message est reçu, le destinataire peut "reprendre" l'activité en cours depuis longtemps en utilisant l'identifiant de corrélation. C'est un modèle traditionnel de BPEL. La principale différence entre la demande/réponse synchrone et le message asynchrone avec identifiant de corrélation est que les ressources peuvent être libérées entre chaque étape. Vous pouvez évoluer avec la dernière étape, mais pas avec la première.

Pour être honnête, j'étais confus avec votre long post et je n'ai pas compris si votre conception était plutôt asynchrone (et correcte), ou synchrone avec requête/réponse (et problématique). Mais j'espère avoir apporté un élément de réponse.

En tout cas, allez visiter le site Modèles d'intégration d'entreprise c'est une source d'information précieuse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X