问题
- java.net.SocketException: Connection reset
现象
- 第一次请求,可能成功,可能失败,但是从第二次开始一直失败,隔断时间再次访问,可能成功,可能失败,连续请求的话都是失败
弯路
- 耗时接口的封装最大超时时间5s -> 30s,确保集团能够正常返回
- 某些平台超时3次后限制接口调用
httpclint的post方法
- 改造前
private String executePost(String url, String requestBody) throws Exception { String responseBody = ""; PostMethod method = null; try { HttpClient client = new HttpClient(); client.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8"); client.getHttpConnectionManager().getParams().setConnectionTimeout(10*1000); method = new PostMethod(url); method.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); method.setRequestHeader("Cookie", "JSESSIONID=F3CC0575913DE1C788D8F000A5524FB3; 0yHmMQUi4f9KO=5AuDkrob9X6NMNHqy.laR8DNMYG.am6Y7vEDX17fj8OjIRUJ9eEAW2n0yWH7mnVlJCnnnXTgLuGAXeS9nr9N8Ea; WT_FPC=id=17dd3ad154dcb5557941647484475935; 5224c5f07e5da28b61=26eb640ae4a64f32311705753eb90f37; WT_SS=1652929249977765b8e56b; Hm_lvt_9004434f3a31031836fd6a07df4d1ae8=1652153360,1652254394,1652750419,1652929250; bjeshoptest=bjeshoptest; jsession_id_scsva=nB72A66AC6FF6C9137360E15C0D6CDAC5-1; WT_si_n=MM_ADV_HOME; 0yHmMQUi4f9KP=53LKuGKtmzE9qqqDqC9VaiaUuYLzaICp8Mo1GZ5Q1ZT3fvcQVY7aZ0feKR6onzWB_eY2XGuihtGEv_ZLZPzH2oVzEueSLsWI2y0YymbQbdil1fDgX0LO1paBZKcpQQ88M4ukGVybeTjeWGZCVpWZ_svEVMQV68Ut.fiZUEktEH23TzKsU_8iu4O4TV0TCfSj2d16pNEMSKXkLR7f7J5bInFPgLFRisEQ954egftQXAq9vZG.ilJEJTwdqrzg0vTQjOlOskOZjMR_hrN5saM0Lxe; Hm_lpvt_9004434f3a31031836fd6a07df4d1ae8=1652929673"); method.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8"); InputStream input = new ByteArrayInputStream(requestBody.getBytes("UTF-8")); method.setRequestEntity(new InputStreamRequestEntity(input)); int statusCode = client.executeMethod(method); if (HttpStatus.SC_OK == statusCode) { byte[] bys = method.getResponseBody(); responseBody = new String(bys, "UTF-8"); } } catch (Exception e) { throw e; } finally { if (method != null) method.releaseConnection(); } return responseBody; }
- 问题
- 使用HttpClient都是使用类似上面的代码,因为Apache官方例子就是如此。但在使用HttpClient会发现如果不断循环发送大量请求到服务器会导致APACHE服务器的连接被占满,后续的请求便排队等待,程序陷入“假死”状态。
- Timeout 30
- KeepAlive On #表示服务器端不会主动关闭链接
- MaxKeepAliveRequests 100
- KeepAliveTimeout 180
- 因此这样的配置就会导致每个链接至少要过180S才会被释放,这样在大量请求访问时就必然会造成连接被占满,请求等待的情况。在 通过DEBUG后发现HttpClient在method.releaseConnection()后并没有把连接关闭,这个方法只是将连接返回给 connection manager。如果使用HttpClient client = new HttpClient()实例化一个HttpClient connection manager默认实现是使用SimpleHttpConnectionManager。SimpleHttpConnectionManager有个构 造函数如下
public SimpleHttpConnectionManager(boolean alwaysClose) { super(); this.alwaysClose = alwaysClose; }
- 使用HttpClient都是使用类似上面的代码,因为Apache官方例子就是如此。但在使用HttpClient会发现如果不断循环发送大量请求到服务器会导致APACHE服务器的连接被占满,后续的请求便排队等待,程序陷入“假死”状态。
解决方案
- 方法一:
- 把事例代码中的第一行实例化代码改为如下即可,在method.releaseConnection();之后connection manager会关闭connection 。
Java代码 HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) );
- 把事例代码中的第一行实例化代码改为如下即可,在method.releaseConnection();之后connection manager会关闭connection 。
-
方法二:
- 实例化代码使用:HttpClient client = new HttpClient();在method.releaseConnection();之后加上
Java代码 ((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown(); shutdown源代码很简单,看了一目了然 Java代码 public void shutdown() { httpConnection.close(); }
- 实例化代码使用:HttpClient client = new HttpClient();在method.releaseConnection();之后加上
-
方法三:
- 实例化代码使用:HttpClient client = new HttpClient();
在method.releaseConnection();之后加上 client.getHttpConnectionManager().closeIdleConnections(0);此方法源码代码如下: Java代码 public void closeIdleConnections(long idleTimeout) { long maxIdleTime = System.currentTimeMillis() - idleTimeout; if (idleStartTime <= maxIdleTime) { httpConnection.close(); } } 将idleTimeout设为0可以确保链接被关闭。 以上这三种方法都是有客户端主动关闭TCP链接的方法。下面再介绍由服务器端自动关闭链接的方法。
- 实例化代码使用:HttpClient client = new HttpClient();
- 方法四:
- 代码实现很简单,所有代码就和最上面的事例代码一样。只需要在HttpMethod method = new GetMethod("http://www.apache.org");加上一行HTTP头的设置即可
Java代码 method.setRequestHeader("Connection", "close");
- 代码实现很简单,所有代码就和最上面的事例代码一样。只需要在HttpMethod method = new GetMethod("http://www.apache.org");加上一行HTTP头的设置即可