【问题标题】:spring boot jms - send and receive message inside @JmsListnerspring boot jms - 在@JmsListner 中发送和接收消息
【发布时间】:2020-11-16 18:53:40
【问题描述】:

QUEUE_INI 使用 IBM MQ9、最新的 spring boot 和 IBM 的 spring-boot-starter。

我想用@JmsListner 接收一条消息,然后不提交它,将另一条消息发送到另一个队列,接收响应,然后全部提交。

到目前为止,我还有下一个:

    @JmsListener(destination = QUEUE_IN, selector = "type='com.example.MainRequest")
    public void receiveMessage(Message message) throws JMSException {
        LOG.info("Received {} <{}>", QUEUE_IN, message);
        jmsTemplate2.convertAndSend(QUEUE_OUT, "SUB Request", requestMsg -> {
            requestMsg.setStringProperty("type", "com.example.SubRequest");
            return requestMsg;
        });
        LOG.info("Message sent");
        Message reply = jmsTemplate2.receiveSelected(QUEUE_IN, "type='com.example.SubResponse'");
        LOG.info("Received reply from {}: <{}>", QUEUE_IN, reply);
    }

我卡在“已发送消息”上。看起来子请求还没有真正发送。我在 MQ UI 中看到队列深度为 1,但里面没有消息,我的子请求侦听器也没有看到任何消息。

我也试过使用sendAndReceive 方法:

Message reply = jmsTemplate.sendAndReceive(QUEUE_OUT, session -> {
    Message msg = session.createTextMessage();
    msg.setStringProperty("type", "com.example.SubRequest");
    LOG.info("Sending msg: <{}> to {}", msg, QUEUE_OUT);
    return msg;
});

但我没有访问模型队列的权限。

有什么办法可以做到吗?

更新: 我在大家的共同帮助下完成了这项工作。我最终得到了单独的服务,只发送带有@Transactional(propagation = Propagation.REQUIRES_NEW) 的子请求。所有其他逻辑都保留在主侦听器中。

同时打开事务开始/结束日志也很有帮助:

logging:
  level:
    org.springframework.transaction.interceptor: trace

【问题讨论】:

  • 您能否在您的问题中提供一些澄清。 Model queue 表示动态队列,这是正确的,您是否使用动态队列作为输出消息。在您的代码中,您似乎正在为QUEUE_IN 收听receiveSelected 中的消息,那不应该是QUEUE_OUT 吗?我认为您应该在两组代码的模型队列上获得相同的不足权限。
  • @chughts 我拥有常规队列的权限。我将子请求发送到QUEUE_OUT。另一个服务将收到它并将回复发送回QUEUE_IN。所以是正确的(记录有错误)

标签: java spring-boot jms ibm-mq


【解决方案1】:

您的出站消息发送者与您的消息接收者在同一事务中注册。因此,出站消息的接收者在事务提交之前不会看到该消息。我认为您需要启动一个新事务来执行内部过程。

== 更新 ==

所以已经有一段时间了,我没有为此设置开发环境,但我建议这样。本质上,您将 Listener 和 Sender/Receiver 分成两个单独的类。 Sender/Receiver 应该被注入到你的监听器中,这样@Transactional 注释才会被尊重。

public class MyListener {
    private final MySender sender;

    public MyListener(MySender sender) {
        this.sender = sender;
    }

    @JmsListener(destination = QUEUE_IN, selector = "type='com.example.MainRequest")
    public void receiveMessage(Message message) throws JMSException {
        LOG.info("Received {} <{}>", QUEUE_IN, message);
        Message reply = sender.sendAndReceive()
        LOG.info("Received reply from {}: <{}>", QUEUE_IN, reply);
    }
}


public class MySender {
    private final JmsTemplate jmsTemplate2;
    private final Destination QUEUE_OUT;

    public MySender(JmsTemplate jmsTemplate2, Destination QUEUE_OUT) {
        this.jmsTemplate2 = jmsTemplate2;
        this.QUEUE_OUT = QUEUE_OUT;
    }

    @Transactional(propagation=Propagation.NESTED)  // Or REQUIRES_NEW, edepending on usage
    public Message sendAndReceive() throws JMSException {
        jmsTemplate2.convertAndSend(QUEUE_OUT, "SUB Request", requestMsg -> {
            requestMsg.setStringProperty("type", "com.example.SubRequest");
            return requestMsg;
        });
        LOG.info("Message sent");
        Message reply = jmsTemplate2.receiveSelected(QUEUE_IN, "type='com.example.SubResponse'");
        LOG.info("Received reply from {}: <{}>", QUEUE_IN, reply);
        return reply;
    }
}

@Transactional(propagation=Propagation.NESTED) 将启动一个新事务,如果一个已经在运行(并在方法退出时提交/回滚),因此您应该能够发送/接收消息并将其返回给您听众。

【讨论】:

  • 是的,我也这么认为。但是添加 TransactionManager bean 和 @Transactional 注释并没有多大帮助。你能扩大你的答案吗?我怎样才能做到这一点?
  • 正如@Nicholas 所说,您需要提交convertAndSend 才能让您的第二个应用程序看到消息并回复它。如果没有提交,则消息对其他应用程序不可见。
  • 尼古拉斯,你几乎是对的。我不得不把它分成 2 个单独的类。不知何故,既不添加 REQUIRED_NEW 也不添加 NESTED 在同一个班级对我不起作用。没有开始新的交易。但是有 2 节课它正在工作。但是,正如@richc 提到的,我还拆分了发送和接收以提交发送
  • 亚历山大;是的,带有注释的东西....如果该方法没有注入到您当前的 bean 中,则它没有经过预处理,因此本地方法调用(同一个类/对象实例中的一个方法到另一个方法)不尊重注释。我想可能有办法做到这一点,但它已经逃脱了我。
【解决方案2】:

很遗憾,由于我没有足够的权限,我无法对上一个答案添加评论。

正如@Nicholas 所说,您的消息接收与消息发送在同一事务中。在receiveMessage 方法完成之前,初始发送不会被提交到队列以供接收者使用。我假设您可能将接收配置为 RECEIVE_TIMEOUT_INDEFINITE_WAIT 之类的东西,然后会阻止该方法的完成。

使用此配置,您已经有一个绑定到QUEUE_IN 的消息侦听器。传递到该队列的消息将仅传递给一个消费者。我看到你正在使用选择器来避免这个问题。

一个选项可能是为 type = com.example.SubResponse 引入另一个消息侦听器并从第一个中删除阻塞接收。

【讨论】:

  • 谢谢。你说的对。我不得不将发送和接收分成不同的方法。但我也必须开始单独的发送交易。
猜你喜欢
  • 2018-10-13
  • 2014-12-30
  • 2017-01-19
  • 2020-04-05
  • 1970-01-01
  • 2011-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多