스프링 AMQP으로하는 PublisherReturn 콜백 내부에서 rabbitmq에 메시지를 보낼 수있는 가장 좋은 방법은 무엇입니까?

pcoates :

나는 스프링 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);
    }

}

편집하다

간단한 예는 문제를 재현합니다. 그것을 실행하려면 :

  1. 유무 RabbitMq 실행
  2. foo는 전화 교환 큐라고 foo는 결합했다
  3. 봄 부팅 응용 프로그램으로 실행

당신은 다음과 같은 결과를 볼 수 있습니다 :

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);
    }

}

이 동작은, 내가 무슨 일인지 확인하기 위해 살펴 보겠습니다 전시하는 샘플 응용 프로그램을 제공 할 수 있습니다.

추천

출처http://43.154.161.224:23101/article/api/json?id=336430&siteId=1