나는 스프링 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);
}
}
편집하다
간단한 예는 문제를 재현합니다. 그것을 실행하려면 :
- 유무 RabbitMq 실행
- foo는 전화 교환 큐라고 foo는 결합했다
- 봄 부팅 응용 프로그램으로 실행
당신은 다음과 같은 결과를 볼 수 있습니다 :
in returnCallback before message send
하지만 당신은 볼 수 없습니다 :
in returnCallback after message send
당신이 주석하는 경우 connectionFactory.setPublisherConfirms(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에서이 문제를 해결했습니다. 이것은 나를 위해 잘 작동합니다 ...
@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);
}
}
이 동작은, 내가 무슨 일인지 확인하기 위해 살펴 보겠습니다 전시하는 샘플 응용 프로그램을 제공 할 수 있습니다.