如何暂时禁用消息监听器

18

如何暂时禁用消息监听器是一种好的方法?我想要解决的问题是:

  • 通过消息监听器接收了一个JMS消息
  • 在处理消息时出现错误
  • 等待我的系统再次准备好来处理该消息
  • 在我的系统准备好之前,不希望再有更多的消息,所以...
  • ...我想要禁用消息监听器
  • 我的系统又可以进行处理了
  • 处理失败的消息,并确认JMS消息已被处理
  • 启用消息监听器

目前,我正在使用 Sun 应用服务器。我通过将消息消费者设置为 null 来禁用消息监听器,并使用 setMessageListener(myOldMessageListener) 重新启用它,但此后我没有收到任何消息。

7个回答

15
如果在系统准备好处理消息之前不从onMessage()监听器方法返回,会怎么样?这将防止JMS在该消费者上传递另一条消息。这相当于同步情况下不调用receive()
对于给定的JMS会话,没有多线程,因此消息管道会被阻塞,直到onMessage()方法返回。
我不熟悉动态调用setMessageListener()的影响。 javadoc说如果在现有侦听器或同步使用者正在消耗消息时调用,则行为未定义。 如果您是从onMessage()内部调用,则似乎遇到了该未定义情况。
如果这对您来说不太粗糙,那么连接级别有start / stop方法。

哦,原来如此简单。我以为会有更多的线程调用onMessage方法。非常感谢! - davidi
连接是线程安全的。 下游会话(会话,消费者,生产者)不是线程安全的。 您的代码需要避免多线程访问。为了强制执行这一点,JMS 不能在一个消费者上多线程调用监听器。 - John M
3
我刚刚注意到JMS规范明确指定了此串行传递行为。它在JMS 1.0.2规范的第4.4.16节中。过去,我认为这仅仅是由线程规则隐含地暗示的。 - John M
我们可以使用Thread.sleep()直到问题得到解决吗?这是最好的设计吗? - Ravindra Devadiga

5

通过使用一个receive()循环替换消息监听器来解决问题,但我仍然对如何禁用消息监听器并在短时间内重新启用它感兴趣。


你好,你已经找到暂停消息监听器接收过程的解决方案了吗? - Dinesh Kumar

0
在 JBoss 中,以下代码可以解决问题:
   MBeanServer mbeanServer = MBeanServerLocator.locateJBoss();
    ObjectName objName = new ObjectName("jboss.j2ee:ear=MessageGateway.ear,jar=MessageGateway-EJB.jar,name=MessageSenderMDB,service=EJB3");
    JMSContainerInvokerMBean invoker = (JMSContainerInvokerMBean) MBeanProxy.get(JMSContainerInvokerMBean.class, objName, mbeanServer);

    invoker.stop(); //Stop MDB
    invoker.start(); //Start MDB

0

我认为你可以调用

messageConsumer.setMessageListener(null);

在您的MessageListener实现中,安排重新建立任务(例如在ScheduledExecutorService中)。此任务应调用

connection.stop();
messageConsumer.setMessageListener(YOUR_NEW_LISTENER);
connection.start();

它将会工作。start() 和 stop() 方法用于重新启动传递结构(而不是 TCP 连接)。

阅读 Javadoc https://docs.oracle.com/javaee/7/api/javax/jms/Connection.html#stop--

暂时停止连接的传入消息传递。可以使用连接的 start 方法重新启动传递。当连接停止时,所有连接的消息消费者的传递都被禁止:同步接收被阻塞,并且消息不会传递到消息侦听器。


0

在我看来,这似乎是消息已经被传递了,但是因为您没有附加侦听器,所以什么也没有发生。我已经有一段时间没有使用JMS了,但是难道您不想在修复系统时将消息发送到死信队列或其他地方,然后在准备好再次处理时将消息移回原始队列吗?


那可能是会发生的事情。不幸的是,我无法控制jms服务器,我所能做的就是指定一个队列来获取消息,没有死信队列。我想我可以停止QueueConnection,但这不能从messagelistener线程中执行。 - davidi
你为什么没有访问JMS服务器的权限呢?正如duffymo所指出的那样,你需要访问错误队列。否则,你将不得不在代码中复制JMS服务器为你提供的行为。 - tddmonkey
我在开发环境中有访问 JMS 服务器的权限,但我不能期望我正在开发的应用程序的用户(一个连接两个消息系统的桥梁)具有对 JMS 服务器相同的权限。那么死信队列是解决这些问题的常规方式吗? - davidi

0
在WebLogic中,您可以设置最大重试次数、错误队列来处理超过最大重试限制的消息以及其他参数。我不确定,但您也可能能够指定等待时间。所有这些都可以在管理控制台中使用。我建议查看您所拥有的JMS提供程序的管理控制台,看看它是否可以执行类似的操作。

0

为了暂时停止连接接收传入消息,您需要使用 Connection 接口的 stop() 方法:https://docs.oracle.com/javaee/7/api/javax/jms/Connection.html#stop--

只是不要在 MessageListener 中调用 connection.stop(),因为根据 JMS 规范,您将会得到死锁或异常。相反,您可以从另一个线程调用 connection.stop(),您只需要同步 MessageListener 和将要挂起连接的线程以及 connection.stop() 函数即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接