天天看點

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

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

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

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

GitHub代碼

1|01、rest服務建構

1、建立一個父工程

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

删除src目錄

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

pom檔案增加如下内容:

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

2、建立一個module

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

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可以看到:

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

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)現在的工程如下:

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

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

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

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

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:使用的負載均衡政策,預設提供了幾種,也可以自己實作(後續講解),預設提供的如下:

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

疑問:

如果上面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抽象類,然後配置到下面即可

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

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、修改配置

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

3、重新開機gateway,然後通路http://localhost:8080/gateway/hello不斷點選,發現點選三次就會調整到下一個服務。

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