API網關spring cloud gateway和負載均衡框架ribbon實戰


    通常我們如果有一個服務,會部署到多台服務器上,這些微服務如果都暴露給客戶,是非常難以管理的,我們系統需要有一個唯一的出口,API網關是一個服務,是系統的唯一出口。API網關封裝了系統內部的微服務,為客戶端提供一個定制的API。客戶端只需要調用網關接口,就可以調用到實際的微服務,實際的服務對客戶不可見,並且容易擴展服務。

    API網關可以結合ribbon完成負載均衡的功能,可以自動檢查微服務的狀況,及時剔除或者加入某個微服務到可用服務列表。此外網關可以完成權限檢查、限流、統計等功能。下面我們將一一完成上面的功能。注意微服務只是提供rest的接口,不會有額外的組件依賴,不需要eureka等。只需要兩個工程,一個是微服務,我們可以部署到多台服務器,那么只是訪問的ip不同,在演示的時候,我們在本機演示,修改端口,達到啟動多個微服務的目的,另一個就是網關,主要是spring cloud gateway 和 ribbon兩大組件來實現網關和負載均衡等功能。

image

GitHub代碼

1|01、rest服務構建



1、創建一個父工程

imageimage

imageimage

刪除src目錄

image

pom文件增加如下內容:

 
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> </parent>
 

2、新建一個module

imageimage

imageimage

3、修改pom.xml文件,增加如下內容:

 
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
 

4、在resources文件夾下面增加一個application.yml文件,內容為:

server: port: 1001

5、新增一個主入口類Provider1001Application,內容如下

 
package com.yefengyu.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Provider1001Application { public static void main(String[] args) { SpringApplication.run(Provider1001Application.class, args); } }
 

6、新增一個rest接口HelloWorldController,內容如下:

 
package com.yefengyu.cloud.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloWorldController { @GetMapping("/hello") @ResponseBody public String hello() { return "hello spring cloud, 1001"; } }
 

7、啟動main方法,在瀏覽器輸入http://localhost:1001/hello可以看到:

image

8、同理,我們再建立2個rest服務,步驟如下

(1)新增module,參考第二小節,主要把名稱改為:

  • provider1002
  • provider1003

(2)pom.xml文件參考第三小節,加上即可。

(3)參考第四小節,新建application.yml文件,注意端口改為

  • 1002
  • 1003

(4)參考第五小節,新建一個主啟動類,名稱為如下,內容都一樣。

  • Provider1002Application
  • Provider1003Application

(5)參考第六小節,新增一個HelloWorldController接口,其中只有下面這句中的 1001 改為 1002 或 1003,方便測試觀察結果

return "hello spring cloud, 1001";

(6)參考第7小節測試成功新增的兩個服務即可。

(7)現在的工程如下:

image

2|02、spring cloud gateway



Spring Cloud Gateway是Spring官方基於Spring 5.0,Spring Boot 2.0和Project Reactor等技術開發的網關,Spring Cloud Gateway旨在為微服務架構提供一種簡單而有效的統一的API路由管理方式。

  • 路由:Gateway的基礎構建模塊。它包括一個ID,一個目標URL,一個斷言集合和一個過濾器集合。如果斷言判斷為真,則路由匹配。
  • 斷言:這是Java8的新增功能,輸入的類型為Spring框架的ServerWebExchange。它可以匹配HTTP請求中的任何東西,比如:請求頭或者參數。
  • 過濾器:是Spring框架的GatewayFilter,請求和響應都可以被Filter修改。

1、新建一個module,按照上面的方式,名稱叫: gateway

2、添加依賴

 
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> <version>2.1.0.RELEASE</version> </dependency> </dependencies>
 

3、新建一個主啟動類 GatewayApplication

 
package com.yefengyu.gateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }
 

4、新增一個application.yml文件,內容如下:

 
server: port: 8080 spring: application: name: gateway_server cloud: gateway: default-filters: routes: - id: my_route uri: http://localhost:1001/ predicates: - Path=/gateway/** filters: - StripPrefix=1
 

5、測試

訪問 http://localhost:8080/gateway/hello

image

上面只是簡單的路由轉發,可以先了解下工作原理:

url 中的 http://localhost:8080/ 會訪問到gateway這個服務,spring cloud gateway會在配置的路由中做謂詞匹配,也就是url中的gateway匹配到了id為my_route的路由,就會把http://localhost:8080/替換為http://localhost:1001/,並且filters中的規則(StripPrefix)會把http://localhost:8080/gateway/hello中的gateway去掉,那么http://localhost:8080/gateway/hello實際就會去訪問http://localhost:1001/hello,也就是訪問到了provider1001服務。

疑問?

上面的uri只配置了provider1001服務,如何使用geteway訪問三個服務呢?需要使用負載均衡ribbon

3|03、ribbon負載均衡



下面的操作都是在gateway這個服務操作的:

1、添加依賴

 
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> <version>2.1.0.RELEASE</version> </dependency>
 

2、修改配置文件,增加或者修改見紅色部分

 
server: port: 8080 spring: application: name: gateway_server cloud: gateway: default-filters: routes: - id: my_route uri: lb://my-load-balanced-service predicates: - Path=/gateway/** filters: - StripPrefix=1 my-load-balanced-service: ribbon: listOfServers: localhost:1001, localhost:1002,localhost:1003 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
 

