菜单

RESTful风格的微服务

2020年1月11日 - 4166am金沙下载
RESTful风格的微服务

本文由码农网 – Sandbox
Wang原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!

Android中大多数应用都会发送和接受HTTP请求,在Android
API中主要由两个HTTP请求的相关类,一个是HttpURLConnection,另一个是Apache
HTTP
Client。这两个类实现的HTTP请求都支持HTTPS协议,基于流的上传和下载,可配置超时时间,IPv6和连接池。Apache
HTTP
Client
DefaultHttpClient和同类的AndroidHttpClient都是可扩展的类。它们有大量且灵活的API,适用于网页浏览器开发。同时它们比较稳定并且bug较少。但是繁多的API的现实下,对其改善与保持兼容性不可得兼,明显Android团队的精力已然不在Apache
HTTP
Client。HttpURLConnectionHttpURLConnection是一个通用,轻量的实现,可以满足大多数的程序进行HTTP请求。这个类虽然一开始比较简陋,但是其主要的几个API使得我们更容易进行稳定改善。连接池污染在冻酸奶之前,HttpURLConnection有着一些烦人的bug。最烦人的就是调用一个可读的InputStream的close方法会污染连接池。我们需要禁用连接池绕开这个问题,如下代码可以禁用连接池。

随着spring
boot快速发展和HTTP2.0的支持力度增加,现在restful标准的微服务接口越来越多,选择一个优秀的HTTP
client也越来越重要了。

引言:如果你也是开发者的话,你很可能已经知道PoLA法则(Principle of
Lease
Astonishment)。那么,看看这篇文章讲述的充满奇幻色彩的调试经历,来见识一下PoLA是如何与HttpURLConnection发生了关联。

