聚合器的forceRelease

时间:2019-04-02 22:02:31

标签: spring-boot spring-integration spring-amqp spring-integration-dsl

我正在尝试找出处理可能在服务中发生的错误的最佳方法,该服务在发生聚合的组超时之后被调用,该服务模仿与满足releaseExpression相同的流程。

这是我的设置:

我有一个AmqpInboundChannelAdapter,可以接收消息并将其发送到我的聚合器。

满足releaseExpression且在groupTimeout到期之前,如果在我的ServiceActivator中引发了异常,则该消息将被发送到该MessageGroup中所有消息的死信队列中。 (在下面的示例中有10条消息,仅用于说明目的)这就是我所期望的。

如果尚未满足我的releaseExpression但已满足groupTimeout且该组超时,则如果在我的ServiceActivator中抛出异常,则消息将不会发送到我的死信队列中并被确认。

在阅读另一篇博客文章之后, link1 它提到发生这种情况是因为该处理发生在MessageGroupStoreReaper的另一个线程中,而不是SimpleMessageListenerContainer所在的线程中。一旦处理从SimpleMessageListener的线程移开,消息将被自动确认。

我添加了上面链接中提到的配置,并看到错误消息被发送到我的错误处理程序。我的主要问题是,什么是处理这种情况以最大程度地减少消息丢失的最佳方法。

以下是我正在探索的选项:

  • 在我的自定义错误处理程序中使用BatchRabbitTemplate将失败的消息发布到如果满足releaseExpression时将进入的相同死信队列。 (这是我在下面概述的方法,但是如果发布期间发生错误,我担心消息会丢失)

  • 调查是否可以解决这个问题,我可以让SimpleMessageListener知道发生的错误,然后将失败的消息批次发送到死信队列?我怀疑这是否可行,因为似乎已经确认了该消息。

  • 请不要将SimpleMessageListenerContainer设置为AcknowledgeMode.AUTO,并在满足releaseExpression或groupTimeOut发生时通过服务对消息进行手动确认。 (这似乎有些混乱,因为MessageGroup中可能有1..N条消息,但想看看其他人做了什么)

理想情况下,我想拥有一个流程,该流程将在遇到releaseExpression时模仿相同的流程,以使消息不会丢失。

有人对过去使用过的这种情况的最佳方式提出建议吗?

感谢您的帮助和/或建议!

这是我当前使用Spring Integration DSL的配置

@Bean
    public SimpleMessageListenerContainer workListenerContainer() {
        SimpleMessageListenerContainer container =
                new SimpleMessageListenerContainer(rabbitConnectionFactory);
        container.setQueues(worksQueue());
        container.setConcurrentConsumers(4);
        container.setDefaultRequeueRejected(false);
        container.setTransactionManager(transactionManager);
        container.setChannelTransacted(true);
        container.setTxSize(10);
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);          
        return container;
    }

  @Bean
    public AmqpInboundChannelAdapter inboundRabbitMessages() {
        AmqpInboundChannelAdapter adapter = new AmqpInboundChannelAdapter(workListenerContainer());       
        return adapter;
    }

我已经定义了一个错误通道,并定义了自己的TaskScheduler用于MessageStoreRepear

   @Bean 
    public ThreadPoolTaskScheduler taskScheduler(){
        ThreadPoolTaskScheduler  ts = new ThreadPoolTaskScheduler();
        MessagePublishingErrorHandler mpe = new MessagePublishingErrorHandler();
        mpe.setDefaultErrorChannel(myErrorChannel());
        ts.setErrorHandler(mpe);
        return ts;
    }


    @Bean
    public PollableChannel myErrorChannel() {
        return new QueueChannel();
    }
 public IntegrationFlow aggregationFlow() {
        return IntegrationFlows.from(inboundRabbitMessages())               
                .transform(Transformers.fromJson(SomeObject.class))             
                 .aggregate(a->{
                    a.sendPartialResultOnExpiry(true);                  
                    a.groupTimeout(3000);   
                    a.expireGroupsUponCompletion(true);
                    a.expireGroupsUponTimeout(true);                    
                    a.correlationExpression("T(Thread).currentThread().id");
                    a.releaseExpression("size() == 10");                            
                    a.transactional(true);
                 }
                )               
                .handle("someService", "processMessages")
                .get();
    }

这是我的自定义错误流

@Bean
    public IntegrationFlow errorResponse() {
        return IntegrationFlows.from("myErrorChannel")
                    .<MessagingException, Message<?>>transform(MessagingException::getFailedMessage,
                            e -> e.poller(p -> p.fixedDelay(100)))
                    .channel("myErrorChannelHandler")
                    .handle("myErrorHandler","handleFailedMessage")
                    .log()
                    .get();
    }

这是自定义错误处理程序

@Component
public class MyErrorHandler {

    @Autowired
    BatchingRabbitTemplate batchingRabbitTemplate;

