代码之家  ›  专栏  ›  技术社区  ›  Hank Jesus M C

基于EJB3的应用程序的最佳通信模式是什么?

  •  2
  • Hank Jesus M C  · 技术社区  · 15 年前

    我正在启动一个JavaEE项目,它需要强大的可扩展性。到目前为止,概念是:

    • 几个消息驱动的bean,负责体系结构的不同部分
    • 每个MDB都注入了一个会话bean,处理业务逻辑
    • 两个实体bean,提供对持久层的访问
    • 通过请求/回复概念通过JMS消息在体系结构的不同部分之间进行通信:
      • MDB接收包含活动请求的消息
      • 使用其会话bean执行必要的业务逻辑
      • 将消息中的响应对象返回给原始请求者

    其思想是,通过消息总线将体系结构的各个部分相互分离,对可伸缩性没有限制。只需启动更多的组件——只要它们连接到同一总线,我们就可以增长。

    不幸的是,我们在请求-回复概念上遇到了巨大的问题。交易管理似乎在我们的方式很多。会话bean不应该使用消息的接缝?!

    阅读 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使用bean管理的事务
      • testservice在初始化时通过联合连接工厂建立与JMS代理的连接和会话(@postconstruct)
      • testservice在发送后提交事务,然后开始另一个事务,并等待10秒的响应
    • 消息获取到MDB 2“locationController”,后者将(本地)SLSB 2“locationService”用于业务逻辑
    • locationController.onMessage()使locationService发送消息 后面 到请求的jmsreplyto队列
      • 相同的BMT概念,相同的@postconstruct概念
    • 都使用同一个连接工厂访问代理

    问题:第一条消息(由SLSB 1发送)和接收(由MDB 2接收)正常。返回消息的发送(由SLSB 2)也可以。然而, SLSB 1从未收到任何东西 -只是超时了。

    我尝试不使用messageselector,不更改,仍然没有接收消息。

    会话bean是否可以使用消息?

    slsb 1-测试服务.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 - LooService .java(只有应答方法,REST与上面相同)

    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"  />
    
    1 回复  |  直到 12 年前
        1
  •  1
  •   ewernli    15 年前

    即使使用JMS,请求/应答模式仍然是 同步 本质上。调用者发送消息,然后等待答复。这不仅因为分布式事务而复杂,而且意味着在等待回复时,会分配和浪费一个或多个资源(在本例中至少是线程)。您不能这样缩放:您本质上受到线程数量的限制。

    要拥有一个真正可扩展的JMS体系结构,一切都必须 异步 . 换言之:你不应该等待。发送和接收的消息应该传递必要的信息以触发下一个活动。

    如果消息的大小太大,则只能存储标识符并将相应的数据存储在数据库中。但随后数据库又变成了一个争用点。

    如果不同的消息需要知道它们在哪个长期运行的过程中参与,您还可以使用 相关标识符 . 当收到消息时,接收可以使用相关标识符“恢复”长时间运行的活动。这是传统的BPEL模式。同步请求/应答和具有相关标识符的异步消息的主要区别在于,可以在每个步骤之间释放资源。你可以用后者缩放,但不能用第一个缩放。

    老实说,我对你的长篇文章感到困惑,不知道你的设计是异步的(和正确的),还是与请求/回复同步的(和有问题的)。但我希望我能提供一些答案。

    无论如何,请访问网站 Enterprise Integration Patterns 它是一个有价值的信息来源。