RestTemplate的三種使用方式
SpringCloud中服務之間的兩種調用RESTful接口通信的方式:
- RestTemplate
- Feign
RestTemplate是一個Http客戶端,類似於HTTPClient,org但比HTTPClient更簡單。我們通過RestTemplate來簡單演示一下服務之間的調用,我們使用兩個服務來做演示。一個商品服務,一個訂單服務。首先創建一個商品服務工程:
選擇相應的依賴:
項目創建完成后,編輯配置文件,需要配置服務的名稱以及服務注冊中心的地址:
-
spring:
-
application:
-
name: product
-
-
eureka:
-
client:
-
service-url:
-
defaultZone: http://localhost:8761/eureka/
-
instance:
-
prefer-ip-address: true
注:如果對eureka還不太清楚的話,可以參考我的另一篇關於eureka的文章:Spring Cloud Eureka-服務注冊與發現
不要忘了在啟動類中,加上@EnableEurekaClient
注解:
-
package org.zero.example.product;
-
-
import org.springframework.boot.SpringApplication;
-
import org.springframework.boot.autoconfigure.SpringBootApplication;
-
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
-
-
-
-
public class ProductApplication {
-
-
public static void main(String[] args) {
-
SpringApplication.run(ProductApplication.class, args);
-
}
-
}
接着創建一個controller類,用於模擬商品列表接口,提供給訂單服務調用。代碼如下:
-
package org.zero.example.product.controller;
-
-
import org.springframework.web.bind.annotation.GetMapping;
-
import org.springframework.web.bind.annotation.RequestMapping;
-
import org.springframework.web.bind.annotation.RestController;
-
-
import java.util.ArrayList;
-
import java.util.List;
-
-
/**
-
* @program: product
-
* @description: product demo
-
* @author: 01
-
* @create: 2018-09-06 22:09
-
**/
-
-
-
public class ProductController {
-
-
/**
-
* 模擬商品列表接口
-
*
-
* @return product list
-
*/
-
-
public List<String> list() {
-
List<String> productList = new ArrayList<>();
-
productList.add( "肥皂");
-
productList.add( "可樂");
-
-
return productList;
-
}
-
}
然后啟動項目,啟動完成后,此時,在eureka的信息面板上應該可以看到product注冊上去了,如下:
商品服務准備好后,使用同樣的步驟創建order項目,這里就不再贅述了。配置文件中除了服務名稱需為order,其他的配置項和product一樣。因為8080已經被product服務占用了,所以還需要手動設置一下項目的端口號:
新建一個ClientController類,我們來看看RestTemplate的第一種使用方式,代碼如下:
-
package org.zero.example.order.controller;
-
-
import org.springframework.web.bind.annotation.GetMapping;
-
import org.springframework.web.bind.annotation.RequestMapping;
-
import org.springframework.web.bind.annotation.RestController;
-
import org.springframework.web.client.RestTemplate;
-
-
import java.util.List;
-
-
/**
-
* @program: order
-
* @description: order demo
-
* @author: 01
-
* @create: 2018-09-06 22:24
-
**/
-
-
-
public class OrderController {
-
-
-
public List info() {
-
// 1.第一種方式,直接使用。缺點:需要指定url地址,不靈活,也無法適應多個地址
-
RestTemplate restTemplate = new RestTemplate();
-
return restTemplate.getForObject("http://localhost:8080/product/list", List.class);
-
}
-
}
寫完后啟動項目,可以看到order服務也注冊到eureka上了:
接口測試結果如下,可以看到成功調用了商品服務的接口:
然后是RestTemplate的第二種使用方式,代碼如下:
-
...
-
-
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
-
-
-
-
public class OrderController {
-
-
-
private LoadBalancerClient loadBalancerClient;
-
-
-
public List info() {
-
// 2.第二種方式,借助LoadBalancerClient獲取服務實例,缺點:需要拼接url依舊不靈活
-
RestTemplate restTemplate = new RestTemplate();
-
// 參數傳的是服務注冊的id
-
ServiceInstance serviceInstance = loadBalancerClient.choose( "PRODUCT");
-
String url = String.format( "http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort() + "/product/list");
-
-
return restTemplate.getForObject(url, List.class);
-
}
-
}
接着是RestTemplate的第三種使用方式,這種方式下我們需要先創建一個配置類,代碼如下:
-
package org.zero.example.order.config;
-
-
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
-
import org.springframework.context.annotation.Bean;
-
import org.springframework.context.annotation.Configuration;
-
import org.springframework.web.client.RestTemplate;
-
-
/**
-
* @program: order
-
* @description: RestTemplate Config
-
* @author: 01
-
* @create: 2018-09-06 22:47
-
**/
-
-
public class RestTemplateConfig {
-
-
-
-
public RestTemplate restTemplate(){
-
return new RestTemplate();
-
}
-
}
然后在controller中注入使用,這種方式雖然最簡潔,其實本質上還是第二種方式:
-
...
-
-
-
public class OrderController {
-
-
-
private RestTemplate restTemplate;
-
-
-
public List info() {
-
// 3.第三種方式,利用@LoadBalanced注解,可在restTemplate里使用應用名稱進行調用
-
return restTemplate.getForObject("http://PRODUCT/product/list", List.class);
-
}
-
}
負載均衡器:Ribbon
eureka是客戶端發現機制的,所以使用的是客戶端負載均衡器,所謂客戶端負載均衡,也就是說負載的策略在客戶端完成,俗稱軟負載。如果我們的商品服務部署在多個節點上的話,當使用Feign進行服務調用的時候,默認會使用Ribbon來做負載均衡。當然使用RestTemplate的時候也是可以結合Ribbon做負載均衡的,例如上一小節中演示的第二、三種使用RestTemplate的方式就是結合了Ribbon。
Ribbon是Netflix發布的負載均衡器,是一種客戶端負載均衡器,運行在客戶端上,它有助於控制HTTP和TCP的客戶端的行為。為Ribbon配置服務提供者地址后,Ribbon就可基於某種負載均衡算法,自動地幫助服務消費者去請求。Ribbon默認為我們提供了很多負載均衡算法,例如輪詢、隨機等。當然,我們也可為Ribbon實現自定義的負載均衡算法。
我們在配置文件中可以自定義負載均衡策略,如下:
-
PRODUCT: # 服務的名稱
-
ribbon: # 負載均衡器
-
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 規則完整的類名,這里使用的是隨機
注:如非必須,一般使用默認的輪詢策略即可
Ribbon特性:
- 服務發現
- 服務選擇規則
- 服務監聽
- ServerList,獲取可用服務列表
- IRule,選擇最終調用,即負載策略
- ServerListFilter,過濾不可用地址
在Spring Cloud中,當Ribbon與Eureka配合使用時,Ribbon可自動從Eureka Server獲取服務提供者地址列表,並基於負載均衡算法,請求其中一個服務提供者實例。下圖展示了Ribbon與Eureka配合使用時的架構:
Feign的使用
Feign是從Netflix中分離出來的輕量級項目,是一個聲明式的REST客戶端,它的出現使得我們在服務中編寫REST客戶端變得更加容易。利用 Feign 可以創建一個接口並對它進行注解,該接口就會具有可插拔的注解支持包括Feign注解與JAX-RS注解,Feign還支持可插拔的編碼器與×××。Feign 靈感來源於Retrofit、JAXRS-2.0和WebSocket,Feign 最初是為了降低統一綁定 Denominator 到 HTTP API 的復雜度,不區分是否支持 Restful。
Spring Cloud 增加了對 Spring MVC的注解,Spring Web 默認使用了HttpMessageConverters, Spring Cloud 集成 Ribbon 和 Eureka 提供的負載均衡的HTTP客戶端 Feign。
Feign特性:
- 聲明式REST客戶端(偽RPC)
- 采用了基於接口的注解
- 同樣使用ribbon做負載均衡器
接下來我們嘗試一下使用Feign編寫REST客戶端,實現訂單服務調用商品服務接口,看看Feign到底有多方便。在商品和訂單服務的項目中,都加入Feign的依賴,pom.xml文件配置的依賴如下:
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-starter-openfeign</artifactId>
-
</dependency>
注:若出現使用阿里雲的倉庫地址不能下載到該依賴的話,可以嘗試使用maven中央倉庫的地址進行下載
首先到商品服務工程中,新建一個client包。本來應該是新建一個Client模塊的,但是為了方便演示,我就直接用包了。在client包下新建一個 ProductClinet 接口,編寫代碼如下:
-
package org.zero.example.product.client;
-
-
import org.springframework.cloud.openfeign.FeignClient;
-
import org.springframework.web.bind.annotation.GetMapping;
-
-
import java.util.List;
-
-
-
// 此注解用於聲明一個Feign客戶端,name屬性指定服務的名稱
-
-
public interface ProductClinet {
-
-
/**
-
* 商品列表接口,注意這里的uri要寫全
-
*
-
* @return Product List
-
*/
-
-
List<String> list();
-
}
我們在使用RestTemplate的時候,都是在訂單服務上編寫接口調用相關代碼的,但是為什么使用Feign就在商品服務上去寫這個代碼呢?這是因為使用Feign的時候,只需要通過注解就能在接口上聲明客戶端,當我們在訂單服務里面使用的時候,注入這個ProductClinet接口調用相應的方法即可實現商品服務接口的調用。而這些接口屬於商品服務對外暴露的接口,由於職責的關系,所以都應該由商品服務去維護,不應該寫在訂單服務里。
編寫好ProductClinet接口的代碼后,使用如下命令將這個項目安裝到本地的maven倉庫中:
mvn clean -Dmaven.test.skip=true install
接着到訂單服務的工程中,加入商品服務的依賴,如下:
-
<dependency>
-
<groupId>org.zero.example</groupId>
-
<artifactId>product</artifactId>
-
<version>0.0.1-SNAPSHOT</version>
-
</dependency>
然后在啟動類中,加上@EnableFeignClients
注解表示開啟 Feign 客戶端以及配置client包的路徑,如下:
-
package org.zero.example.order;
-
-
import org.springframework.boot.SpringApplication;
-
import org.springframework.boot.autoconfigure.SpringBootApplication;
-
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
-
import org.springframework.cloud.openfeign.EnableFeignClients;
-
-
-
-
-
public class OrderApplication {
-
-
public static void main(String[] args) {
-
SpringApplication.run(OrderApplication.class, args);
-
}
-
}
修改 OrderController 代碼如下:
-
...
-
-
import org.zero.example.product.client.ProductClinet;
-
-
-
-
public class OrderController {
-
-
-
private ProductClinet productClinet;
-
-
-
public List info() {
-
return productClinet.list();
-
}
-