Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/327.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用spring amqp,什么';从PublisherReturn回调内部向rabbitmq发送消息的最佳方式是什么?_Java_Spring Boot_Spring Amqp - Fatal编程技术网

Java 使用spring amqp,什么';从PublisherReturn回调内部向rabbitmq发送消息的最佳方式是什么?

Java 使用spring amqp,什么';从PublisherReturn回调内部向rabbitmq发送消息的最佳方式是什么?,java,spring-boot,spring-amqp,Java,Spring Boot,Spring Amqp,我使用的是SpringAMQP:2.1.6.RELEASE 我有一个带PublisherReturn回调的RabbitTemplate 如果我向没有绑定队列的routingKey发送消息 它,然后正确调用返回回调。当这种情况发生时,我 要将消息发送到备用路由密钥。然而,如果 我在返回回调中使用RabbitTemplate,它只是挂断了。我 没有看到任何东西表明消息无法发送 RabbitTemplate不会将控制权返回给我的ReturnCallback,而我 也没有看到任何发布者确认 如果我创建

我使用的是SpringAMQP:2.1.6.RELEASE

我有一个带PublisherReturn回调的RabbitTemplate

  • 如果我向没有绑定队列的routingKey发送消息 它,然后正确调用返回回调。当这种情况发生时,我 要将消息发送到备用路由密钥。然而,如果 我在返回回调中使用RabbitTemplate,它只是挂断了。我 没有看到任何东西表明消息无法发送 RabbitTemplate不会将控制权返回给我的ReturnCallback,而我 也没有看到任何发布者确认
  • 如果我创建一个新的RabbitTemplate(使用相同的CachingConnectionFactory) 然后它仍然以同样的方式运行。我的电话挂断了
  • 如果我向routingKey发送消息,而routingKey确实绑定了一个队列, 然后消息正确到达队列。返回的回调不是 在这个场景中调用
经过一些调查,我得出结论,rabbitTemplate和/或连接被阻塞,直到原始消息被完全处理

如果我创建第二个CachingConnectionFactory和RabbitTemplate,并在PublisherReturn回调中使用它们,那么它似乎可以正常工作

所以,问题是:使用spring amqp在PublisherReturn回调中发送消息的最佳方式是什么

我已经搜索过了,但是找不到任何解释你应该如何做的东西

以下是我所拥有的简化细节:

@Configuration
public class MyConfig {

    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
        connectionFactory.setPublisherReturns(true);
        // ... other settings left out for brevity
        return connectionFactory;
    }

    @Bean
    @Qualifier("rabbitTemplate")
    public RabbitTemplate rabbitTemplate(ReturnCallbackForAlternative returnCallbackForAlternative) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory());
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setReturnCallback(returnCallbackForAlternative);
        // ... other settings left out for brevity
        return rabbitTemplate;
    }

    @Bean
    @Qualifier("connectionFactoryForUndeliverable")
    public ConnectionFactory connectionFactoryForUndeliverable() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
        // ... other settings left out for brevity
        return connectionFactory;
    }

    @Bean
    @Qualifier("rabbitTemplateForUndeliverable")
    public RabbitTemplate rabbitTemplateForUndeliverable() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactoryForUndeliverable());
        // ... other settings left out for brevity
        return rabbitTemplate;
    }

}
然后发送我正在使用的消息

    @Autowired
    @Qualifier("rabbitTemplate")
    private RabbitTemplate rabbitTemplate;

    public void send(Message message) {
        rabbitTemplate.convertAndSend(
            "exchange-name",
            "primary-key",
            message);
    }
ReturnCallback中的代码是

@Component
public class ReturnCallbackForAlternative implements RabbitTemplate.ReturnCallback {

    @Autowired
    @Qualifier("rabbitTemplateForUndeliverable")
    private RabbitTemplate rabbitTemplate;

    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        rabbitTemplate.convertAndSend(
            "exchange-name",
            "alternative-key",
            message);
    }

}
编辑

