最近工作中遇到了一个rpc高频调用出现
java.net.BindException: 地址已在使用
问题,由于线下测试的时候并没有庞大的数据量来支撑,这个问题并没有在测试阶段被发现。研究发现,出现改问题的主要是因为短时间内OS的socket被迅速烧光,程序初启动前20秒内一切正常,后面开始狂抛此类异常。之前的http调用主要使用httpclient框架实现,代码如下:
HttpClient httpClient = HttpClient(); GetMethod method = GetMethod(); httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(); httpClient.getHttpConnectionManager().getParams().setSoTimeout(); { httpClient.executeMethod(method); } (Throwable t) { System..println(); } String responseBodyAsString = ; { responseBodyAsString = method.getResponseBodyAsString(); System..println(); } (Throwable t) { System..println(); }
该段代码被封装在一个普通的发放内 每次调用传入相应的URL和参数即可。正常情况下并不会出现什么问题。深入分析代码后发现httpclient 对象内部会默认创建一个HttpClientParams对象和SimpleHttpConnectionManager对象 manager 对象内部会持有一个connection 这里可以类比与jdbc 中的connection。非常蛋疼的一点是这个连接在执行完请求后并不会被立即关闭。SimpleHttpConnectionManager对象中有这样一句
alwaysClose = ;
这意味着该连接默认不会被关闭。当然随着方法被执行完,这些对象就成了垃圾数据会被gc掉。但是底层资源的释放速度可就不好说了。恰恰在于上面的那一大段代码处于方法里,在高频调用这个方法时,会创建超多的底层tcp连接。下面的故事就不用多说了。
可能有人会想到既然没关掉这个连接那就想办法关掉就是了 。本人初期也是这么想的 关掉不就行了吗。网上荡了一份代码
HttpClient httpClient = HttpClient(HttpClientParams(),SimpleHttpConnectionManager());
据说这样就可以保证连接会断开(客户端断开),然并卵~~~
继续坑,既然断掉也不行那就复用好吧,翻看源码发现
HttpConnectionManager
这个接口的实现有三个(包含SimpleHttpConnectionManager),其中一个叫做
MultiThreadedHttpConnectionManager
发现了曙光,为多线程准备的ConnectionManager .搞来玩玩。 于是乎就出现了以下的代码
MultiThreadedHttpConnectionManager =MultiThreadedHttpConnectionManager(); { .getParams().setMaxTotalConnections(); .getParams().setDefaultMaxConnectionsPerHost(); }
需要指出的是,这个管理器可以管理多个连接池,底层是一个Map key为host Value 则是connectionPool
setDefaultMaxConnectionsPerHost 就是指每个host允许建立的最多连接数。不需要太多!
结合
new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true));
想到了什么 ?
是的 :
new HttpClient(new HttpClientParams(),cm);
下面的代码原封不动,但是要主要的是获取完数据后记得调用
method.releaseConnection();
正所谓好借好还,再借不难嘛
这些连接会被管理器揣在兜里需要的时候问他申请,有就给你,没有嘛一边呆着等
个人猜测是借助了http连接的keepAlive 特性。实现多个请求复用一个通道。
注:本文借鉴了多篇该领域的文章,同时结合自身的一些实践心得整理而成