grpc-swift ClientInterceptor用EventLoopFuture报Invalid state错误排查
问题原因分析
这个错误的核心是你在脱离Interceptor上下文的EventLoop线程中调用了context.send(_:promise:),导致gRPC的上下文状态失效。具体细节:
- gRPC-Swift的
ClientInterceptor的send方法绑定到特定EventLoop执行,所有与context相关的操作必须在这个EventLoop上完成。 - 用
async/await从actor获取Token时,异步任务可能被调度到其他线程/EventLoop,此时再调用context.send,原上下文已不在可操作状态,触发"Invalid state"错误。 - 若创建
EventLoopPromise时未使用context.eventLoop,而是用了其他EventLoop,也会导致跨线程操作上下文的问题。
正确实现方式
修复后的代码示例,核心是确保所有与context的交互都回到原EventLoop执行:
import GRPC final class AuthTokenInterceptor<Request: GRPCRequest, Response: GRPCResponse>: ClientInterceptor<Request, Response> { private let tokenActor: TokenActor init(tokenActor: TokenActor) { self.tokenActor = tokenActor super.init() } override func send(_ request: Request, context: ClientInterceptorContext<Request, Response>) { // 1. 用context的EventLoop创建Promise,保证上下文一致 let promise = context.eventLoop.makePromise(of: Void.self) // 2. 处理异步获取Token的逻辑 promise.completeWithTask { [weak self] in guard let self = self else { return } // 异步获取Token let token = await self.tokenActor.getToken() // 3. 修改请求,添加认证Token到metadata var modifiedRequest = request modifiedRequest.metadata.add( name: "Authorization", value: "Bearer \(token)" ) // 4. 切回context的EventLoop执行send操作 try await context.eventLoop.perform { context.send(modifiedRequest, promise: promise) } } // 处理异步任务中的错误 promise.futureResult.whenFailure { error in context.send(error: error) } } } // 示例Token Actor actor TokenActor { func getToken() async throws -> String { // 模拟异步获取Token逻辑 try await Task.sleep(nanoseconds: 1_000_000_000) return "your-auth-token" } }
关键注意点
- 必须用
context.eventLoop创建Promise:避免跨EventLoop操作导致的状态不一致。 - 异步操作后切回原EventLoop:通过
context.eventLoop.perform确保context.send在正确线程上下文执行,这是解决"Invalid state"错误的核心。 - 不要直接将自定义Promise传给
context.send:context.send的promise参数是用来接收该send操作的结果,而非绑定你的异步Token获取任务,两者要区分开。
内容的提问来源于stack exchange,提问作者sbarow
相关产品推荐
相关产品推荐

