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灵感来源于RetrofitJAXRS-2.0WebSocket。Feign最初是为了降低统一绑定Denominator 到HTTP API的复杂度,不区分是否支持 RESTful。

Feign还以子项目的方式提供了多种Client实现,比如(feign-httpclientfeign-okhttpfeign-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 的实现,它们分别是:

  1. 基于 JDK HttpURLConnection 的 SimpleClientHttpRequestFactory
  2. 基于 Apache HttpComponents Client 的
    HttpComponentsClientHttpRequestFactory
  3. 基于 OkHttp 3 的 OkHttpClientHttpRequestFactory
  4. 基于 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)的坑

  1. 默认情况不允许修改受限制的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。

  1. 在使用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的使用做了一些调研,总结出了这篇文章,有不全面的或者偏差的点,请在评论中讨论。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容