    @ServiceActivator(inputChannel = "myErrorChannelHandler")
    public void handleFailedMessage(Message<?> message) {       
        ArrayList<SomeObject> payload = (ArrayList<SomeObject>)message.getPayload();        
        payload.forEach(m->batchingRabbitTemplate.convertAndSend("some.dlq","#", m));
    }

}

这是BatchingRabbitTemplate bean

    @Bean   
    public BatchingRabbitTemplate batchingRabbitTemplate() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(5);
        scheduler.initialize();
        BatchingStrategy batchingStrategy = new SimpleBatchingStrategy(10, Integer.MAX_VALUE, 30000);
        BatchingRabbitTemplate batchingRabbitTemplate = new BatchingRabbitTemplate(batchingStrategy, scheduler);    
        batchingRabbitTemplate.setConnectionFactory(rabbitConnectionFactory);
        return batchingRabbitTemplate;
    }

更新1 )以显示自定义MessageGroupProcessor:

public class CustomAggregtingMessageGroupProcessor extends AbstractAggregatingMessageGroupProcessor {
    @Override
    protected final Object aggregatePayloads(MessageGroup group, Map<String, Object> headers) {
        return group;
    }
}

示例服务:

@Slf4j
public class SomeService  {
    @ServiceActivator
    public void processMessages(MessageGroup messageGroup) throws IOException {
        Collection<Message<?>> messages  = messageGroup.getMessages();
        //Do business logic 
        //ack messages in the group
        for (Message<?> m : messages) {
            com.rabbitmq.client.Channel channel = (com.rabbitmq.client.Channel) 
                    m.getHeaders().get("amqp_channel");
            long deliveryTag = (long) m.getHeaders().get("amqp_deliveryTag");
            log.debug(" deliveryTag = {}",deliveryTag);
            log.debug("Channel = {}",channel);
            channel.basicAck(deliveryTag, false);
        }
    }
}

更新的IntegrationFlow

public IntegrationFlow aggregationFlowWithCustomMessageProcessor() {
        return IntegrationFlows.from(inboundRabbitMessages()).transform(Transformers.fromJson(SomeObject.class))
                .aggregate(a -> {
                    a.sendPartialResultOnExpiry(true);
                    a.groupTimeout(3000);
                    a.expireGroupsUponCompletion(true);
                    a.expireGroupsUponTimeout(true);
                    a.correlationExpression("T(Thread).currentThread().id");
                    a.releaseExpression("size() == 10");
                    a.transactional(true);
                    a.outputProcessor(new CustomAggregtingMessageGroupProcessor());
                }).handle("someService", "processMessages").get();
    }

使用新的ErrorHandler进行小操作

public class MyErrorHandler {

    @ServiceActivator(inputChannel = "myErrorChannelHandler")
    public void handleFailedMessage(MessageGroup messageGroup) throws IOException {
        if(messageGroup!=null) {
            log.debug("Nack messages size = {}", messageGroup.getMessages().size());
            Collection<Message<?>> messages  = messageGroup.getMessages();
            for (Message<?> m : messages) {
                com.rabbitmq.client.Channel channel = (com.rabbitmq.client.Channel) 
                        m.getHeaders().get("amqp_channel");
                long deliveryTag = (long) m.getHeaders().get("amqp_deliveryTag");           
                log.debug("deliveryTag = {}",deliveryTag);
                log.debug("channel = {}",channel);
                channel.basicNack(deliveryTag, false, false);
            }       
        }
    }
}

更新2 添加了自定义的ReleaseStratgedy并将其更改为aggegator

public class CustomMeasureGroupReleaseStratgedy implements ReleaseStrategy {

    private static final int MAX_MESSAGE_COUNT = 10;

    public boolean canRelease(MessageGroup messageGroup) {
        return messageGroup.getMessages().size() >= MAX_MESSAGE_COUNT;
    }
}
   public IntegrationFlow aggregationFlowWithCustomMessageProcessorAndReleaseStratgedy() {
        return IntegrationFlows.from(inboundRabbitMessages()).transform(Transformers.fromJson(SomeObject.class))
                .aggregate(a -> {
                    a.sendPartialResultOnExpiry(true);
                    a.groupTimeout(3000);
                    a.expireGroupsUponCompletion(true);
                    a.expireGroupsUponTimeout(true);
                    a.correlationExpression("T(Thread).currentThread().id");                   
                    a.transactional(true);
                    a.releaseStrategy(new CustomMeasureGroupReleaseStratgedy());            
                    a.outputProcessor(new CustomAggregtingMessageGroupProcessor());
                }).handle("someService", "processMessages").get();
    }

1 个答案:

答案 0 :(得分:0)

您的理解中存在一些缺陷。如果使用AUTO,则在发生异常时仅最后一条消息将变成死字母。在发布之前成功存储在组中的邮件将被立即确认。

实现所需目标的唯一方法是使用MANUAL插口。

没有办法“告诉侦听器容器将消息发送到DLQ”。容器从不将消息发送到DLQ,它拒绝消息,然后代理将其发送到DLX / DLQ。