You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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线程池:
    suspend fun someBlockingOperation() {
        withContext(Dispatchers.IO) {
            // 这里放阻塞代码,会被调度到IO线程池,不影响Netty线程
        }
    }
    
    不过对于Spring AI来说,用异步API是最优解,完全不需要走这个路子。

这样改完之后,你应该就不会再碰到那个block()的错误了,整个调用链都是非阻塞的,完美适配WebFlux的协程环境!

火山引擎 最新活动