Java 使用spring amqp,什么';从PublisherReturn回调内部向rabbitmq发送消息的最佳方式是什么?
我使用的是SpringAMQP:2.1.6.RELEASE 我有一个带PublisherReturn回调的RabbitTemplateJava 使用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,而我 也没有看到任何发布者确认 如果我创建
- 如果我向没有绑定队列的routingKey发送消息 它,然后正确调用返回回调。当这种情况发生时,我 要将消息发送到备用路由密钥。然而,如果 我在返回回调中使用RabbitTemplate,它只是挂断了。我 没有看到任何东西表明消息无法发送 RabbitTemplate不会将控制权返回给我的ReturnCallback,而我 也没有看到任何发布者确认
- 如果我创建一个新的RabbitTemplate(使用相同的CachingConnectionFactory) 然后它仍然以同样的方式运行。我的电话挂断了
- 如果我向routingKey发送消息,而routingKey确实绑定了一个队列, 然后消息正确到达队列。返回的回调不是 在这个场景中调用
@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);
}
}
编辑
复制问题的简化示例。
要运行它,请执行以下操作:
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;谢谢-我会继续,并确保我们在下一个版本中总是在不同的线程上调用回调。在此之前,您可以将发送任务交给回调中的另一个线程。再次感谢你找到这个。