RESTful风格的微服务-HTTP client
随着spring boot快速发展和HTTP2.0的支持力度增加,现在restful标准的微服务接口越来越多,选择一个优秀的HTTP client也越来越重要了。
前言
当我们在maven仓库中搜索关键字(http client)时,会出现十几页的搜索结果,可见在Java社区中http client之多,但是这些当中我们常见的不多。
列举几个常见的:
HttpURLConnection(JDK)
Apache Commons HttpClient(或被称为 Apache HttpClient 3.x)
Apache HttpComponents Client(或被称为 Apache HttpClient 4.x)
OKHttp(Square,Android应用中常见)
Asynchronous Http Client(Twitter等)
google-http-client-xxx(google各种版本)
以上这些HTTP client相对功能简单的,主要实现的功能是调用http协议饿接口,主要在协议层面的控制,比如,设置http method、设置超时时间(连接超时、读取超时)、请求参数、header、cookies以及响应的处理等。还有类似于Apache CXF中的client组件,它是一个经常用在web service服务开发中的组件。它们是在上述基础的http client之上封装了一层,对某一特定使用场景做一些定制化的包装,增加使用的便利性。
Retrofit(Square)、RestTemplate(Spring)、Feign(Netflix)这一类HTTP client是在RESTful标准的微服务中常见的,这一类将client的易用性做到了更好,并且更方便编写restful api的调用。一般还会提供消息转换、参数映射、提供注解等方式,在使用上写少量的代码即可完成功能,更像是一个RPC调用的client编写方式。
Feign
Feign使得Java HTTP客户端编写更方便。Feign灵感来源于Retrofit、JAXRS-2.0和WebSocket。Feign最初是为了降低统一绑定Denominator 到HTTP API的复杂度,不区分是否支持 RESTful。
Feign还以子项目的方式提供了多种Client实现,比如(feign-httpclient、feign-okhttp、feign-ribbon),它们集成了当前比较流行的Http Client组件,如Apache HttpClient、Okhttp、Ribbon等,且其默认的Client实现为HttpURLConnection。
Feign还提供了请求和响应数据格式的编码解码器,用于解析json报文的有feign-gson、feign-jackson等,以及用于解析xml报文的有feign-sax、feign-jaxb等。
RestTemplate
RestTemplate是spring web框架中提供的restful接口调用工具,它也是针对多个基础的http client组件做了集成,如Apache HttpClient、Okhttp等,且其默认的Client实现为HttpURLConnection。
目前(5.0.4.RELEASE)的 RestTemplate 主要有四种 ClientHttpRequestFactory 的实现,它们分别是:
基于 JDK HttpURLConnection 的 SimpleClientHttpRequestFactory
基于 Apache HttpComponents Client 的
HttpComponentsClientHttpRequestFactory基于 OkHttp 3 的 OkHttpClientHttpRequestFactory
基于 Netty4 的 Netty4ClientHttpRequestFactory(已弃用,已经被reactor相关取代)
其中每个都有异步的http client实现,但是已经已经弃用(AsyncRestTemplate已经被WebClient取代)。
但是与Feign相比的缺点是:HTTP Get方法不可以在request body中传递参数,只能是基于URL Template的方式传递参数(虽然很多人不提倡使用HTTP get发送数据);调用方式相对繁琐;设置请求的Http header参数复杂。
优点就是有具有spring框架的优良传统,扩展性特别好。
后来通过查阅资料发现 RestTemplate 默认是使用 spring 自身的 SimpleClientHttpRequestFactory 创建请求对象和对其进行相关设置(如请求头、请求体等),它只支持 PUT 和 POST 方法带请求体,RestTemplate 的 DELETE 方法不支持传入请求体是因为 JDK 中 HttpURLConnection 对象的 delete 方法不支持传入请求体(如果对 HttpURLConnection 对象的 delete 方法传入请求体,在运行时会抛出 IOException)。我尝试使用HttpComponentsClientHttpRequestFactory创建请求对象,依然能在Get方法中带请求体。
如何选择一个优秀的HTTP client
虽然目前来看服务之间调用大部分还是采用的RPC和消息队列,但是目前随着微服务的解决方案越来越多样性,也有很多人选择的HTTP这种通用性很强的协议。
RPC的优势在于,它们基本都使用了基于NIO的高效的网络传输模型,并且针对服务调用场景专门设计了协议和序列化技术,还对传输数据做了压缩处理。HTTP的优势在于成熟稳定、实现简单、支持广泛、兼容性良好、防火墙友好、消息的可读性高。一般在开放API、跨平台的服务间调用和对性能要求不苛刻的场景(HTTP/2可提高性能)中广泛使用。
优秀的HTTP client需要具备的特性:
连接池
超时间的设置(连接超时、读取超时等)
是否支持异步
请求和响应的编解码
可扩展性
经过我的使用对比,还是觉得Feign的HTTP client使用起来比较方便,推荐使用。
HttpURLConnection(JDK)的坑
默认情况不允许修改受限制的Header中的值,受限制的Header如下:
private static final String[] restrictedHeaders = { /* Restricted by XMLHttpRequest2 */ //"Accept-Charset", //"Accept-Encoding", "Access-Control-Request-Headers", "Access-Control-Request-Method", "Connection", /* close is allowed */ "Content-Length", //"Cookie", //"Cookie2", "Content-Transfer-Encoding", //"Date", //"Expect", "Host", "Keep-Alive", "Origin", // "Referer", // "TE", "Trailer", "Transfer-Encoding", "Upgrade", //"User-Agent", "Via"};
可以通过在JVM启动参数中添加:-Dsun.net.http.allowRestrictedHeaders=true,来设置允许修改这些HTTP Header。
在使用feign调用http接口时,如果在请求体中写入数据,GET方法会被转成POST方法发送请求,导致服务端报出不支持POST方法的405错误,其实是get方法不能有request body,会制造一定的问题排查困难。查看HttpURLConnection的源码发现如下代码块:
if (!this.doOutput) { throw new ProtocolException("cannot write to a URLConnection if doOutput=false - call setDoOutput(true)"); } else { if (this.method.equals("GET")) { this.method = "POST"; } }
HTTP的GET方法是否可以带请求体
HttpURLConnection 不可以
OkHttpClient 报错:method GET must not have a request body.
ApacheHttpClient 可以
这几天抽空对HTTP client的使用做了一些调研,总结出了这篇文章,有不全面的或者偏差的点,请在评论中讨论。
作者:rabbitGYK
链接:https://www.jianshu.com/p/6adb1fb56afc
共同学习,写下你的评论
评论加载中...
作者其他优质文章