Spring 如何使用JmsTemplate进行手动确认并从Rabbitmq队列中删除消息

Spring 如何使用JmsTemplate进行手动确认并从Rabbitmq队列中删除消息,spring,spring-boot,rabbitmq,spring-jms,Spring,Spring Boot,Rabbitmq,Spring Jms,我将RabbitMq(带JMS)与jmsTemplate一起使用,我能够使用来自RabbitMq队列的消息,但它会自动进行确认 我有它的搜索API,但无法找到它 如何设置手动确认 在下面的代码中,当消息从队列中被使用时,我想用该消息调用web服务,并且取决于我想从队列中删除该消息的响应。 我已经创建了一个项目,其中我使用Listener和另一个带有调用的项目从队列中读取消息 第一个项目: package com.es.jms.listener; import javax.jms.Connect

我将RabbitMq(带JMS)与jmsTemplate一起使用,我能够使用来自RabbitMq队列的消息,但它会自动进行确认

我有它的搜索API,但无法找到它

如何设置手动确认

在下面的代码中,当消息从队列中被使用时,我想用该消息调用web服务,并且取决于我想从队列中删除该消息的响应。 我已经创建了一个项目,其中我使用Listener和另一个带有调用的项目从队列中读取消息

第一个项目:

package com.es.jms.listener;

import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.listener.MessageListenerContainer;
import org.springframework.jms.listener.SimpleMessageListenerContainer;

import com.rabbitmq.jms.admin.RMQConnectionFactory;

@Configuration
public class RabbitMqMessageListener {

    @Bean
    public ConnectionFactory jmsConnectionFactory() {
        RMQConnectionFactory connectionFactory = new RMQConnectionFactory();
        connectionFactory.setUsername("Username");
        connectionFactory.setPassword("Password");
        connectionFactory.setVirtualHost("vhostname");
        connectionFactory.setHost("hostname");

        return connectionFactory;
    }

    @Bean
    public MessageListener msgListener() {
        return new MessageListener() {
            public void onMessage(Message message) {

                System.out.println(message.toString());
                if (message instanceof TextMessage) {
                    try {
                        String msg = ((TextMessage) message).getText();
                        System.out.println("Received message: " + msg);

                        // call web service here and depends on web service
                        // response
                        // if 200 then delete msg from queue else keep msg in
                        // queue

                    } catch (JMSException ex) {
                        throw new RuntimeException(ex);
                    }
                }

            }
        };
    }

    @Bean
    public MessageListenerContainer messageListenerContainer() {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
        container.setConnectionFactory(jmsConnectionFactory());
        container.setDestinationName("test");

        container.setMessageListener(msgListener());
        return container;

    }
}
第二个项目:

package com.rabbitmq.jms.consumer.controller;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

import javax.jms.ConnectionFactory;

import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jms.JmsException;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;


import com.rabbitmq.jms.admin.RMQConnectionFactory;

import redis.clients.jedis.Jedis;

@Controller
public class ReceiverController {
    @Autowired
    JmsTemplate jmsTemplate;


    @Bean
    public ConnectionFactory jmsConnectionFactory() {
        RMQConnectionFactory connectionFactory = new RMQConnectionFactory();
        connectionFactory.setUsername("Username");
        connectionFactory.setPassword("Password");
        connectionFactory.setVirtualHost("vhostname");
        connectionFactory.setHost("hostname");

        return connectionFactory;
    }

    @CrossOrigin
    @SuppressWarnings({ "unchecked", "rawtypes" })
    @RequestMapping(method = RequestMethod.GET, value = "/getdata")
    @ResponseBody
    public ResponseEntity<String> fecthDataFromRedis()
            throws JSONException, InterruptedException, JmsException, ExecutionException, TimeoutException {
        System.out.println("in controller");

        jmsTemplate.setReceiveTimeout(500L);
        // jmsTemplate.
        String message = (String) jmsTemplate.receiveAndConvert("test");

                    // call web service here and depends on web service
                    // response
                    // if 200 then delete msg from queue else keep msg in
                    // queue
        System.out.println(message);

        }

