为什么我在使用OkHttp时会收到java.net.SocketTimeoutException异常?

10

这是我的配置:

httpClient = new OkHttpClient.Builder()
        .callTimeout(Duration.ofSeconds(60))
        .connectTimeout(Duration.ofSeconds(60))
        .readTimeout(Duration.ofSeconds(60))
        .writeTimeout(Duration.ofSeconds(60))
        .build();

我有一个使用此客户端的多线程进程。运行几秒钟后,我得到以下错误信息:

java.net.SocketTimeoutException: timeout
at okio.Okio$4.newTimeoutException(Okio.java:232)
at okio.AsyncTimeout.exit(AsyncTimeout.java:286)
at okio.AsyncTimeout$2.read(AsyncTimeout.java:241)
at okio.RealBufferedSource.indexOf(RealBufferedSource.java:358)

如果我将超时时间设置为60秒,那怎么可能会发生这种情况?

编辑:
即使添加自定义调度程序也没有帮助:

Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequests(Integer.MAX_VALUE);
dispatcher.setMaxRequestsPerHost(Integer.MAX_VALUE);

技术细节:
与我之前所说的相反,我正在Linux机器上运行客户端和服务器:

客户端机器: net.ipv4.tcp_keepalive_time = 7200
服务器机器: net.ipv4.tcp_keepalive_time = 7200


1
@MichielLeegwater,这不是重复问题,而且链接的SO帖子也没有提供合理的解决方案。 - IsaacLevon
客户端 - MacOS,服务器 - 在Ubuntu上使用Flask。几秒钟就是几秒钟(比如说5秒)。 - IsaacLevon
在 Mac 上显示 sysctl net.inet.tcp 的输出,我认为这是您的代码遇到超时异常的原因。如果不是,请说明服务器正在执行什么操作(难道 Flask 不是 Python 的东西吗?)。 - Karol Dowbecki
你知道从日志中可以看出这个超时时间有多长吗?比如30秒或更短?还有这个客户端连接到哪个服务器? - Tarun Lalwani
1
这个问题已在okhttp 4.3中得到解决。请参考https://github.com/square/okhttp/issues/3146#issuecomment-569986444。 - Jose
显示剩余11条评论
2个回答

5
您很可能受制于操作系统强加的套接字超时。由于套接字由系统管理,因此Java无法对其进行扩展。根据这个伟大的答案,"更改TCP超时"部分:

不幸的是,由于TCP连接在操作系统级别上受到管理,Java不支持在每个套接字级别上配置超时,例如在java.net.Socket中。我发现一些尝试使用Java本地接口(JNI)创建调用本机代码以配置这些选项的Java套接字,但没有一个看起来被广泛社区采用或支持。

对于MacOS,您需要查看sysctl net.inet.tcp输出并查看系统限制。

谢谢Karol(我猜这个命令在Linux上也适用,对吧?) - IsaacLevon
总之,在应用程序级别(使用Java)定义超时时间是没有意义的,因为套接字超时时间是在操作系统级别上定义的。 - IsaacLevon
在我的 Linux 机器上,我看到:net.ipv4.tcp_keepalive_time = 7200。 - IsaacLevon
你可以在Java中减少超时限制,但不能将其增加到操作系统超时限制之上。目前我不明白你是在MacOS还是Linux上遇到了这个超时问题,因为这些限制取决于操作系统。请重新提出你的问题,明确指定引起问题的操作系统和代码,现在很难理解。 - Karol Dowbecki

2
我认为问题出在其他地方。超时确实有效。我在我的机器上尝试了以下操作,它可以工作30秒钟。原始答案: "最初的回答"
package test;

import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 *
 * @author jingged
 */
public class Test {
OkHttpClient httpClient = null;
     void initClient(){
        httpClient = new OkHttpClient.Builder()
        .connectTimeout(30, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .writeTimeout(30, TimeUnit.SECONDS)
        .build();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Test test = new Test();
        test.initClient();
        for(int i = 0;i<10;i++){
            new Thread(new Runnable(){
                @Override
                public void run() {
                   Request request = new Request.Builder().url("https://10.255.255.1").build();
                   Instant start = Instant.now();
                   try (Response response = test.httpClient.newCall(request).execute()) {
                        String s =  response.body().string();
                        System.out.println(s);
                    } catch (IOException ex) {
                        if (ex instanceof java.net.SocketTimeoutException){
                            System.err.print("Socket TimeOut");
                        }else{
                            ex.printStackTrace();
                        }
                    }
                   Instant end = Instant.now();
                    Duration interval = Duration.between(start, end);
                    System.out.println("Execution time " + interval.getSeconds()+" seconds");
                }

            }).start();
        }
    }
}

输出:

Execution time 30 seconds
Execution time 30 seconds
Execution time 30 seconds
Execution time 30 seconds
Execution time 30 seconds
Execution time 30 seconds
Execution time 30 seconds
Execution time 30 seconds
Execution time 30 seconds
Execution time 30 seconds
Socket TimeOutSocket TimeOutSocket TimeOutSocket TimeOutSocket TimeOutSocket TimeOutSocket TimeOutSocket TimeOutSocket TimeOutSocket TimeOut
BUILD SUCCESSFUL (total time: 32 seconds)

尝试上面的代码。上面代码中提到的url将产生TimeOut异常,因为它不存在,但超时时间应保持30秒。顺便提一下,我在MAC OS High Sierra上使用Java 8和NetBeans 8.2测试了这个代码,用的是okhttp3。"Original Answer"翻译成"最初的回答"。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接