当前位置: 首页 > 知识库问答 >
问题:

为什么OkHttp不重用其连接?

锺离锦
2023-03-14

我正在使用OkHttp 3.5.0执行超文本传输协议-基准测试。我正在向同一个URL发送数千个请求。

我期望OkHttp-client使用ConnectionPool并一遍又一遍地重用它的连接。但是如果我们研究netstat,我们会看到许多连接处于TIME_WAIT状态:

TCP    127.0.0.1:80           127.0.0.1:51752        TIME_WAIT
TCP    127.0.0.1:80           127.0.0.1:51753        TIME_WAIT
TCP    127.0.0.1:80           127.0.0.1:51754        TIME_WAIT
TCP    127.0.0.1:80           127.0.0.1:51755        TIME_WAIT
TCP    127.0.0.1:80           127.0.0.1:51756        TIME_WAIT
...

在数千个请求之后,我得到了一个SocketException:没有可用的缓冲区空间(达到最大连接数?)

代码预执行请求(Kotlin):

val client = OkHttpClient.Builder()
        .connectionPool(ConnectionPool(5, 1, TimeUnit.MINUTES))
        .build()

val request = Request.Builder().url("http://192.168.0.50").build()

while (true) {
    val response = client.newCall(request).execute()
    response.close()
}

而不是响应。close()我使用响应。body()。string(),则不会发生SocketException,但netstat仍显示大量等待连接的时间,并且基准性能越来越低。

我哪里做错了?

PS:我尝试过使用ApacheHttpClient及其PoolighttpClientConnectionManager,看起来它工作得很好。但我想找出OkHttp的问题所在。


共有2个答案

子车俊哲
2023-03-14

感谢toien和我找到了我的解决方案(对于非标准超文本传输协议服务器)。

public class Http1CodecWrapper implements HttpCodec {
    private HttpCodec codec;

    public Http1CodecWrapper(HttpCodec codec) {
        this.codec = codec;
    }

    // ...

    @Override
    public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
        return codec.readResponseHeaders(expectContinue)
            .addHeader("Connection", "keep-alive");
    }
}

OkHttpClient httpClient = new OkHttpClient.Builder()
    .addNetworkInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                RealInterceptorChain realChain = (RealInterceptorChain) chain;
                Request request = realChain.request();
                StreamAllocation allocation = realChain.streamAllocation();
                HttpCodec codec = new Http1CodecWrapper(realChain.httpStream());
                RealConnection connection = (RealConnection) realChain.connection();

                return realChain.proceed(request, allocation, codec, connection);
            }
        })
    .build();
孙才捷
2023-03-14

我的版本是3.13.0,离3.5.0不远,我也遇到了时间等待问题。

在深入研究源代码之后,我在CallServerInterceptor中找到了。java第142行:

if ("close".equalsIgnoreCase(response.request().header("Connection"))
    || "close".equalsIgnoreCase(response.header("Connection"))) {
   streamAllocation.noNewStreams();
}

在流量分配方面。java第367行:

public void noNewStreams() {
    Socket socket;
    Connection releasedConnection;
    synchronized (connectionPool) {
      releasedConnection = connection;
      socket = deallocate(true, false, false); // close connection!
      if (connection != null) releasedConnection = null;
    }
    closeQuietly(socket);
    if (releasedConnection != null) {
      eventListener.connectionReleased(call, releasedConnection);
    }
}

这意味着,如果请求或响应中存在“connection:CLOSE”头,okhttp将关闭连接

虽然问题提交已经很久了,但我希望这个答案能帮助遇到这个问题的人,祝你们好运。

 类似资料:
  • 问题内容: 我有一个http客户端,可以创建与主机的多个连接。我想设置它可以设置到特定主机的最大连接数。go的request.Transport中没有此类选项。我的代码看起来像 我希望这会创建1个连接。正如我在for循环中所说的那样。但这一直在创建无限数量的连接。 使用请求库的类似python代码仅创建一个连接。 由于某种原因,go代码没有重用http连接。 编辑:go代码需要关闭主体才能重用连接

  • 在我的应用程序与RabbitMQ服务器失去连接后,我在日志中看到 然后,当我的应用程序重新连接到RabbitMQ服务器时,我在日志中看到 但是,当应用程序重新连接到RabbitMQ服务器时,通道不会重新打开,在RabbitMQ管理控制台中,我可以在“通道”选项卡中看到没有任何通道。 重新连接后,在RabbitMQ服务器日志中: 我使用的是Spring Boot 1.5.3和Spring Boot

  • 问题内容: 我正在尝试使用OkHttp,但是它一直崩溃。有人可以快速浏览一下,看看是否知道发生了什么事。谢谢。 日志猫: 这是我尝试使用的代码示例。它来自在线教程(teamtreehouse.com)Java代码: 问题答案: OkHttp需要Okio,OkHttp可以使用Okio来实现快速I / O和可调整大小的缓冲区。您可以在此处下载Okio(最新的JAR)。 要么 Android gradl

  • 我正在开发一个需要从服务器下载数据的移动应用程序。它工作得很好,但在对服务器进行了一些更改后,来自改装的GET请求停止工作。它成功地发出请求,有时甚至得到成功的响应,但它从不调用onResponse()或onFailure()方法,因此应用程序只能挂起。 基本上有两种情况: 请求正确完成。Okhttp收到响应并打印内容,但它以一条消息结束(“阅读:意外的EOF!”)。之后应用程序停止工作。它不会冻

  • 如果我的理解是正确的,那么为什么不将的==和!=运算符重载为: 那我们就可以省下两个铸件了?

  • 然后用Gson lib将响应转换为我们需要的对象。 这来自Square/OKHTTP文档: 它的请求/响应API是用流畅的构建器和不变性设计的。它同时支持同步阻塞调用和带有回调的异步调用