复制问题的简化示例。 要运行它,请执行以下操作:

  • 让RabbitMq运行
  • 将名为foo的交换绑定到名为foo的队列
  • 作为spring启动应用程序运行
  • 您将看到以下输出:

    in returnCallback before message send
    
    但你不会看到:

    in returnCallback after message send
    
    如果注释掉
    connectionFactory.setPublisherConfigrms(true)运行正常

    @SpringBootApplication
    public class HangingApplication {
    
        public static void main(String[] args) {
          SpringApplication.run(HangingApplication.class, args);
        }
    
        @Bean
        public ConnectionFactory connectionFactory() {
          CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
          connectionFactory.setPublisherReturns(true);
          connectionFactory.setPublisherConfirms(true);
          return connectionFactory;
        }
    
        @Bean
        public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
          RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
          rabbitTemplate.setExchange("foo");
          rabbitTemplate.setMandatory(true);
    
          rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            System.out.println("Confirm callback for main template. Ack=" + ack);
          });
    
          rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            System.out.println("in returnCallback before message send");
            rabbitTemplate.send("foo", message);
            System.out.println("in returnCallback after message send");
          });
    
          return rabbitTemplate;
        }
    
        @Bean
        public ApplicationRunner runner(@Qualifier("rabbitTemplate") RabbitTemplate template) {
    
          return args -> {
            template.convertAndSend("BADKEY", "foo payload");
          };
        }
    
        @RabbitListener(queues = "foo")
        public void listen(String in) {
          System.out.println("Message received on undeliverable queue : " + in);
        }
    
    }
    
    这是我使用的build.gradle:

    plugins {
        id 'org.springframework.boot' version '2.1.5.RELEASE'
        id 'java'
    }
    
    apply plugin: 'io.spring.dependency-management'
    
    group 'pcoates'
    version '1.0-SNAPSHOT'
    
    sourceCompatibility = 1.11
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        compile 'org.springframework.boot:spring-boot-starter-amqp'
    }
    

    它会导致amqp客户端代码出现某种死锁。最简单的解决方案是在单独的线程上执行发送-在回调中使用
    TaskExecutor

    exec.execute(() -> template.send(...));
    
    您可以使用相同的模板/连接工厂,但发送必须在不同的线程上运行

    我认为我们最近已经改变了框架,总是在另一个线程上调用返回回调(在最后一个人报告这一点之后),但它看起来好像是从裂缝中掉了出来

    我打开了

    编辑

    您确定使用的是2.1.6吗

    我们在2.1.0中通过阻止发送尝试使用返回到达的相同通道修复了此问题。这对我来说很好

    @springboot应用程序
    公共类SO57234770应用程序{
    公共静态void main(字符串[]args){
    SpringApplication.run(So57234770Application.class,args);
    }
    @豆子
    公共应用程序运行程序(RabbitTemplate模板){
    setReturnCallback((消息、replyCode、replyText、exchange、routingKey)->{
    发送模板(“foo”,消息);
    });
    返回参数->{
    模板.convertAndSend(“BADKEY”、“foo”);
    };
    }
    @RabbitListener(queues=“foo”)
    公共void侦听(字符串输入){
    系统输出打印项次(输入);
    }
    }
    

    如果你能提供一个展示这种行为的示例应用程序,我会看看发生了什么。

    顺便说一句,你不应该使用
    convertAndSend
    ,因为
    message
    已经是
    message
    (转换后)。嗯-你确定你使用的是2.1.6吗?我们在2.1.0中以不同的方式修复了此问题,并使用了一个验证程序。如果你能组装一个展示它的小测试应用程序,我很乐意看一看(可能会有所不同)。谢谢。我刚刚试过你的例子,它对我也很有用。我将尝试在一个小测试应用程序中重新创建我遇到的问题。我很确定我使用的是2.1.6。当我添加publisherConfirms时,ReturnCallback中的发送似乎挂起。我在问题的末尾加了一个例子。我以您为例,添加了publisherConfirms。我将添加使用的build.gradle文件到.confirmation;谢谢-我会继续,并确保我们在下一个版本中总是在不同的线程上调用回调。在此之前,您可以将发送任务交给回调中的另一个线程。再次感谢你找到这个。