先说问题关键:乱序输出的原因可能与Flux和异步处理的特性有关。在Flux的流中,每个操作(例如map、flatMap等)都是异步执行的,这意味着它们会在可用的线程上非阻塞地运行。这种异步特性意味着,如果您的数据处理或者数据源在时间上有差异(例如网络延迟或者不同的响应时间),最终的输出顺序可能与输入顺序不一致。
此外,flatMap操作本身就不保证保持源元素的顺序。flatMap将输入的元素转换为新的Publisher,然后合并这些Publisher发出的元素。因为这些转换后的Publisher可能会以不同的速度产生数据,所以合并的结果可能会出现顺序混乱。
如果希望保持原始请求的顺序,可以考虑使用concatMap代替flatMap。concatMap会等待每一个内部Publisher完成之后才处理下一个,从而保持了处理的顺序。但是,使用concatMap可能会降低处理的并发性和效率,因为它需要按顺序一个接一个地处理每个元素。
原本ChatGpt的流式输出代码:
webClient.post()
.uri("/chat/completions")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer apikey")
.bodyValue(request)
.retrieve()
.bodyToFlux(ChatGPTResponse.class)
.flatMap(response -> Flux.fromIterable(response.getChoices()))
.map(choice -> choice.getDelta().getContent())
.doOnNext(content -> System.out.println("Received content: " + content))
.map(content -> ServerSentEvent.builder(content).build())
.retry(3)
.onErrorResume(e -> {
System.err.println("Error occurred: " + e.getMessage());
return Flux.just(ServerSentEvent.builder("Error: " + e.getMessage()).build());
});
修改后
webClient.post()
.uri("/chat/completions")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer apikey")
.bodyValue(request)
.retrieve()
.bodyToFlux(ChatGPTResponse.class)
.concatMap(response -> Flux.fromIterable(response.getChoices()))
.map(choice -> choice.getDelta().getContent())
.doOnNext(content -> System.out.println("Received content: " + content))
.map(content -> ServerSentEvent.builder(content).build())
.retry(3)
.onErrorResume(e -> {
System.err.println("Error occurred: " + e.getMessage());
return Flux.just(ServerSentEvent.builder("Error: " + e.getMessage()).build());
});