private void disableConnectionReuseIfNecessary() { // HTTP connection reuse which was buggy pre-froyo if (Integer.parseInt(Build.VERSION.SDK)  Build.VERSION_CODES.FROYO) { System.setProperty("", "false"); }}

当我们在maven仓库中搜索关键字(http
client)时,会出现十几页的搜索结果,可见在Java社区中http
client之多,但是这些当中我们常见的不多。

如果你和我一样也是开发者的话,你很可能已经听说过“PoLA”原则,或者叫作“产生最少意外”原则。意思非常简单,就是不要让你的用户感到惊讶。或者更明确一些,就像本文这种情况,不要让另外一个开发者感到惊讶。不幸的是,我上个星期就遇到了大大超出我意外的事情,我们有个服务的客户调用端总是发出一些垃圾的请求。

压缩数据与大小从2.3开始,我们默认对返回的响应进行了压缩,HttpURLConnection会自动为发出去的请求加上Accept-Encoding:
gzip这个头信息。如果gzip压缩的响应有问题,可以通过下面代码禁用gzip。

列举几个常见的:

图片 1

urlConnection.setRequestProperty("Accept-Encoding", "identity");

你说垃圾请求吗?是的,就像这样,我们完全不清楚这些请求是从哪里来的。又是这样一个时刻,经理们毫无头绪,抱头乱窜,惊呼“我们肯定是被黑客攻击了”,或者 ”有人把防火墙给关掉了!!”

由于HTTP中的Content-Length头信息返回的是压缩后的大小,所以我们不能使用getContentLength()来计算未压缩数据的大小。正确的做法应该是读取HTTP响应中的字节,直到InputStream.read()方法返回为-1.HTTPs改进从Gingerbread开始,增加了对HTTPs链接的优化。在进行HTTPs请求之前,HttpsURLConnection会尝试使用服务器名字指示(Server
Name
Indication),这种技术可以让多个HTTPs主机共享一个IP地址。在HTTPs请求中,HttpsURLConnection也支持压缩和会话标签。一旦连接失败,HttpsURLConnection会不使用上面的三个特性进行重试。这样即可以保证在连接时高效率地连接到最新的服务器,也可以在不破坏兼容性的同时连接到旧服务器。响应缓存从4.0开始,HttpURLConnection引入了响应缓存机制。一旦缓存创建,后续的HTTP请求会按照下面情况处理完全缓存的响应会直接从本地存储中读取,响应很快,不需要网络连接。有条件的缓存必须由服务端进行freshness验证,比如client发出一个请求,如”Give
me /foo.png if it changed since
yesterday”,然后服务器端要么返回最新的内容,要么返回304未修改的状态。如果内容不变,则不下载。没有缓存的响应需要服务器处理,然后这些请求被缓存下来。对于低于4.0的版本,我们可以使用反射开启响应的缓存机制

以上这些HTTP
client相对功能简单的,主要实现的功能是调用http协议饿接口,主要在协议层面的控制,比如,设置http
method、设置超时时间(连接超时、读取超时)、请求参数、header、cookies以及响应的处理等。还有类似于Apache
CXF中的client组件,它是一个经常用在web
service服务开发中的组件。它们是在上述基础的http
client之上封装了一层,对某一特定使用场景做一些定制化的包装,增加使用的便利性。

无论如何,先说点背景情况吧,我们的项目里有自动记录活动日志的功能,当某些情况下,比如一个进程启动的时候就会进行记录。这包括我们那出问题的网络服务客户端和服务端,因为它们两者都属于系统的一部分。在某些时候,我们注意到,服务端的响应还没有发出的时候,另外一个来自同样客户端的请求又发了过来。这个真是出乎意料的,因为客户端代码是单线程的,也没有其他的客户端掺和进来。审查代码、测试之后,结论是我们的客户端不可能在第一个请求还没结束的时候再同时发出另外一个。

rivate void enableHttpResponseCache() { try { long  = 10 * 1024 * 1024; // 10 MiB File  = new File(getCacheDir(), "http"); Class.forName("android.net.") .getMethod("install", File.class, long.class) .invoke(null, , ); } catch (Exception ) { }}

Retrofit、RestTemplate、Feign这一类HTTP
client是在RESTful标准的微服务中常见的,这一类将client的易用性做到了更好,并且更方便编写restful
api的调用。一般还会提供消息转换、参数映射、提供注解等方式,在使用上写少量的代码即可完成功能,更像是一个RPC调用的client编写方式。

经过一整天的调试和研究日志发现,事实上,在服务端处理还未结束的时候客户端其实已经断开连接了。所以,这些请求终究并不是同时发生的,但是为什么我们花了一整天的时间才发现呢?这跟我们玩了一整天的星球大战有啥区别?

当然,这里还需要服务器端设置HTTP缓存相关的头信息。哪家强在2.3之前的版本,Apache的HTTP请求响应实现比较稳定,bug也少,所以在那些版本上它的最好。但是在2.3之后,毫无疑问,HttpURLConnection是最好的。它API精简实用,默认支持压缩,响应缓存等。最重要的这是Android团队重点投入的,而Apache的版本已经被抛弃了。所以还是使用HttpURLConnection吧。原文信息:Android’s
HTTP Clients译文转自:技术小黑屋

Feign使得Java
HTTP客户端编写更方便。Feign灵感来源于Retrofit、JAXRS-2.0和WebSocket。Feign最初是为了降低统一绑定Denominator
到HTTP API的复杂度,不区分是否支持 RESTful。

好吧,其实也不是。我们发现了罪魁祸首,服务端的容器软件HTTP的读超时设置被调得太低了。服务端的日志显示的确生成了响应,但是客户端却在此之前已经断开了,因为服务器端发生了读超时。这些在服务器端当然没有日志记录,因为这种行为是更低一层协议决定的(HTTP栈),而不是服务端的应用代码。

Feign还以子项目的方式提供了多种Client实现,比如(feign-httpclient、feign-okhttp、feign-ribbon),它们集成了当前比较流行的Http
Client组件,如Apache
HttpClient、Okhttp、Ribbon等,且其默认的Client实现为HttpURLConnection。

是的,没错,我听明白了,但是客户端的日志该怎么解释?客户端是不是应该抛出一个“ReadTimeoutException”异常,或者类似的玩意,然后可以写到日志里?然而,没错,事实上,并没有。就像现在发现的一样,真正的意外来自HttpURLConnection类的内部(更确切地说,是默认的Oracle的官方实现sun.net.www.protocol.http.HttpURLConnection)。

Feign还提供了请求和响应数据格式的编码解码器,用于解析json报文的有feign-gson、feign-jackson等,以及用于解析xml报文的有feign-sax、feign-jaxb等。

你以前是否知道HttpURLConnection的默认实现有个在某些情形下自动重试的特性?好吧,我之前就不知道。当时的情况是,客户端的确触发了超时异常,但是却被HttpURLConnection给捕捉了,而它自己决定重新尝试一次。这就意味着,你调用了HttpURLConnection的read()方法,它阻塞了,你正在等待,看起来就好像是在等待第一次请求的响应一样。但是在HttpURLConnection内部,它作了不止一次尝试,因此创建了不止一个socket连接。这就解释了为什么第二次及以后的请求永远在日志里找不到,因为这些第二次之后的请求是HttpURLConnection内部发起的。

RestTemplate是spring
web框架中提供的restful接口调用工具,它也是针对多个基础的http
client组件做了集成,如Apache
HttpClient、Okhttp等,且其默认的Client实现为HttpURLConnection。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图