(2)POST(创建):在服务器上创建一个新资源。

(3)PUT(编辑):更新服务器上的资源,提供整个资源。

(4)PATCH(编辑):更新服务器上的资源,仅提供已更改的属性。

(5)DELETE(删除):从服务器中删除资源。


下面两个不是很常用。

(1)HEAD(查看):检索有关资源的元数据,如数据的哈希值或上次更新的时间。

(2)OPTIONS(查看):检索有关允许消费者使用资源的信息。客户端和服务端的交互是无状态的,GET请求通常是可以被缓存的,资源使用复数,URL中可以有表述版本的信息,举例如下。

微服务一个服务调用另一个服务 微服务调用微服务_HTTP

按照顺序对应的含义如下。

(1)使用2.0版本的接口,创建一个产品。

(2)使用2.0版本的接口,根据ID查看产品信息。

(3)使用1.0版本的接口,根据ID全量更新产品信息。

(4)使用1.0版本的接口,根据ID部分更新产品信息。

(5)使用1.0版本的接口,根据ID删除产品。

RESTful的接口还有很多设计原则,这里不再赘述,感兴趣的读者可以查阅相关资料进行学习,在微服务中使用的HTTP通信协议就是采用的RESTful的接口设计,所以熟练掌握REST API的设计用法在微服务架构中是十分重要的。

HTTP通信方法

========

一般在项目中采用什么技术来完成HTTP通信?在一些早期的项目中,可以看到Apache HttpComponents的身影,它的功能也十分强大,但是在使用时,需要编写大量的基础代码,往往还需要进行二次封装,而在如今Spring Boot盛行的时代,大家更热衷于现取现用,正所谓约定大于配置,所有的基础工作都按照一定的约定交由框架来完成。下面以Spring为例,主要介绍RestTemplate和WebClient两种方式进行HTTP的通信。

1. RestTemplateRestTemplate是Spring Web中提供的用于在客户端完成同步的HTTP请求的核心类,大大简化了与HTTP服务器的通信,并实现了RESTful的设计原则。RestTemplate默认采用JDK原生的HTTP连接工具实现,当然也可以切换库,如Apache HttpComponents、Netty和OKHttp等第三方的HTTP库。我们以默认的实现为例,首先需要声明一个RestTemplate,这里采用Spring Boot的注解式声明方式,代码如下。

微服务一个服务调用另一个服务 微服务调用微服务_微服务一个服务调用另一个服务_02

当然,还可以给它初始化一些公共属性,如URL、用户名密码,或者一些连接超时等设置,代码如下。

微服务一个服务调用另一个服务 微服务调用微服务_http_03

完成 RestTemplate 的声明之后就可以使用它了 , 之前说过RestTemplate实现了RESTful的设计原则,所以RestTemplate提供了便捷的方法去实现HTTP的GET、POST、PUT、PATCH和DELETE方法。例如,getForEntity()就是以GET的方式发送HTTP请求,而postForEntity()则 是 以 POST 的 方 式 发 送 HTTP 请 求 。 当 然 , 还 有 其 他 实 现 , 如patchForObject()、put()、delete()和optionsForAllow()等方法,这里就不一一介绍了。下面以GET和POST两种最常见的方式来介绍RestTemplate的用法,其他的大同小异。

(1)RestTemplate的GET方法。

RestTemplate提供了getForObject和getForEntity两种方式发送GET的HTTP请求,其中getForObject方法可以直接将响应的Body转换为指定的类型,方法定义如下。

微服务一个服务调用另一个服务 微服务调用微服务_架构_04

其中,第一个方法比较常用,按照顺序传递URL的参数,通过在URL中定义{}来表示参数的站位,{}中可以写具体含义的单词,也可以写数字,如{0},代码如下。

微服务一个服务调用另一个服务 微服务调用微服务_微服务一个服务调用另一个服务_05

此外,getForObject还提供了另外两种重载方法,分别提供了通过Map传递参数和没有参数两种功能。没有参数很好理解,不再赘述,下面的代码展示了通过Map传递参数的方式。

微服务一个服务调用另一个服务 微服务调用微服务_微服务一个服务调用另一个服务_06

不难看出,Map中的key对应着URL中{}里的单词,使用Map的不足之处是当参数太多时,顺序容易弄错,而且方法会写得很长,不易读,也不易维护。getForObject方法能满足我们大部分的需求,但有时可能需要获取除Body之外的信息,如响应头、响应状态码等,这时就需要getForEntity了,getForEntity和getForObject一样,提供了3种实现,方法定义如下。

微服务一个服务调用另一个服务 微服务调用微服务_微服务一个服务调用另一个服务_07

可以发现,getForEntity的方法参数和getForObject的一样,唯一的区别是getForEntity的返回类型是ResponseEntity。这里不再对每个方法进行详细介绍了,下面还是以第一个方法为例,代码如下。

微服务一个服务调用另一个服务 微服务调用微服务_微服务一个服务调用另一个服务_08

(2)RestTemplate的POST方法。

与GET的方式一样,POST也提供了postForObject和postForEntity两种方式来完成POST的HTTP请求。方法定义如下。

微服务一个服务调用另一个服务 微服务调用微服务_http_09

POST的6个方法定义与GET几乎一样,所以这里没有把方法说明粘贴进来,仔细观察可以发现,POST的方法多了一个request的参数,这个参数会被放进请求的Body中,当没有需要时也可以传入null,举例如下:

微服务一个服务调用另一个服务 微服务调用微服务_微服务_10

关于RestTemplate的其他方法就不再列举了,感兴趣的读者可以查看Spring Web库的源码,或者访问Spring的官网查阅相关教程。

2. WebClient

WebClient相比RestTemplate是一个较新的HTTP访问方式,之前提到过,RestTemplate是一个同步的请求方式,当请求发出后,当前线程会等待,直到有响应后才会继续执行后续代码。其实远程调用是一个可以异步的过程,在等待请求响应时,我们完全可以做其他的事情,所以RestTemplate在一些性能要求比较高的地方使用就显得不是那么合适了。

微服务一个服务调用另一个服务 微服务调用微服务_微服务_11

这时就需要使用可以异步完成请求的WebClient了,当然,我们可以仍然使用RestTemplate,然后通过线程池或CompletableFuture等方式创建新的线程来执行RestTemplate的请求而不阻塞当前线程的执行。不过这样做不是特别优雅,而且每次还需要自行维护关于创建不同线程的代码。

随着JavaScript的Reactive设计理念越来越流行,不少语言和框架开始相继模仿。Spring也采用了“No blocking”(无阻塞)的方式,推出了Spring WebFlux,关于WebFlux的功能有很多,这里主要来看一下WebFlux中WebClient的用法。

相比RestTemplate,WebClient最大的优势就是可以使用Reactive的方式执行非阻塞的HTTP请求,即异步的请求服务端。WebClient同样实现了RESTful的设计原则,支持GET、POST、PUT、PATCH和DELETE等操作,而且写法更接近于流式,示例代码如下。

微服务一个服务调用另一个服务 微服务调用微服务_HTTP_12

这是一个GET方法,代码很好理解,只不过是流式的写法,先定义HTTP的方法,然后定义URI,最后定义返回类型。再来看一个POST的例子,代码如下。

微服务一个服务调用另一个服务 微服务调用微服务_架构_13

很显然,相比GET方法多一个syncBody 方法,类似于RestTemplate的request参数,这个方法会把该参数当作请求的Body发送到服务端。仔细观察可以发现,与RestTemplate相比有一个最大的不同,就是方法的返回值变成了Mono类型,其实除了Mono类型,WebFlux还提供了Flux类型,代码如下。