最近項目中使用httpClient配置的RestTemplate,寫了一個http請求工具,設置了連接池,SocketTimeOut等參數。但是在高頻率的請求某個接口的時候,會報錯提示java.net.SocketException: Connection reset, 後來找到原因是請求方式寫錯了,POST請求被我發成了GET. 但是出現這種問題會是什麼原因呢。
代碼大致如下:
private static final String URL = "http://localhost:8081/test";
private ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("test-pool-%d").build();
private ExecutorService executorService = new ThreadPoolExecutor(300, 300, 0,
TimeUnit.SECONDS, new LinkedBlockingDeque<>(1000), threadFactory, new ThreadPoolExecutor.AbortPolicy());
@Autowired
private RestTemplate restTemplate;
@Override
public void run(String... args) throws Exception {
List<CompletableFuture> futureList = new ArrayList<>();
CountDownLatch latch = new CountDownLatch(300);
for (int i = 0; i < 301; i++) {
CompletableFuture future = CompletableFuture.runAsync(() -> {
try {
latch.countDown();
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
ResponseEntity<Object> res = restTemplate.exchange(URL, HttpMethod.GET, null, Object.class);
log.info("返回的是:{}", res.getBody());
}, executorService);
futureList.add(future);
}
CompletableFuture<Void> future = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[]{}));
log.info("都派發結束了");
future.join();
log.info("都請求結束了");
}
異常詳情我就不貼了,大致就是請求完以後還沒有來得及讀取返回結果,連接就斷開了。本着研究問題的角度,啓動了另一個服務,另一個服務提供一個http接口,同時設置tomcat的SocketTimeOut 爲0, 也就是服務端處理結束後,socket連接立馬就關閉。使用的SpringBoot內置的tomcat, 所以直接在代碼設置。
@Configuration
public class TomcatConfig {
@Bean
public TomcatServletWebServerFactory configTomcat() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addConnectorCustomizers(connector -> {
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
protocol.setKeepAliveTimeout(0);
});
return tomcat;
}
}
預期是300個請求併發執行,得到300個成功的結果,但多次執行,總是隻能成功一百多兩百多個,後面就是報的Connection reset 的錯誤了。 再嘗試取消protocol.setKeepAliveTimeout(0); 設置,或者設置爲30000 ms, 所有300次請求都能成功。可見原因就是,服務端的SocketTimeOut 時間太短了,客戶端還沒來得及接收完數據,鏈接就被斷開了。