网关使用 Apache HttpClient 连接池出现异常
最近网关发版出现大量如下异常,而有如下文章:
org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
两个主机建立网络连接是一个比较复杂的过程,涉及到多个数据包的交换。建立网络连接本身就很耗时间,而 Http 连接需要三次握手,开销就更大。但是可以直接使用已经建立好的 Http连接,那么花费就比较小。耗时更短,从而提高访问的吞吐量。
传统的 HttpURLConnection 并不支持连接池,如果要实现连接池机制,那么需要自己来管理连接对象。对于网络请求这种底层相对复杂的操作,没有一定经验的程序员很难写好这块代码逻辑。
除了 HttpURLConnection,常用的Http Client 要数 Apache 的 HttpClient。一般情况下, HttpClient 已经能满足业务需求了;但是在网关这种高并发场景下,使用 HttpClient 进行大量的请求网络,还是需要用连接池才能提高网关的TPS,不然很容易成为网关的瓶颈。
Apache 的HttpClient的早期版本,提供了PoolingClientConnectionManager、DefaultHttpClient 等类来实现 Http连接池,但这些类在 4.3.x 版本之后大部分已过时。后续版本提供了PoolingHttpClientConnectionManager 等类进行 Http连接池的实现。PoolingHttpClientConnectionManager 是一个 Http 连接池管理器,用来服务于多线程时并发获取连接的请求。每个路由(IP)将池化不大于 defaultMaxPerRoute 参数的连接。
pom.xml文件引入依赖
org.apache.httpcomponents httpclient 4.5.13 org.apache.httpcomponents httpcore 4.4.15 org.apache.httpcomponents httpclient-cache 4.5.13
定义连接池主要参数
publicclassHttpPoolProperties{private Integer maxTotal;privateIntegermaxRoute;private Integer defaultMaxPerRoute;private String hostName;private Integer port;private Integer connectTimeout;private Integer connectionRequestTimeout;privateIntegersocketTimeout;private Integer validateAfterInactivity;//TODO 省略get set 方法}
maxTotal:允许跨所有路线的最大连接数
defaultMaxPerRoute:所有路由默认最大连接数
maxPerRoute:指定某个路由的最大连接数
defaultMaxPerRoute与maxPerRoute这两个参数存在优先级关系。具体可以参看 AbstractConnPool 类的如下方法的引用关系
private int getMax(T route) {Integerv=(Integer)this.maxPerRoute.get(route);return v != null ? v : this.defaultMaxPerRoute;}
connectTimeout:多久等待与远程服务器抛出超时异常之前建立连接
socketTimeout:多久等待服务器抛出超时异常之前,各种消息响应
http://docs.oracle.com/javase/1.5.0/docs/api/java/net/SocketOptions.html#SO_TIMEOUT
connectionRequestTimeout:多久试图抛出异常之前,先从连接池中的连接时等待(连接池不会立即返回,如果所有的连接被检出)
staleConnectionCheckEnabled:可以在潜在的 IOExceptions 成本的性能有所提高被禁用
http://hc.apache.org/httpclient-3.x/performance.html#Stale_connection_check
获取 HttpClient 对象
public static CloseableHttpClient createHttpClient(HttpPoolProperties httpPoolProperties) {ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory.getSocketFactory();Registryregistry = RegistryBuilder. create().register("http", plainsf).register("https", sslsf).build();PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);// 将最大连接数增加cm.setMaxTotal(httpPoolProperties.getMaxTotal());// 将每个路由基础的连接增加cm.setDefaultMaxPerRoute(httpPoolProperties.getDefaultMaxPerRoute());if(StringUtils.hasText(httpPoolProperties.getHostName()) && httpPoolProperties.getPort()!=null) {HttpHost httpHost = new HttpHost(httpPoolProperties.getHostName(), httpPoolProperties.getPort());// 将目标主机的最大连接数增加cm.setMaxPerRoute(new HttpRoute(httpHost), httpPoolProperties.getMaxRoute());}// 请求重试处理HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {// 如果已经重试了5次,就放弃if (executionCount >= 5) {return false;}//如果服务器丢掉了连接,那么就重试if (exception instanceof NoHttpResponseException) {return true;}// 不要重试SSL握手异常if (exception instanceof SSLHandshakeException) {return false;}// 超时if (exception instanceof InterruptedIOException) {return false;}// 目标服务器不可达if(exceptioninstanceofUnknownHostException){return false;}// 连接被拒绝if (exception instanceof ConnectTimeoutException) {return false;}// SSL握手异常if (exception instanceof SSLException) {return false;}HttpClientContext clientContext = HttpClientContext.adapt(context);HttpRequest request = clientContext.getRequest();// 如果请求是幂等的,就再次尝试if (!(request instanceof HttpEntityEnclosingRequest)) {return true;}return false;}};CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).setRetryHandler(httpRequestRetryHandler).build();return httpClient;}
记得点「赞」和「在看」↓
爱你们
相关阅读
-
世界热推荐:今晚7:00直播丨下一个突破...
今晚19:00,Cocos视频号直播马上点击【预约】啦↓↓↓在运营了三年... -
NFT周刊|Magic Eden宣布支持Polygon网...
Block-986在NFT这样的市场,每周都会有相当多项目起起伏伏。在过去... -
环球今亮点!头条观察 | DeFi的兴衰与...
在比特币得到机构关注之后,许多财务专家预测世界将因为加密货币的... -
重新审视合作,体育Crypto的可靠关系才能双赢
Block-987即使在体育Crypto领域,人们的目光仍然集中在FTX上。随着... -
简讯:前端单元测试,更进一步
前端测试@2022如果从2014年Jest的第一个版本发布开始计算,前端开发... -
焦点热讯:刘强东这波操作秀
近日,刘强东发布京东全员信,信中提到:自2023年1月1日起,逐步为...