FeignClient调用SSE接口阻塞问题及流式返回实现咨询
解决FeignClient调用SSE接口无法流式返回的问题
问题现象
调用SSE接口时,FeignClient会等待所有数据接收完成后才一次性返回结果;而WebClient(或Spring 6 HTTP Interface)能实时逐段返回SSE数据——比如自定义的Mock接口每1秒返回一条数据,WebClient能立刻输出,Feign却要等10秒才全量输出。不管是自定义Mock SSE接口,还是OpenAI的对话SSE API,都存在该问题。
核心原因
默认Feign(包括未正确配置的ReactorFeign)基于阻塞式HTTP客户端实现,即使方法返回Flux,底层仍会将整个响应体读取完成后再封装成Flux返回,没有真正处理SSE的分块响应。要实现流式返回,必须配置专门的SSE解码器,让Feign能逐块解析响应。
解决方案步骤
1. 调整依赖
确保引入ReactorFeign核心依赖及对应解码器,以处理SSE分块和JSON解析(以OpenAI场景为例):
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-reactor-core</artifactId> <version>12.3</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-reactor-jackson</artifactId> <version>12.3</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-reactive-wrappers</artifactId> <version>12.3</version> </dependency>
2. 配置ReactorFeign客户端
构建客户端时指定SseDecoder,让Feign能逐块解析SSE响应:
@Bean public OpenAIClient openAIClient() { return ReactorFeign.builder() // 用SseDecoder处理分块响应,结合JacksonDecoder解析JSON格式的SSE事件 .decoder(new SseDecoder(new JacksonDecoder())) .target(OpenAIClient.class, "https://api.openai.com"); }
如果是自定义的字符串类型SSE接口,替换成StringDecoder即可:
@Bean public MockSSEClient mockSSEClient() { return ReactorFeign.builder() .decoder(new SseDecoder(new StringDecoder())) .target(MockSSEClient.class, "http://localhost:8080"); }
3. 修正Feign接口定义
添加Accept: text/event-stream请求头,明确告知服务端返回SSE格式,同时指定正确的返回类型:
public interface OpenAIClient { @RequestLine("POST /v1/chat/completions") @Headers({ "Authorization: Bearer {apiKey}", "Accept: text/event-stream", "Content-Type: application/json" }) // OpenAI的SSE返回是ChatCompletionChunk格式,而非直接返回String Flux<ChatCompletionChunk> getSSEStream(@Param("apiKey") String apiKey, ChatRequest request); }
自定义Mock接口的示例:
public interface MockSSEClient { @RequestLine("GET /mockChatSteam") @Headers("Accept: text/event-stream") Flux<String> stream(); }
4. 控制器层流式返回
将Feign返回的Flux直接返回给前端,无需额外封装:
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> stream() { ChatRequest request = new ChatRequest(); // 填充对话请求参数 return openAIClient.getSSEStream("your-openai-api-key", request) .map(chunk -> chunk.getChoices().get(0).getMessage().getContent()) .filter(Objects::nonNull); }
关键说明
SseDecoder是实现流式返回的核心,它会逐行读取响应体,每识别到一个SSE事件就立刻发送到Flux中,而非等待全量数据。- 必须显式设置
Accept: text/event-stream请求头,避免服务端返回非流式的响应格式。 - OpenAI的SSE返回是JSON分块,需用
JacksonDecoder配合SseDecoder解析成对应实体类,不能直接用Flux<String>,否则会拿到未解析的原始JSON字符串。
内容的提问来源于stack exchange,提问作者zy_sun
相关产品推荐
相关产品推荐

