处理 OkHttp HTTP/2 REFUSED_STREAM 错误

Dealing with OkHttp HTTP/2 REFUSED_STREAM errors

我们正在使用 OkHttp3 (v4.9.1) 从 Spring 引导应用程序以高度并发的方式建立 h2c(HTTP/2 无 TLS)连接。为此,我们使用以下方法缩小了支持的协议范围:

builder.protocols(List.of(Protocol.H2_PRIOR_KNOWLEDGE))

建立连接通常工作正常,HTTP/2 使用流而不是专用连接。然而,当服务器(基于 nginx,单节点,单地址)在达到一定数量的请求后关闭连接时(如其 keepalive_request 选项所指示),我们观察到零星的错误爆发。发生这种情况时,OkHttp 似乎不会尝试重试连接,而只是向调用者抛出异常:

okhttp3.internal.http2.StreamResetException: stream was reset: REFUSED_STREAM
    at okhttp3.internal.http2.Http2Stream.takeHeaders(Http2Stream.kt:148)
    at okhttp3.internal.http2.Http2ExchangeCodec.readResponseHeaders(Http2ExchangeCodec.kt:96)
    at okhttp3.internal.connection.Exchange.readResponseHeaders(Exchange.kt:106)
    at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.kt:79)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:34)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at org.example.OkHttpAutoConfiguration.lambda$authenticate(OkHttpAutoConfiguration.java:95)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
    at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154)
    [...]

请求是这样发起的:

httpClient.newCall(buildRequest(uri)).execute()

推荐的处理这些错误的方法是什么?

是否有一个选项(我们可能错过了)让 OkHttp 对应用程序透明地处理这个问题?

对于这个问题,您提出了我们在 OkHttp 中修复它的理由。 https://github.com/square/okhttp/issues/6700

在此期间,您需要一个使用 try/catch 块的拦截器,如果异常符合此条件,则在 catch 子句中再次尝试。