spring-integration:将排队的消息发送给选择性消费者

时间:2018-05-29 04:17:04

标签: spring-integration spring-integration-dsl

我有一个Spring集成流程,它会产生一些消息,这些消息应该保持在等待合适的消费者出现并消费它们。

@Bean
public IntegrationFlow messagesPerCustomerFlow() {
    return IntegrationFlows.
            from(WebFlux.inboundChannelAdapter("/messages/{customer}")
                    .requestMapping(r -> r
                            .methods(HttpMethod.POST)
                    )
                    .requestPayloadType(JsonNode.class)
                    .headerExpression("customer", "#pathVariables.customer")
            )
            .channel(messagesPerCustomerQueue()) 
            .get();
}

@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerSpec poller() {
    return Pollers.fixedRate(100);
}

@Bean
public QueueChannel messagesPerCustomerQueue() {
    return MessageChannels.queue()
            .get();
}

队列中的消息应通过http作为服务器发送的事件传递,如下所示。

PublisherSubscription只是发布者和IntegrationFlowRegistration的持有者,后者用于在不再需要时销毁动态创建的流(请注意,GET的传入消息没有内容,但处理不当通过Webflux集成的ATM,因此需要一个小的解决方法来访问推送到customer标头的路径变量:

@Bean
public IntegrationFlow eventMessagesPerCustomer() {
    return IntegrationFlows
       .from(WebFlux.inboundGateway("/events/{customer}")
            .requestMapping(m -> m.produces(TEXT_EVENT_STREAM_VALUE))
            .headerExpression("customer", "#pathVariables.customer")
            .payloadExpression("''") // neeeded to make handle((p,h) work
       )
       .log()
       .handle((p, h) -> {
           String customer = h.get("customer").toString();
           PublisherSubscription<JsonNode> publisherSubscription =
               subscribeToMessagesPerCustomer(customer);
           return Flux.from(publisherSubscription.getPublisher())
                   .map(Message::getPayload)
                   .doFinally(signalType ->
                      publisherSubscription.unsubscribe());
       })
       .get();
}

上述对服务器发送事件的请求动态地注册一个流,该流根据需要订阅队列信道,selective consumer由具有throwExceptionOnRejection(true)的过滤器实现。遵循Message Handler chain的规范,该规范应确保向所有消费者提供消息,直到接受消息为止。

public PublisherSubscription<JsonNode> subscribeToMessagesPerCustomer(String customer) {
    IntegrationFlowBuilder flow = IntegrationFlows.from(messagesPerCustomerQueue())
            .filter("headers.customer=='" + customer + "'",
                    filterEndpointSpec -> filterEndpointSpec.throwExceptionOnRejection(true));
    Publisher<Message<JsonNode>> messagePublisher = flow.toReactivePublisher();

    IntegrationFlowRegistration registration = integrationFlowContext.registration(flow.get())
            .register();

    return new PublisherSubscription<>(messagePublisher, registration);
}

此构造原则上有效,但存在以下问题:

  • 在没有订阅者的情况下发送到队列的消息会导致MessageDeliveryException: Dispatcher has no subscribers for channel 'application.messagesPerCustomerQueue'
  • 在没有匹配订阅者的情况下发送到队列的消息导致AggregateMessageDeliveryException: All attempts to deliver Message to MessageHandlers failed

我想要的是该消息保留在队列中并重复提供给所有订阅者,直到它被消费或过期(适当的选择性消费者)。我怎么能这样做?

1 个答案:

答案 0 :(得分:1)

  

请注意,GET的传入消息没有内容,Webflux集成无法正确处理ATM

我不明白这个问题。

WebFluxInboundEndpoint适用于此算法:

if (isReadable(request)) {
   ...
else {
    return (Mono<T>) Mono.just(exchange.getRequest().getQueryParams());
}

GET方法真正进入else分支。要发送的邮件的payloadMultiValueMap。我们最近还为您解决了POST的问题,该问题已在版本5.0.5中发布:https://jira.spring.io/browse/INT-4462

  

Dispatcher没有订阅者

原则上QueueChannel不可能发生。那里根本没有任何调度员。它只是存储队列和发件人提供消息。您错过了与我们分享的其他内容。但是让我们用自己的名字来称呼:messagesPerCustomerQueue不是你应用程序中的QueueChannel

<强>更新

关于:

  

我想要的是该消息保留在队列中并重复提供给所有订阅者,直到它被消费或过期(适当的选择性消费者)

只有我们看到的是基于嵌入式ActiveMQ的PollableJmsChannel才能为消息提供TTL。作为此队列的使用者,您应该PublishSubscribeChannel使用setMinSubscribers(1)使MessagingTemplate在没有订阅者时抛出MessageDeliveryException。这样,将回滚JMS事务,并且消息将返回到下一个轮询周期的队列。

内存中QueueChannel的问题是,没有事务重新传递,并且从该队列中轮询的消息将会丢失。

另一个类似于JMS(事务性)的选项是JdbcChannelMessageStore的{​​{1}}。虽然这样我们没有TTL功能......