我有一个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
。 我想要的是该消息保留在队列中并重复提供给所有订阅者,直到它被消费或过期(适当的选择性消费者)。我怎么能这样做?
答案 0 :(得分:1)
请注意,GET的传入消息没有内容,Webflux集成无法正确处理ATM
我不明白这个问题。
WebFluxInboundEndpoint
适用于此算法:
if (isReadable(request)) {
...
else {
return (Mono<T>) Mono.just(exchange.getRequest().getQueryParams());
}
GET
方法真正进入else
分支。要发送的邮件的payload
是MultiValueMap
。我们最近还为您解决了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功能......