1.什么是路由網關
截至目前為止的例子中,我們創建了一個service,叫做:HelloService,然后我們把它部署到了兩台服務器(即提供了兩個provider),然后我們又使用ribbon將其做了負載均衡。目前為止這一切都看上運作的很好,我們通過地址訪問地址http://localhost:9291/hello,實際是路由到了http://localhost:9191/hello和http://localhost:9192/hello兩個服務器上。
緊接着,隨着業務更進一步,我們又創建了UserService,又創建了ProductService,我們提供的服務器也越來越多,但是我們發現一個問題,即:即提供一種服務,前端程序員都需要通過IP+端口的形式去訪問,很快URL地址就多的爆炸了,而且,甚至某些別有用心的同學因為知道了這些目標地址,開始采用非常規的手段去做些壞事。所以,我們必須做些手段來規避這些糟糕的事情。
路由網關出現了。
當我們輸入URL,比如zuikc.com/hello或者zuikc.com/user的時候,路由網關會去分析這個地址,並且根據地址的pattern,
1:去決定到底是訪問helloservice還是userservice;
2:到eureka注冊中心拿到該服務的id;
3:通過ribbon去訪問該服務id中的一台provider;
4:拿到response,返回給調用者;
並且,由於路由網關能做這些事情,還有額外的一些事情,比如權限驗證(shiro,springsecurity等),就天然的適合放到路由網關也一並實現了。
關於路由網關,以前有zuul,但是zuul已經停止更新了,Spring Cloud Gateway被Spring Cloud官方推出來,作為第二代網關框架,取代Zuul網關。
總結一下,路由網關的作用就是:路由轉發、權限校驗、限流控制。
2.路由網關原理
來看Spring Cloud Gateway官方提供的架構圖,
客戶端向Spring Cloud Gateway發出請求。 Gateway Handler Mapping匹配路徑並將其發送到Gateway web handler處理。 Gateway web handler處理請求,將其發送給過濾器鏈。
過濾器鏈主要分兩大類:pre和post。“pre”過濾器一般進行權限、限流、日志輸出等功能,以及請求頭的更改、協議的轉換;“post”過濾器是在收到響應后,可以對響應數據做統一修改,比如響應頭、協議的轉換等。
3.實現
現在,讓我們用代碼實現一下吧。
首先,創建子模塊,在我們的例子中,創建完畢后,解決方案像如下這樣,
現在,導入依賴如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>springcloud.parent</artifactId>
<groupId>com.zuikc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<name>gateway</name>
<artifactId>gateway</artifactId>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
</project>
先創建一個最簡單的application.yml,
server:
port: 8880
然后,讓我們創建application類,
package com.zuikc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName GatewayApplication
* @Description 我們提供咨詢和培訓服務,關於本文有任何困惑,請關注並聯系“碼農星球”
* @Author 碼農星球
**/
@SpringBootApplication
@RestController
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route("host_route", r -> r.path("/hello/**").filters(f -> f.stripPrefix(1)).uri("http://localhost:9291"))
.route("host_route", r -> r.path("/user/**").filters(f -> f.stripPrefix(1)).uri("http://localhost:9391"))
.build();
}
}
在這個類中,我們將“域名/hello”下的所有請求轉發到了HelloService所在ribbon服務器中,將“域名/user”下所有的請求轉到User所在的ribbon下。
然后,啟動application。這個時候,讓我們輸入地址:http://localhost:8880/hello/hello,可以看到結果類似如下:
服務將在兩個provider中切換。注意,上述url中,第一個hello,是指路由到helloservice中,第二個hello,是具體的服務。
接下來,讓我們試一下,http://localhost:8880/user/something。由於我們目前並沒有開發UserService,所以就出現errorpage了~~
4.使用配置實現
我們也可以使用配置來實現路由。
在上面的代碼中,我們首先去掉application中的bean,
package com.zuikc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName GatewayApplication
* @Description 我們提供咨詢和培訓服務,關於本文有任何困惑,請關注並聯系“碼農星球”
* @Author 碼農星球
**/
@SpringBootApplication
@RestController
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
然后,修改application.yml,
server:
port: 8880
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://localhost:9291
predicates:
- Path=/hello/**
filters:
- StripPrefix=1
- id: host_route
uri: http://localhost:9391
predicates:
- Path=/user/**
filters:
- StripPrefix=1
然后,重啟application,得到的效果是一樣一樣滴。
5.通過filters使用Hystrix
如果注意上文中的http://localhost:8880/user/something,我們發現原來在gateway中也是可以指定熔斷器fallback的。
那就好辦了,首先,讓我們引入hystrix,
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
其次,創建一個fallback,
package com.zuikc;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName FallBack
* @Description 我們提供咨詢和培訓服務,關於本文有任何困惑,請關注並聯系“碼農星球”
* @Author 碼農星球
**/
@RestController
@RequestMapping("/fallback")
public class FallBack {
@RequestMapping("")
public String fallback(){
return "error";
}
}
再次,修改我們的配置文件,
server:
port: 8880
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://localhost:9291
predicates:
- Path=/hello/**
filters:
- StripPrefix=1
- id: host_route
uri: http://localhost:9391
predicates:
- Path=/user/**
filters:
- StripPrefix=1
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
重啟application。
這個時候,再次訪問http://localhost:8880/user/something,頁面輸出為error。
感謝關注“碼農星球”。本文版權屬於“碼農星球”。我們提供咨詢和培訓服務,關於本文有任何困惑,請關注並聯系我們。
本文參考:https://spring.io/guides/gs/gateway/
一些官方的例子在: