說下場景,我的程式在多執行緒場景下一個回圈體中處理業務資料,其中需要呼叫一個外部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客戶端,或者至少為每個主機名保持一個客戶端實體,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/549974.html
標籤:其他
上一篇:java -- 執行緒