目标是将大型json.gz文件(4 GB压缩,大约12 GB未压缩,1200万行)从Web服务器直接流式传输到数据库,而无需在本地下载。由于Spring集成出站网关不支持gzip格式,我自己使用okhttp自动解压缩响应:
body = response.body().byteStream(); // thanks okhttp
reader = new InputStreamReader(body, StandardCharsets.UTF_8);
br = new BufferedReader(reader, bufferSize);
Flux<String> flux = Flux.fromStream(br.lines())
.onBackpressureBuffer(10000, x -> log.error("Buffer overrun!"))
.doAfterTerminate(() -> closeQuietly(closeables))
.doOnError(t -> log.error(...))
在整合流程中:
.handle(new MessageTransformingHandler(new GzipToFluxTransformer(...)))
.split()
.log(LoggingHandler.Level.DEBUG, CLASS_NAME, Message::getHeaders)
.channel(repositoryInputChannel())
但是
2017-12-08 22:48:47.846 [task-scheduler-7] [ERROR] c.n.d.y.s.GzipToFluxTransformer - Buffer overrun!
2017-12-08 22:48:48.337 [task-scheduler-7] [ERROR] o.s.i.h.LoggingHandler - org.springframework.messaging.MessageHandlingException:
error occurred in message handler [org.springframework.integration.splitter.DefaultMessageSplitter#1];
nested exception is reactor.core.Exceptions$OverflowException: The receiver is overrun by more signals than expected (bounded queue...),
failedMessage=...}]
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:153)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:132)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:105)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:73)
使用桥接轮询的无界队列在运行时连接输出通道。这是为了便于测试,以便可以用DirectChannel
代替队列进行测试。
@Bean(name = "${...}")
public PollableChannel streamingOutputChannel() {
return new QueueChannel();
}
@Bean
public IntegrationFlow srcToSinkBridge() {
return IntegrationFlows.from(streamingOutputChannel())
.bridge(e -> e.poller(Pollers.fixedDelay(500)))
.channel(repositoryInputChannel())
.get();
}
我在这里有几个疑问。
答案 0 :(得分:1)
问题是log
声明!它使用窃听将分离器的输出通道更改为DirectChannel
,这会混淆AbstractMessageSplitter.
boolean reactive = getOutputChannel() instanceof ReactiveStreamsSubscribableChannel;
引用文档:
从5.0版开始,......如果Splitter的输出通道是 ReactiveStreamsSubscribableChannel的实例, AbstractMessageSplitter生成Flux结果而不是Iterator 并且输出通道订阅了此Flux以获得背压 基于对下游流量需求的分割。
工作代码如下 - 只需在分离器后立即将日志语句移动到最后修复背压问题:
IntegrationFlows.from(inputChannel)
.filter(Message.class, msg -> msg.getHeaders().containsKey(FILE_TYPE_HEADER))
.handle(new GzipToFluxTransformer(...))
.transform((Flux<String> payload) -> payload
.onBackpressureBuffer(getOnBackpressureBufferSize(),
s -> log.error("Buffer overrun!")))
.split()
.channel(c -> c.flux(outputChannel))
.log(LoggingHandler.Level.DEBUG, CLASS_NAME, Message::getHeaders)
.get();
我在Spring集成GitHub上打开了问题2302。