【问题标题】:JMS client that doesn't terminate不终止的 JMS 客户端
【发布时间】:2019-04-29 20:45:58
【问题描述】:

我们有一个 JMS 客户端需要处于空闲状态,直到它收到一条消息。当它收到一条消息时,它会执行一些功能,然后回到空闲状态。我的问题是,确保客户端保持正常运行的最佳方法是什么?让 JMS 客户端软件处理这个问题是一种好习惯,还是我们需要在主机上将软件作为服务运行(和/或做其他事情)?我们目前依赖 JMS 客户端软件,因为它似乎通过打开的连接保持线程处于活动状态,但我不确定这是否是最佳实践。我们使用 ActiveMQ 作为我们的消息代理和客户端软件。

编辑:添加代码示例

以下是客户端如何使用 JMS 客户端连接保持在线状态的示例:

import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnectionFactory;

public class JmsTestWithoutClose implements MessageListener {

    private final Connection connection;
    private final Session session;
    private final MessageConsumer consumer;

    public static void main(String[] args) throws JMSException {
        System.out.println("Starting...");
        JmsTestWithoutClose test = new JmsTestWithoutClose("<username>", "<password>", "tcp://<host>:<port>");
        // if you uncomment the line below, the program will terminate
        // if you keep it commented, the program will NOT terminate
        // test.close();
        System.out.println("Last line of main method...");
    }

    public JmsTestWithoutClose(String username, String password, String url) throws JMSException {
        // create connection and session
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(username, password, url);
        this.connection = factory.createConnection();
        connection.start();
        this.session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Destination destination = session.createTopic("Topic_Name");
        consumer = session.createConsumer(destination);
        consumer.setMessageListener(this);
    }

    public void close() throws JMSException {
        session.close();
        connection.close();
    }

    @Override
    public void onMessage(Message message) {
        // process the message
    }

}

【问题讨论】:

    标签: java jms activemq


    【解决方案1】:

    当前的客户端实现是否满足您的要求?如果是这样,我不会改变它。如果当前的客户端实现满足您的要求,那么我会更改它。仅仅为了遵循“最佳实践”而更改可以正常运行并满足您的需求(并且没有明显可预见的问题)的软件几乎肯定不是最佳的资源投资。

    应用程序的行为最终取决于应用程序本身。我看不到将应用程序作为服务运行或在应用程序外部执行任何其他操作实际上会迫使它在侦听/等待消息时保持正常运行。如果应用程序被编程,例如,使用 JMS API 并创建一个 MessageListener(即负责异步接收消息的类)然后退出而不实际等待消息,这将是一个错误。将这样的应用程序作为服务运行,以便操作系统在错误退出后继续重新启动它,这不是一个好的解决方案。

    拥有正确编写的客户端是最佳做法。用一些外部机制来支撑它是不好的做法。

    您提供的示例代码写得不好,有一些明显的问题:

    1. ConnectionSession 对象超出范围,这意味着它们永远无法正确关闭。最终,这些对象将被 JVM 垃圾收集。这是不恰当的资源管理。
    2. 应用程序没有完全终止的唯一原因是 ActiveMQ 5.x 客户端的实现方式(即打开的连接阻止 JVM 进程终止)。此实现细节不是 JMS API 提供的公共合同的一部分,因此不应依赖。如果您要使用不同的 JMS 客户端实现,例如ActiveMQ Artemis 核心 JMS 客户端,应用程序main() 退出后立即完全终止。

    要解决此问题,您的 main() 方法应在创建 JmsTestWithoutClose 实例后等待。在 Java 中有很多方法可以做到这一点:

    1. while 循环与Thread.sleep() 结合使用,可以根据需要修改循环条件以允许应用程序退出。
    2. 使用专为线程协调设计的对象,如java.util.concurrent.CountDownLatch。使用CountDownLatchmain() 方法可以调用await(),并且当应用程序完成处理消息时,MessageListener 实现可以调用countDown()
    3. 使用while 循环从控制台读取输入,并且仅在输入特殊字符串时继续(例如exit)。

    在任何情况下,应用程序都必须关闭它创建的所有资源(例如会话、连接等)。

    【讨论】:

    • 感谢您的回复。它满足了我们在 DEV 环境中的需求,我们将在其他环境中对其进行测试,但我们仍然不确定它是否被认为是最佳实践。一旦它们在其他环境中通过测试并且我们确认我们正在遵循最佳实践(或有充分的理由偏离最佳实践),我们希望将它们投入生产。
    • 我用一些额外的细节更新了我的答案。希望对您有所帮助。
    • 在您的更新中您写道,如果应用程序被编程为创建一个 MessageListener 然后退出而不实际等待消息,这将是一个错误。我的问题是关于那句话的最后一部分。等待消息的正确方法是什么?当我从 Eclipse 运行测试应用程序时,我的主要方法完成但进程没有终止(终止按钮仍处于启用状态),大概是因为我从未关闭连接。保持连接打开是保持程序运行和等待消息的好方法吗?
    • 我添加了一个代码示例来进一步解释我的意思,即使用 JMS 客户端连接来保持程序运行。看一下 main 方法中注释掉的“test.close()”上面的注释。谢谢!
    • 保持连接打开不是保持程序运行的好方法。我已经更新了我的答案,并提供了更多详细信息以进行澄清。
    猜你喜欢
    • 2010-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多