AggregatingMessageHandler的手动ACK

时间:2017-11-14 15:35:16

标签: java spring spring-integration amqp spring-amqp

我正在尝试构建像http://example.com -> https://www.example.com https://example.com -> DNS Error 这样的集成方案。

我想在内存中聚合消息,如果我聚合10条消息,或者达到超时10秒,则释放它。我想这个配置没问题:

Rabbit -> AmqpInboundChannelAdapter(AcknowledgeMode.MANUAL) -> DirectChannel -> AggregatingMessageHandler -> DirectChannel -> AmqpOutboundEndpoint

现在,我的问题是 - 除了以这种方式创建我自己的AggregatingMessageHandler实现之外,有没有更简单的方法来制作ack消息:

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

更新

我设法在你的帮助之后做到了。最重要的部分:连接工厂必须有public class ManualAckAggregatingMessageHandler extends AbstractCorrelatingMessageHandler { ... private void ackMessage(Channel channel, Long deliveryTag){ try { Assert.notNull(channel, "Channel must be provided"); Assert.notNull(deliveryTag, "Delivery tag must be provided"); channel.basicAck(deliveryTag, false); } catch (IOException e) { throw new MessagingException("Cannot ACK message", e); } } @Override protected void afterRelease(MessageGroup messageGroup, Collection<Message<?>> completedMessages) { Object groupId = messageGroup.getGroupId(); MessageGroupStore messageStore = getMessageStore(); messageStore.completeGroup(groupId); messageGroup.getMessages().forEach(m -> { Channel channel = (Channel)m.getHeaders().get(AmqpHeaders.CHANNEL); Long deliveryTag = (Long)m.getHeaders().get(AmqpHeaders.DELIVERY_TAG); ackMessage(channel, deliveryTag); }); if (this.expireGroupsUponCompletion) { remove(messageGroup); } else { if (messageStore instanceof SimpleMessageStore) { ((SimpleMessageStore) messageStore).clearMessageGroup(groupId); } else { messageStore.removeMessagesFromGroup(groupId, messageGroup.getMessages()); } } } } factory.setPublisherConfirms(true)必须具有以下两个设置:AmqpOutboundEndpointoutboundEndpoint.setConfirmAckChannel(manualAckChannel()),这是其余类的实现:

outboundEndpoint.setConfirmCorrelationExpressionString("#root")

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

1 个答案:

答案 0 :(得分:1)

对,聚合器不需要这么复杂的逻辑。

您可以在聚合器发布后在聚合器和AmqpOutboundEndpoint之间的服务激活器中确认它们。

您必须使用basicAck() multiple标记到true

@param multiple true to acknowledge all messages up to and

那么,为了这个目的,你肯定需要一个自定义MessageGroupProcessor来提取整个批次的最高AmqpHeaders.DELIVERY_TAG并将其设置为输出聚合消息的标题。

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

/**
 * 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) {