OkHttp Address already in use: no further information异常

  说下场景,我的程序在多线程场景下一个循环体中处理业务数据,其中需要调用一个外部http接口去获取一些数据,程序总会在在本地执行一段时间后会抛出Address already in use: no further information错误。

  这是大量并发场景下出现的问题,经过查阅原因是OkHttp的链接没有被有效回收和复用导致的端口资源占用,okHttp在发起请求调用外部接口时也会占用本地的端口资源,因为okHttp需要建立Socket链接来和对方通信,端口是本地一个随机的未被使用的端口,okHttp会尽量复用这些资源以减少服务器消耗,但如果在短时间内出现大量的请求都在创建新的okHttp对象去发起请求,那么每个okHttp对象的资源占用都不会共享,导致资源被浪费,亦或者没有及时关闭响应体,比如response.close()。最终,资源被耗尽,出现 Address already in use: no further information。而我的调用代码就是为了图方便简单的每次请求创建一个新的OkHttpClient

  解决方式很简单,使用单例的OkHttpClient并配置好超时时间,同时注意资源关闭,如果是在Spring环境下做个配置就行,其中线程数和连接回收时间根据自己情况进行调整:

@Configuration
public class HttpClientConfiguration {
    @Bean
    public OkHttpClient httpClient(){
        return  new OkHttpClient().newBuilder()
                //设置线程池              最大200线程     最大保持10秒连接既自动回收
                .connectionPool(new ConnectionPool(200, 10, TimeUnit.SECONDS))
                //设置超时时间
                .connectTimeout(5, TimeUnit.SECONDS).readTimeout(5,TimeUnit.SECONDS)
                .writeTimeout(5,TimeUnit.SECONDS).build();
    }
}
 
 
class Test{
 
  @Autowired
  private OkHttpClient httpClient;
 
  public JsonNode getFieldData(String token){
        String body = "{xxx}";
 
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"),body);
        Request request = new Request.Builder().url("http://xxxx").post(requestBody).header("token",token).build();
 
        //请求成功结果正常则返回数据
        try (Response response = httpClient.newCall(request).execute()){
            if(response.isSuccessful()){
                String resultJsonStr = response.body().string();
                JsonNode resJson = objectMapper.readTree(resultJsonStr);
                return resJson.get("result");
            }
        }catch (Exception e){
            logger.error(e.getMessage(),e);
        }
        return objectMapper.createObjectNode();
    }
}

另外,根据找到的资料,单例模式下的OkHttp性能也会更好:

如果okhttp客户端不是单例的,那么每次发送请求时都会创建一个新的客户端对象,这样会导致资源浪费和性能下降,而且可能会导致内存泄漏。okhttp客户端是设计成单例的,它可以共享连接池、响应缓存和配置。因此,建议使用单例模式来创建和管理okhttp客户端,或者至少为每个主机名保持一个客户端实例。

热门相关:斗神战帝   霸皇纪   仗剑高歌   薄先生,情不由己   第一神算:纨绔大小姐