Spring WebFlux+Kotlin协程+Spring AI调用OpenRouter API时出现block()阻塞错误的解决咨询
Spring WebFlux+Kotlin协程+Spring AI调用OpenRouter API时出现block()阻塞错误的解决咨询
兄弟,我之前做WebFlux+Kotlin协程+Spring AI的集成时,也踩过一模一样的坑!给你理清楚问题根源和具体的解决办法:
问题根源
你猜的完全没错——Spring AI默认的call()方法内部确实用了block()来同步等待WebClient的响应,而WebFlux的Netty IO线程(就是报错里的reactor-http-nio-*)是严格禁止阻塞操作的,一旦触发就会抛出这个IllegalStateException。
具体解决步骤
1. 切换到Spring AI的异步API
Spring AI其实提供了非阻塞的异步调用方法,把你代码里的call()换成callAsync(),然后用Kotlin协程的awaitSingleOrNull()来异步获取结果,完全不会触发阻塞:
suspend fun chatComplete(message: String, conversationId: String): ChatResponse { val fullResponse = chatClient.prompt() .user(message) .callAsync() // 替换为异步版本的callAsync .awaitSingleOrNull() // 协程上下文里用await异步等待,而非block ?.content() ?: "" return ChatResponse(message = fullResponse, conversationId = conversationId) }
2. 确保控制器方法是suspend函数
这个你大概率已经做了,但再确认下:你的WebFlux控制器方法必须标记为suspend,这样Spring会自动把它调度到协程上下文,避免在Netty线程直接执行操作:
@RestController class ChatController(private val chatService: ChatService) { @PostMapping("/chat") suspend fun chat(@RequestBody request: ChatRequest): ChatResponse { return chatService.chatComplete(request.message, request.conversationId) } }
3. 升级Spring AI到最新稳定版
如果你的Spring AI版本比较旧,建议升级到1.0.0+的稳定版,这个版本开始对Kotlin协程和WebFlux的非阻塞支持做了很多优化,能避免不少旧版本的兼容问题。
推荐的非阻塞集成姿势
- 全程保持异步链:所有Spring AI的操作都用异步API(
callAsync()、streamAsync()等),配合协程的await()/asFlow()方法,绝对不要在协程里手动调用block()系列方法。 - 流式响应场景的处理:如果需要实现OpenRouter的流式对话返回,可以用
streamAsync()获取Flux,再转成Kotlin的Flow,WebFlux会自动帮你转成SSE(Server-Sent Events):
控制器里直接返回这个suspend fun streamChat(message: String): Flow<String> { return chatClient.prompt() .user(message) .streamAsync() .asFlow() .map { it.content() ?: "" } }Flow<String>就行,前端可以通过SSE订阅流式结果。 - 万不得已的阻塞操作处理:如果项目里有必须保留的同步阻塞代码,别直接在Netty线程执行,用
withContext(Dispatchers.IO)把它切换到IO线程池:
不过对于Spring AI来说,用异步API是最优解,完全不需要走这个路子。suspend fun someBlockingOperation() { withContext(Dispatchers.IO) { // 这里放阻塞代码,会被调度到IO线程池,不影响Netty线程 } }
这样改完之后,你应该就不会再碰到那个block()的错误了,整个调用链都是非阻塞的,完美适配WebFlux的协程环境!




