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客户端,或者至少为每个主机名保持一个客户端实例。
热门相关:斗神战帝 霸皇纪 仗剑高歌 薄先生,情不由己 第一神算:纨绔大小姐