        return new ResponseEntity(message , HttpStatus.OK);

    }

}
包com.rabbitmq.jms.consumer.controller;
导入java.util.concurrent.ExecutionException;
导入java.util.concurrent.TimeoutException;
导入javax.jms.ConnectionFactory;
导入org.json.JSONException;
导入org.json.JSONObject;
导入org.springframework.beans.factory.annotation.Autowired;
导入org.springframework.context.annotation.Bean;
导入org.springframework.http.HttpStatus;
导入org.springframework.http.ResponseEntity;
导入org.springframework.jms.jmsceception;
导入org.springframework.jms.core.JmsTemplate;
导入org.springframework.stereotype.Controller;
导入org.springframework.web.bind.annotation.CrossOrigin;
导入org.springframework.web.bind.annotation.RequestMapping;
导入org.springframework.web.bind.annotation.RequestMethod;
导入org.springframework.web.bind.annotation.ResponseBody;
导入com.rabbitmq.jms.admin.RMQConnectionFactory;
导入redis.clients.jedis.jedis;
@控制器
公共类接收控制器{
@自动连线
JmsTemplate JmsTemplate;
@豆子
公共连接工厂jmsConnectionFactory(){
RMQConnectionFactory connectionFactory=新的RMQConnectionFactory();
connectionFactory.setUsername(“用户名”);
connectionFactory.setPassword(“密码”);
connectionFactory.setVirtualHost(“vhostname”);
setHost(“主机名”);
返回连接工厂;
}
@交叉起源
@SuppressWarnings({“unchecked”,“rawtypes”})
@RequestMapping(method=RequestMethod.GET,value=“/getdata”)
@应答器
来自Redis()的公共响应
抛出JSONException、InterruptedException、JmsException、ExecutionException、TimeoutException{
System.out.println(“控制器内”);
jmsTemplate.setReceiveTimeout(500L);
//jmsTemplate。
字符串消息=(字符串)jmsTemplate.receiveAndConvert(“测试”);
//在此处调用web服务并依赖于web服务
//回应
//如果为200,则从队列中删除消息,否则保留消息
//排队
System.out.println(消息);
}
返回新的响应属性(消息,HttpStatus.OK);
}
}
我该怎么做


提前感谢。

您不是在使用
JmsTemplate
,而是在使用
SimpleMessageListenerContainer
来接收消息

如果您使用的是模板,则必须将
execute
方法与
SessionCallback
一起使用,因为确认必须发生在接收消息的会话范围内

但是,使用
SimpleMessageListenerContainer
,只需将
sessionAcknowledgeMode
设置为
Session.CLIENT\u ACKNOWLEDGE
。查看容器javadocs

/**
 * Message listener container that uses the plain JMS client API's
 * {@code MessageConsumer.setMessageListener()} method to
 * create concurrent MessageConsumers for the specified listeners.
 *
 * <p>This is the simplest form of a message listener container.
 * It creates a fixed number of JMS Sessions to invoke the listener,
 * not allowing for dynamic adaptation to runtime demands. Its main
 * advantage is its low level of complexity and the minimum requirements
 * on the JMS provider: Not even the ServerSessionPool facility is required.
 *
 * <p>See the {@link AbstractMessageListenerContainer} javadoc for details
 * on acknowledge modes and transaction options. Note that this container
 * exposes standard JMS behavior for the default "AUTO_ACKNOWLEDGE" mode:
 * that is, automatic message acknowledgment after listener execution,
 * with no redelivery in case of a user exception thrown but potential
 * redelivery in case of the JVM dying during listener execution.
 *
 * <p>For a different style of MessageListener handling, through looped
 * {@code MessageConsumer.receive()} calls that also allow for
 * transactional reception of messages (registering them with XA transactions),
 * see {@link DefaultMessageListenerContainer}.
   ...
然后,将
execute
方法与
SessionCallback
一起使用

Boolean result = this.jmsTemplate.execute(session -> {
    MessageConsumer consumer = session.createConsumer(
            this.jmsTemplate.getDestinationResolver().resolveDestinationName(session, "bar", false));
    String result = null;
    try {
        Message received = consumer.receive(5000);
        if (received != null) {
            result = (String) this.jmsTemplate.getMessageConverter().fromMessage(received);

            // Do some stuff here.

            received.acknowledge();
            return true;
        }
    }
    catch (Exception e) {
        return false;
    }
    finally {
        consumer.close();
    }
}, true);

正如我所说,您必须使用
execute
方法-我添加了一个示例。感谢分享代码。但我想用将从队列中消耗的消息调用web服务。在您的代码中,输出的“字符串值”我将无法在内部访问。若我把它带到外面处理,直到消息被确认并从队列中删除。我不理解你们的担心;您可以在我说的
//在这里做一些事情的地方调用web服务。你可以归还你想要的任何东西;我只是碰巧将值返回到外部代码。我已经改变了示例,改为返回布尔值-也许这会让它更清楚。如果抛出异常,它将返回false,消息将不会被确认。感谢您共享代码。这解决了问题,我昨天工作了一整天。对我来说,另一个可行的选择是创建一个新连接和一个消息消费者,并在连接上启用client_ack,但您的解决方案使使用jmsTemplate消费client_ack中的消息成为可能。当做
Boolean result = this.jmsTemplate.execute(session -> {
    MessageConsumer consumer = session.createConsumer(
            this.jmsTemplate.getDestinationResolver().resolveDestinationName(session, "bar", false));
    String result = null;
    try {
        Message received = consumer.receive(5000);
        if (received != null) {
            result = (String) this.jmsTemplate.getMessageConverter().fromMessage(received);

            // Do some stuff here.

            received.acknowledge();
            return true;
        }
    }
    catch (Exception e) {
        return false;
    }
    finally {
        consumer.close();
    }
}, true);