SpringCloud中服務之間的兩種調用RESTful接口通訊的方式:java
RestTemplate是一個Http客戶端,相似於HTTPClient,org但比HTTPClient更簡單。咱們經過RestTemplate來簡單演示一下服務之間的調用,咱們使用兩個服務來作演示。一個商品服務,一個訂單服務。首先建立一個商品服務工程:web
選擇相應的依賴:算法
項目建立完成後,編輯配置文件,須要配置服務的名稱以及服務註冊中心的地址:spring
spring: application: name: product eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ instance: prefer-ip-address: true
注:若是對eureka還不太清楚的話,能夠參考個人另外一篇關於eureka的文章:Spring Cloud Eureka-服務註冊與發現bash
不要忘了在啓動類中,加上@EnableEurekaClient
註解:架構
package org.zero.example.product; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class ProductApplication { public static void main(String[] args) { SpringApplication.run(ProductApplication.class, args); } }
接着建立一個controller類,用於模擬商品列表接口,提供給訂單服務調用。代碼以下:app
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 **/ @RestController @RequestMapping("/product") public class ProductController { /** * 模擬商品列表接口 * * @return product list */ @GetMapping("/list") public List<String> list() { List<String> productList = new ArrayList<>(); productList.add("肥皂"); productList.add("可樂"); return productList; } }
而後啓動項目,啓動完成後,此時,在eureka的信息面板上應該能夠看到product註冊上去了,以下:負載均衡
商品服務準備好後,使用一樣的步驟建立order項目,這裏就再也不贅述了。配置文件中除了服務名稱需爲order,其餘的配置項和product同樣。由於8080已經被product服務佔用了,因此還須要手動設置一下項目的端口號:dom
新建一個ClientController類,咱們來看看RestTemplate的第一種使用方式,代碼以下:maven
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 **/ @RestController @RequestMapping("/order") public class OrderController { @GetMapping("/info") 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; @RestController @RequestMapping("/order") public class OrderController { @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/info") 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 **/ @Configuration public class RestTemplateConfig { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
而後在controller中注入使用,這種方式雖然最簡潔,其實本質上仍是第二種方式:
... @RestController @RequestMapping("/order") public class OrderController { @Autowired private RestTemplate restTemplate; @GetMapping("/info") public List info() { // 3.第三種方式,利用@LoadBalanced註解,可在restTemplate裏使用應用名稱進行調用 return restTemplate.getForObject("http://PRODUCT/product/list", List.class); } }
eureka是客戶端發現機制的,因此使用的是客戶端負載均衡器,所謂客戶端負載均衡,也就是說負載的策略在客戶端完成,俗稱軟負載。若是咱們的商品服務部署在多個節點上的話,當使用Feign進行服務調用的時候,默認會使用Ribbon來作負載均衡。固然使用RestTemplate的時候也是能夠結合Ribbon作負載均衡的,例如上一小節中演示的第2、三種使用RestTemplate的方式就是結合了Ribbon。
Ribbon是Netflix發佈的負載均衡器,是一種客戶端負載均衡器,運行在客戶端上,它有助於控制HTTP和TCP的客戶端的行爲。爲Ribbon配置服務提供者地址後,Ribbon就可基於某種負載均衡算法,自動地幫助服務消費者去請求。Ribbon默認爲咱們提供了不少負載均衡算法,例如輪詢、隨機等。固然,咱們也可爲Ribbon實現自定義的負載均衡算法。
咱們在配置文件中能夠自定義負載均衡策略,以下:
PRODUCT: # 服務的名稱 ribbon: # 負載均衡器 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 規則完整的類名,這裏使用的是隨機
注:如非必須,通常使用默認的輪詢策略便可
Ribbon特性:
在Spring Cloud中,當Ribbon與Eureka配合使用時,Ribbon可自動從Eureka Server獲取服務提供者地址列表,並基於負載均衡算法,請求其中一個服務提供者實例。下圖展現了Ribbon與Eureka配合使用時的架構:
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特性:
接下來咱們嘗試一下使用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; @Component // 此註解用於聲明一個Feign客戶端,name屬性指定服務的名稱 @FeignClient(name = "PRODUCT") public interface ProductClinet { /** * 商品列表接口,注意這裏的uri要寫全 * * @return Product List */ @GetMapping("/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; @EnableEurekaClient @SpringBootApplication @EnableFeignClients(basePackages = "org.zero.example.product.client") public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
修改 OrderController 代碼以下:
... import org.zero.example.product.client.ProductClinet; @RestController @RequestMapping("/order") public class OrderController { @Autowired private ProductClinet productClinet; @GetMapping("/info") public List info() { return productClinet.list(); } }
重啓這兩個項目,測試接口以下: