1. 背景
笔者工作中有调用第三方服务提供商REST服务的场景,需要对这些服务提供商接口的请求响应情况做一些打点,记录性能和稳定性相关数据,以对服务接口做质量评估。
如果把打点逻辑埋进业务代码中,每个调用点都需要写相同的逻辑,这引发了典型的高耦合问题,无疑不合理。笔者的视角转为:能否在项目中使用的Apache HttpClient(项目中使用的版本是4.5.3) 工具(以下简称httpclient)上做扩展,完成打点功能。 完成类似的打点功能有两种常用的解决方案。 1. 责任链模式。其代表是servlet中的filter。 2. 切面。其代表是Spring 的AOP增强。 3. 运行时使用JVM Agent挂载技术做类增强。其代表是Btrace。 运行时动态增强不侵入代码,但是可能会带来安全性和性能的不确定性,并且不符合公司的技术栈,所以方案聚焦到了1或者2。笔者带着这两种预选的方案思路开始baidu和google,但是没发现靠谱的解决方案,于是开始Apache HttpClient 4.5.3了读码之旅,也才有了这篇总结。2. Apache HttpClient调研
2.1 一般使用流程 Httpclient的一般使用步骤: 1. 调用 HttpClientBuilder.create() 创建一个HttpClientBuilder; 2. 对HttpClientBuilder进行配置,包括超时时间、Keepalive等; 3. 调用 HttpClientBuilder.build() 创建CloseableHttpClient; 4. 创建HttpUriRequest(如HttpGet、HttpPost),并填充请求参数; 5. 调用Clo seableHttpClient.execute 执行实际发送请求,并接受响应; 6. 处理返回的CloseableHttpResponse响应,得到返回的数据。 我们的需求是要在发起请求前、收到响应后打点,所以重点调研 CloseableHttpClient.execute 的执行流程。 2.2 CloseableHttpClient.execute执行流程 翻越层层的封装,看到了这一个比较清晰的调用关系:InternalHttpClient.execute -> InternalHttpClient.doExecute -> ClientExecChain.execute。从执行的类名就可以管中窥豹: 这是一个责任链模式,责任链的变量名是 execChain 。接下来看链条中有哪些链节点以及如何构建此执行链。 InternalHttpClient的execChain是由 HttpClientBuilder.build() 实际创建并传入。实际创建过程的细节不表,笔者梳理了一张execChain图:ProtocolExec.execute还是比较清晰的,先是设置一些HTTP协议的头,然后重点代码来了:
1. HttpClientBuilder.build()中,可以通过HttpClientBuilder配置,增删可选的内建处理节点,但是不支持添加自定义处理节点。
2. 如果想要在execChain添加自定义处理节点,需要提供自定义的HttpClientBuilder。
3. 解决方案1. 自定义Exec处理节点:MonitorExec implements ClientExecChain。构造时需要传入执行责任链execChain;
2. MonitorExec.execute伪代码:
excuteBefore(); try { response = this.requestExecutor.execute(); Integer httpstatus = response.getStatusLine().getStatusCode(); if (httpstatus>=300) { not200Fail(); } else { success(); } return response; } catch (Exception e) { exceptionFail(); throw e; }
3. 提供自定义HttpClientBuilder:MonitorHttpClientBuilder,build方法中把自定义MonitorExec的加入到execChain中。MonitorHttpClientBuilder的其他方法,以及build方法里的其他逻辑不变;
4. 用户使用时,将HttpClientBuilder替换成自定义的MonitorHttpClientBuilder。 这种方案,需要用户将HttpClientBuilder替换成自定义的MonitorHttpClientBuilder,一是对代码业务代码有一定侵入,二是对httpclient版本有依赖,不够完美,但还能够接受。只是copy HttpClientBuilder的代码会不会有开源许可证的问题? 上述方案简述中,没有说明自定义MonitorExec在整个execChain所处的顺序位置。实际上这个问题还经历了一些波折。笔者第一反应是添加在ProtocolExec之后,MainClientExec之前,这样做的最大好处是自定义的MonitorExec可以获取到之前所有节点的增强结果,能得到的数据最丰富。按照这个思路实现后,成功、异常失败、非200失败三种功能测试都符合预期,但是在请求某个URL接口时出现了如下问题: 每次请求,MonitorExec都会被执行两次。增加日志,调试代码发现问题就出在MonitorExec所处execChain执行顺序上。用一张图说明:![c8cee3aa3386a082dd65cf3d88974388.png](https://i-blog.csdnimg.cn/blog_migrate/a713e0ed6d2dde66f9d10a42175127b8.png)
也许想看
除敏捷实践外,项目管理还能做点什么?
你应该知道的异步服务数据防丢失方案设计
一文弄懂Nginx最核心的配置
[手把手系列之]Docker 部署 vue 项目
好文我在看?