3、重啟gateway服務,不斷訪問 http://localhost:8080/gateway/hello,發現可以不斷訪問的時候在三個服務中來回切換,達到了負載均衡的目的。上面我們使用了輪詢的負載均衡策略。

注意:

  • listOfServers:配置的微服務的服務器ip端口
  • NFLoadBalancerRuleClassName:使用的負載均衡策略,默認提供了幾種,也可以自己實現(后續講解),默認提供的如下:

image

疑問:

如果上面listOfServers中的任何一個服務關閉了,然后使用gateway訪問,會出現什么情況?

事實是這樣的:

比如provider1003服務宕機。那么使用輪詢算法的時候,不斷訪問gateway,會出現:

provider1001 正常,provider1002 正常,provider1003 異常provider1001 正常,provider1002 正常,provider1003 異常。。。

provider1003 已經宕機,但是請求還是不斷轉發到該服務上,導致服務訪問的過程中,部分請求異常。

 

我們需要有這樣的功能,如果某個服務宕機,那么請求就不會發送到該服務器上,如果宕機的服務器恢復了,那么請求又可以發送到該服務器上,要實現這個功能,需要ribbon對服務進行健康檢查。

(1)首先微服務需要有個rest接口,就叫做heath接口吧,調用heath接口返回ok表明服務正常。

(2)gateway需要有調用heath接口的功能,並且配置到ribbon可以不斷調用該接口,時刻檢查服務的狀態,如果有服務器掛掉,可以很快感知到,並把該服務剔除可用服務列表。

4|04、健康檢查



1、provider1001,provider1002,provider1003增加一個rest接口HealthController

 
package com.yefengyu.cloud.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class HealthController { @GetMapping("/heath") @ResponseBody public String heath() { return "ok"; } }
 

2、在gateway工程里面做如下修改

(1)新建一個Config類

 
package com.yefengyu.gateway.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class MainConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
 

(2)新建一個健康檢查的類,主要是調用heath接口。

 
package com.yefengyu.gateway.loadbanlance; import com.netflix.loadbalancer.IPing; import com.netflix.loadbalancer.Server; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @Component public class HealthExamination implements IPing { @Autowired private RestTemplate restTemplate; @Override public boolean isAlive(Server server) { String url = "http://" + server.getId() + "/heath"; try { ResponseEntity<String> heath = restTemplate.getForEntity(url, String.class); if (heath.getStatusCode() == HttpStatus.OK) { System.out.println("ping " + url + " success and response is " + heath.getBody()); return true; } System.out.println("ping " + url + " error and response is " + heath.getBody()); return false; } catch (Exception e) { System.out.println("ping " + url + " failed"); return false; } } }
 

上面代碼繼承IPing接口,判斷服務是否可用。我們在微服務中增加heath接口,在gateway中調用該接口,如果返回正常則認為微服務可用。

(3)修改配置文件,注意最后一行,這是唯一增加的。

 
server: port: 8080 spring: application: name: gateway_server cloud: gateway: default-filters: routes: - id: my_route uri: lb://my-load-balanced-service predicates: - Path=/gateway/** filters: - StripPrefix=1 my-load-balanced-service: ribbon: listOfServers: localhost:1001,localhost:1002,localhost:1003 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule NFLoadBalancerPingClassName: com.yefengyu.gateway.loadbanlance.HealthExamination
 

3、重新啟動微服務和gateway服務,然后通過網關訪問,可以看到可以正常訪問,如果此時把某一台微服務停掉,訪問的時候開始可能會報錯,但是隨着健康檢查的運行,檢測到該服務不可用,則會把該服務剔除,以后只會訪問正常運行的服務。當宕機的服務重啟,健康檢查還會把該服務加入到可用服務列表,下次請求就會再次發送到該服務上。

5|05、自定義負載均衡策略



上面演示了隨機、輪詢等負載均衡算法,我們可以自定義負載均衡算法。需求是:每個服務器訪問三次再跳轉到下一個服務器。

只需要在gateway項目中實現AbstractLoadBalancerRule抽象類,然后配置到下面即可

image

1、負載均衡算法(參考RandomRule)

 
package com.yefengyu.gateway.loadbanlance; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; import java.util.ArrayList; import java.util.List; public class MyRule extends AbstractLoadBalancerRule { private volatile int total; private volatile int index; List<Server> upList = new ArrayList<>(); public MyRule() { } public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { return null; } else { Server server = null; while (server == null) { if (Thread.interrupted()) { return null; } List<Server> allList = lb.getAllServers(); int serverCount = allList.size(); if (serverCount == 0) { return null; } if (total == 0) { upList = lb.getReachableServers(); } if (total < 3) { if (upList.size() != lb.getReachableServers().size()) { index = 0; } server = lb.getReachableServers().get(index); total++; } else { total = 0; index++; if (index >= lb.getReachableServers().size()) { index = 0; } } if (server == null) { Thread.yield(); } else { if (server.isAlive()) { return server; } server = null; Thread.yield(); } } return server; } } public Server choose(Object key) { return this.choose(this.getLoadBalancer(), key); } public void initWithNiwsConfig(IClientConfig clientConfig) { } }
 

2、修改配置

image

3、重啟gateway,然后訪問http://localhost:8080/gateway/hello不斷點擊,發現點擊三次就會調整到下一個服務。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM