私は春-AMQPを使用しています:2.1.6.RELEASE
私はPublisherReturnコールバックとRabbitTemplateを持っています。
- 私はそれにバインドされたキューを持っていないroutingKeyにメッセージを送信する場合には、リターンコールバックが正しく呼び出されます。これが起こるとき、私は代替routingKeyにメッセージを送りたいです。私はReturnCallbackでRabbitTemplateを使用している場合しかし、それだけでハングアップします。私は、メッセージが/送信することはできません、RabbitTemplateは私のReturnCallbackに制御を返さないことができますと言っては何も表示されていないと私はどちらか任意のPublisherConfirmが表示されません。
- 私は(同じCachingConnectionFactory付き)新しいRabbitTemplateを作成する場合、それはまだ同じように動作します。私の呼び出しはちょうどハングアップします。
- 私はそれにバインドされたキューを持っているroutingKeyにメッセージを送信する場合、メッセージが正しくキューに到着します。ReturnCallbackは、このシナリオでは呼び出されません。
いくつかの調査の後、私は、元のメッセージが完全に処理されるまでrabbitTemplateおよび/または接続がブロックされているという結論に来ています。
私は二CachingConnectionFactoryとRabbitTemplateを作成し、PublisherReturnコールバックにこれらを使用する場合、それは正常に動作しているようです。
この春、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);
}
}
EDIT
簡単な例は、問題を再現します。それを実行するには:
- 持ってRabbitMQのランニング
- fooという交換はキューと呼ばれるFOOに結合しました
- 春ブーツアプリとして実行します
あなたは、次のような出力が表示されます:
in returnCallback before message send
しかし、あなたは表示されません。
in returnCallback after message send
あなたはコメントアウトした場合connectionFactory.setPublisherConfirms(true);
には、[OK]を実行します。
@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(...));
あなたは、同じテンプレート/接続ファクトリを使用することができますが、送信は、別のスレッドで実行する必要があります。
私たちは最近、常に別のスレッド(最後の人がこれを報告した後)のリターンコールバックを呼び出すためのフレームワークを変更したと思ったが、それが割れて落ちたように見えます。
私は開いて発行し、この時間を。
EDIT
あなたは2.1.6を使用していることを確認していますか?
私たちは、リターンが到着したのと同じチャネルを使用しようとするから、送信を防止することにより、2.1.0でこの問題を修正しました。これは私のために罰金を動作します...
@SpringBootApplication
public class So57234770Application {
public static void main(String[] args) {
SpringApplication.run(So57234770Application.class, args);
}
@Bean
public ApplicationRunner runner(RabbitTemplate template) {
template.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
template.send("foo", message);
});
return args -> {
template.convertAndSend("BADKEY", "foo");
};
}
@RabbitListener(queues = "foo")
public void listen(String in) {
System.out.println(in);
}
}
あなたはこの挙動を示すサンプルアプリケーションを提供することができた場合は、私が上で何が起こっているのか見るために見てみましょう。