Java AggregatingMessageHandler的手动确认

Java AggregatingMessageHandler的手动确认,java,spring,spring-integration,amqp,spring-amqp,Java,Spring,Spring Integration,Amqp,Spring Amqp,我正在尝试构建这样的集成场景Rabbit->AmqpInboundChannelAdapter(AcknowledgeMode.MANUAL)->DirectChannel->AggregatingMessageHandler->DirectChannel->amqOutboundEndpoint 我想聚合内存中的消息,如果我聚合了10条消息,或者达到了10秒的超时时间,就释放它。我想这个配置还可以: @Bean @ServiceActivator(inputChannel = "amqpInp

我正在尝试构建这样的集成场景
Rabbit->AmqpInboundChannelAdapter(AcknowledgeMode.MANUAL)->DirectChannel->AggregatingMessageHandler->DirectChannel->amqOutboundEndpoint

我想聚合内存中的消息,如果我聚合了10条消息,或者达到了10秒的超时时间,就释放它。我想这个配置还可以:

@Bean
@ServiceActivator(inputChannel = "amqpInputChannel")
public MessageHandler aggregator(){
    AggregatingMessageHandler aggregatingMessageHandler = new AggregatingMessageHandler(new DefaultAggregatingMessageGroupProcessor(), new SimpleMessageStore(10));
    aggregatingMessageHandler.setCorrelationStrategy(new HeaderAttributeCorrelationStrategy(AmqpHeaders.CORRELATION_ID));
    //default false
    aggregatingMessageHandler.setExpireGroupsUponCompletion(true);  //when grp released (using strategy), remove group so new messages in same grp create new group
    aggregatingMessageHandler.setSendPartialResultOnExpiry(true);   //when expired because timeout and not because of strategy, still send messages grouped so far
    aggregatingMessageHandler.setGroupTimeoutExpression(new ValueExpression<>(TimeUnit.SECONDS.toMillis(10)));  //timeout after X

    //timeout is checked only when new message arrives!!
    aggregatingMessageHandler.setReleaseStrategy(new TimeoutCountSequenceSizeReleaseStrategy(10, TimeUnit.SECONDS.toMillis(10)));
    aggregatingMessageHandler.setOutputChannel(amqpOutputChannel());
    return aggregatingMessageHandler;
}

@服务
公共类ManualAckServiceActivator{
@ServiceActivator(inputChannel=“manualAckChannel”)
公共无效句柄(@表头(手动确认对)列表手动确认对){
manualAckPair.forEach(manualAckPair->{
manualAckPair.basicAck();
});
}
}

没错,聚合器不需要如此复杂的逻辑

您只需在聚合器发布后确认它们-在聚合器和该
AmqpOutboundEndpoint
之间的service activator中

您必须在那里使用
basicAck()
,并将
multiple
标志设置为
true

@param multiple true to acknowledge all messages up to and
因此,您肯定需要一个自定义的
MessageGroupProcessor
来提取整个批处理的最高
AmqpHeaders.DELIVERY_标记
,并将其设置为输出聚合消息的头

您可以只扩展
DefaultAggregatingMessageGroupProcessor
并覆盖其
aggregateHeaders()

/**
*此默认实现只返回组中没有冲突的所有头。缺席的头球
*在组中的一个或多个邮件上,不视为冲突。子类可以用
*如有必要,提供更高级的冲突解决策略。
*
*@param group消息组。
*@返回聚合的标题。
*/
受保护的映射聚合头(MessageGroup){

如果我在聚合器发布后确认消息,消息不可能到达
AmqpOutboundEndpoint
但我仍然确认它们(意味着它们从AMQP队列中丢失)?我知道这不太可能,但仍然是一个问题???您正在确认接收到的消息。这与生成的消息无关。或者您只想在正确发送到其他AMQP队列时确认?是的,我想确认接收消息(在
AmqpInboundChannelAdapter(AcknowledgeMode.MANUAL)
)。基本上等待10秒钟,以便聚合消息并将其释放到新的exchange。如果释放(到AMQP exchange)成功,则仅确认接收到的消息(在接收到的AMQP队列上)。我这里需要某种消息节流器/聚合器。好的。因此,使用那些
AmqpHeaders.CHANNEL
和最高的
AmqpHeaders.DELIVERY_标记
头作为
basicAck()的选项
amqoutboundendpoint
confirmCalback
中。查看它的
confirmAckChannel
:这里的最高值是否正确?因为我只能将1234条消息中的一条(2,3,4)进行聚合并发送,而1仍在等待聚合和发送。我假设如果使用最高值,我会丢失1?
public class ManualAckPair {
    private Channel channel;
    private Long deliveryTag;

    public ManualAckPair(Channel channel, Long deliveryTag) {
        this.channel = channel;
        this.deliveryTag = deliveryTag;
    }

    public void basicAck(){
        try {
            this.channel.basicAck(this.deliveryTag, false);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public abstract class AbstractManualAckAggregatingMessageGroupProcessor extends AbstractAggregatingMessageGroupProcessor {
    public static final String MANUAL_ACK_PAIRS = PREFIX + "manualAckPairs";

    @Override
    protected Map<String, Object> aggregateHeaders(MessageGroup group) {
        Map<String, Object> aggregatedHeaders = super.aggregateHeaders(group);
        List<ManualAckPair> manualAckPairs = new ArrayList<>();
        group.getMessages().forEach(m -> {
            Channel channel = (Channel)m.getHeaders().get(AmqpHeaders.CHANNEL);
            Long deliveryTag = (Long)m.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
            manualAckPairs.add(new ManualAckPair(channel, deliveryTag));
        });
        aggregatedHeaders.put(MANUAL_ACK_PAIRS, manualAckPairs);
        return aggregatedHeaders;
    }
}
@Service
public class ManualAckServiceActivator {

    @ServiceActivator(inputChannel = "manualAckChannel")
public void handle(@Header(MANUAL_ACK_PAIRS) List<ManualAckPair> manualAckPairs) {
    manualAckPairs.forEach(manualAckPair -> {
        manualAckPair.basicAck();
    });
}
}
@param multiple true to acknowledge all messages up to and
/**
 * This default implementation simply returns all headers that have no conflicts among the group. An absent header
 * on one or more Messages within the group is not considered a conflict. Subclasses may override this method with
 * more advanced conflict-resolution strategies if necessary.
 *
 * @param group The message group.
 * @return The aggregated headers.
 */
protected Map<String, Object> aggregateHeaders(MessageGroup group) {