大蟒蛇python教程共享HttpClient连接池及重试机制解析

一、httpclient

简介

httpclient 是apache jakarta common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 http 协议的客户端编程工具包,基于标准的java语言。

功能介绍

  • 支持http和https协议
  • 实现了http的方法,get,post,put,delete等方法。
  • 连接管理器支持多线程的应用。
  • 可以设置连接超时

使用方法

使用httpclient发送请求,接收响应可以分为一下几步:

  • 创建httpclient对象
  • 创建请求方法的实例,并且指定url
  • 发送请求参数,get请求和post请求发送参数的方式有所不同
  • 调用httpclient对象的execute方法,返回httpresponse对象
  • 调用httpresponse的getallheaders()、getheaders(string name)等方法可获取服务器的响应头;调用httpresponse的getentity()方法可获取httpentity对象,该对象包装了服务器的响应内容
  • 连接释放。无论成功与否,必须释放连接

二、httpclientutil

2.1 httpclient版本

笔者用到的版本是4.5.5,由于是maven工程,需要在pom文件引入对应的坐标。

2.2 项目中用到的工具类如下

代码中出现了@slf4j,作用是引入log,手动打印日志。这个注解是lombok的注解。

解释一下,什么是route?

route的概念可以理解为客户端机器到目标机器的一条线路,例如使用httpclient的实现来分别请求 www.163.com 的资源和 www.sina.com 的资源就会产生两个route。缺省条件下对于每个route,httpclient仅维护2个连接,总数不超过20个连接。

2.3 笔者着重说一下http连接池

1 为什么要使用http连接池?

延迟降低,如果不使用连接池,每次发起的http请求都会重新建立tcp连接(三次握手),用完就会关闭连接(4次握手),采用连接池则会减少这不是分时间的消耗。连接池管理的对象都是长连接。

支持更大的并发,由于连接池只适用于请求经常访问同一主机(或同一端口的情况),连接池避免了反复建立连接,抢占端口资源的情况,如果没用连接池,可能导致连接建立不了。

2 设置超时时间

首先要明白三个概念:sockettimeout,connecttimeout,connectionrequesttimeout。

  • sockettimeout:客户端和服务器读取数据的timeout
  • connecttimeout:客户端和服务器建立连接的timeout
  • connectionrequesttimeout:从连接池获取连接的timeout

3 解释:一次http请求

一次http请求,必定会有三个阶段,一:建立连接;二:数据传送;三,断开连接。

当建立连接在规定的时间内(connectiontimeout )没有完成,那么此次连接就结束了。后续的sockettimeoutexception就一定不会发生。只有当连接建立起来后,

也就是没有发生connectiontimeoutexception ,才会开始传输数据,如果数据在规定的时间内(sockettimeout)传输完毕,则断开连接。否则,触发sockettimeoutexception。

三、httpclient的重试机制

上面说了这么多,就是为了引出下面的重试问题。由于项目中要访问外部接口,访问接口的时候,偶尔会出现sockettimeoutexception:read timed out,其实就是客户端读取服务器的数据超时了。

3.1. 那么问题来了httpclient有没有重试策略?

使用poolinghttpclientconnectionmanager得到的internalhttpclient实例,是抽象类closeablehttpclient的一个实现。

看一下clientexecchain接口的实现类

HttpClient连接池及重试机制解析

简单看一下build()方法

自上而下,创建了不同的clientexecchain实例。注意:创建对象的顺序就是执行器链的顺序

在构造closeablehttpclient实例的时候,判断是否关闭了自动重试功能,automaticretriesdisabled默认是false。如果没有指定执行器链,就用retryexec。默认的重试策略是defaulthttprequestretryhandler。

如果重写了serviceunavailableretrystrategy接口,或者使用了defaultserviceunavailableretrystrategy,serviceunavailableretryexec也会加入到执行器链里。

同理,redirecthandlingdisabled默认是false,redirectexec也会加入到执行器链,并且会最先执行。

3.2 执行流程

前面已经看到我们使用的htticlient本质上是internalhttpclient,这里看下他的执行发送数据的方法。

首先经过redirectexec,redirectexec里面调用serviceunavailableretryexec的excute(),进入serviceunavailableretryexec后,调用retryexec的excute(),进入发到retryexec后,调用protocolexec的execute(),最后调用mainclientexec的excute()。

执行器链结束后,执行httprequestexecutor的excute(),excute()方法调用了自己的dosendrequest()。

之后一步一步的返回,遇到异常进行处理。

下面是retryexec发送请求的部分

当发生ioexception,判断是否要重试。如果重试则记录相应的次数,如果不重试,就抛出异常并且退出。

通过构造函数,可以看出:

重试3次请求成功,就不再重试interruptedioexception、unknownhostexception、connectexception、sslexception,发生这4种异常不重试

  • 重试3次
  • 请求成功,就不再重试
  • interruptedioexception、unknownhostexception、connectexception、sslexception,发生这4种异常不重试

​ 关于默认的重试策略

  • 如果超过三次不进行重试
  • 以上4中异常及其子类不进行重试
  • 同一个请求在异步任务中已经停止,不进行重试
  • 幂等的方法可以进行重试,比如get,含有http body都可以认为是非幂等
  • 请求没有发送成功,可以进行重试

问题来了,发送成功的请求是怎么样的?

下面的代码在httpcorecontext里面,httpcorecontext是httpcontext的实现类

当前httpcontext中的http.request_sent设置为true,则认为已经发送成功。

httprequestexecutor的excute(),调用了自己的dosendrequest()。

判断是否携带实体的方法

注:httpentityenclosingrequest是一个接口

httpentityenclosingrequestbase是实现httpentityenclosingrequest的抽象类

下图可以看出,httppost和httpput是httpentityenclosingrequestbase的子类

HttpClient连接池及重试机制解析

简要分析一下,上述的操作过程

  • 开始将http.request_sent设置为false
  • 通过流flush数据到客户端
  • 然后将http.request_sent设置为true

显然conn.flush()是可以发生异常的。注意:conn都是从连接池获取的。

3.3 关闭重试

默认是开启重试的,可以在创建httpclientbuilder的时候,调用下面的方法关闭。

四、总结

4.1重试发生的条件

只有发生ioexception才会发生重试

interruptedioexception、unknownhostexception、connectexception、sslexception,发生这4种异常不重试

get方法可以重试3次,post方法对应的socket流没有被flush成功时可以重试3次

4.2不发生重试的异常

  • interruptedioexception,线程中断异常
  • unknownhostexception,找不到对应host
  • connectexception,找到了host但是建立连接失败。
  • sslexception,https认证异常

4.3 实践中遇到的异常

另外,我们还经常会提到两种超时,连接超时与读超时:

1. java.net.sockettimeoutexception: read timed out

2. java.net.sockettimeoutexception: connect timed out

这两种超时都是sockettimeoutexception,继承自interruptedioexception,属于线程中断异常,不会进行重试。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持<计算机技术网(www.ctvol.com)!!>。

需要了解更多python教程分享HttpClient连接池及重试机制解析,都可以关注python教程分享栏目—计算机技术网(www.ctvol.com)!

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/pythontutorial/1068608.html

(0)
上一篇 2022年3月27日
下一篇 2022年3月27日

精彩推荐