春季整合:如何以SSE的形式传递延迟的详细信息

时间:2018-08-16 18:37:01

标签: spring-integration server-sent-events spring-integration-dsl

我有一个要尽快检索并返回的项目列表。

对于每个项目,我还需要检索详细信息,它们可能会在几秒钟后返回。

我当然可以使用HTTP网关创建两个不同的路由,并首先请求列表,然后请求详细信息。但是,我必须等到所有细节都到了。我想立即寄回清单,然后在收到详细资料后立即寄回。

更新

按照Artem Bilan的建议,我的流程将Flux作为有效载荷返回,它将作为Mono的项目列表与作为Flux的已处理项目合并。

请注意,以下示例通过调用toUpperCase来模拟项目的详细处理;我的实际用例需要路由和拨出电话以获取每个项目的详细信息:

    @Bean
public IntegrationFlow sseFlow() {
    return IntegrationFlows
            .from(WebFlux.inboundGateway("/strings/sse")
                    .requestMapping(m -> m.produces(MediaType.TEXT_EVENT_STREAM_VALUE))
                    .mappedResponseHeaders("*"))
            .enrichHeaders(Collections.singletonMap("aHeader", new String[]{"foo", "bar"}))
            .transform("headers.aHeader")
            .<String[]>handle((p, h) -> {
                return Flux.merge(
                        Mono.just(p),
                        Flux.fromArray(p)
                                .map(t -> {
                                    return t.toUpperCase();
                                    // return detailsResolver.resolveDetail(t);
                                }));
            })
            .get();
}

那更接近我的目标。当我使用curl从该流中请求数据时,我立即获得了项目列表,稍后又获得了处理过的项目:

λ curl http://localhost:8080/strings/sse
data:["foo","bar"]

data:FOO

data:BAR

虽然仅将字符串转换为大写即可,但我很难用WebFlux.outboundGateway进行详细信息的传出呼叫。上面注释掉的代码中的detailsResolver定义如下:

@MessagingGateway
public interface DetailsResolver {

    @Gateway(requestChannel = "itemDetailsFlow.input")
    Object resolveDetail(String item);

}

@Bean
IntegrationFlow itemDetailsFlow() {
    return f -> f.handle(WebFlux.<String>outboundGateway(m ->
            UriComponentsBuilder.fromUriString("http://localhost:3003/rest/path/")
                    .path(m.getPayload())
                    .build()
                    .toUri())
            .httpMethod(HttpMethod.GET)
            .expectedResponseType(JsonNode.class)
            .replyPayloadToFlux(false));
}

当我在detailsResolver呼叫中注释并注释掉t.toUpperCase时,outboundGateway似乎已正确设置(日志显示订户存在,Demand发出信号),但从未得到回应(在ExchangeFunctions.exchange#91中未达到断点)。

我通过从上下文中将其作为bean并调用其方法来确保DetailsResolver本身正在工作-这使我得到了JsonNode响应。

可能是什么原因?

1 个答案:

答案 0 :(得分:2)

是的,我不会在这里使用toReactivePublsiher(),因为您拥有当前请求的上下文。每个请求都需要助焊剂。我会使用类似Flux.merge(Publisher<? extends I>... sources)的东西,其中第一个Flux用于项目,第二个用于每个项目的详细信息(类似Tuple2)。

为此,您确实可以使用以下内容:

  IntegrationFlows
                .from(WebFlux.inboundGateway("/sse")
                        .requestMapping(m -> m.produces(MediaType.TEXT_EVENT_STREAM_VALUE)))

您的下游流应产生Flux作为有效载荷以供回复。

在测试案例中,我有一个这样的样本:

    @Bean
    public IntegrationFlow sseFlow() {
        return IntegrationFlows
                .from(WebFlux.inboundGateway("/sse")
                        .requestMapping(m -> m.produces(MediaType.TEXT_EVENT_STREAM_VALUE))
                        .mappedResponseHeaders("*"))
                .enrichHeaders(Collections.singletonMap("aHeader", new String[] { "foo", "bar", "baz" }))
                .handle((p, h) -> Flux.fromArray((String[]) h.get("aHeader")))
                .get();
    }