一 基于Gateway的路由
1.1 Gateway基礎路由配置
- 導入依賴(注意SpringCloud Gateway使用的web框架為webflux熙侍,和SpringMVC不兼容林螃,需要將父模塊的web模塊去掉)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
- 配置啟動類
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class,args);
}
}
- 編寫配置文件
server:
port: 8080
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: product # 我們自定義的路由 ID,保持唯一
uri: http://localhost:9001 # 目標服務地址
predicates: # 路由條件,Predicate 接受一個輸入參數辫呻,返回一個布爾值結果
- Path=/product/**
- 測試訪問
http://localhost:8080/product/1
1.2 Gateway動態(tài)路由
自動從服務中心列表中獲取地址
- 添加eureka客戶端依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 動態(tài)路由的配置文件
server:
port: 8080
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: product
uri: lb://product # lb代表從注冊中心獲取服務
predicates:
- Path=/product/**
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
prefer-ip-address: true
1.3 重寫轉發(fā)路徑
- 通過RewritePath配置重寫轉發(fā)的url
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: product
uri: lb://product # lb代表從注冊中心獲取服務
predicates:
- Path=/product-service/**
filters:
- RewritePath=/product-service/(?<segment>.*), /$\{segment}
- 在網頁上請求http://localhost:8080/product-service/product/1拌汇,此時會將請求轉發(fā)到http://127.0.0.1:9001/product/1
二 基于Gateway的過濾器
2.1 過濾器類型
- GatewayFilter::應用到單個路由或者一個分組的路由上。
- GlobalFilter:應用到所有的路由上屎债。通過全局過濾器可以實現對權限的統(tǒng)一校驗仅政,安全性驗證等功能。
2.2 權限校驗
//自定義全局過濾器盆驹,需要實現GlobalFilter, Ordered兩個接口
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
//實現過濾的邏輯
//ServerWebExchange 當前請求和響應的上下文
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (!"yorick".equals(token)){
System.out.println("驗證失敗");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//攔截
return exchange.getResponse().setComplete();
}
//放行
return chain.filter(exchange);
}
//指定過濾器的優(yōu)先級圆丹,越小級別越高
@Override
public int getOrder() {
return 0;
}
}
2.3 網關限流
- 常見的限流方法
- 計數器
計數器限流算法是最簡單的一種限流實現方式。其本質是通過維護一個單位時間內的計數器躯喇,每次請求計數器加1辫封,當單位時間內計數器累加到大于設定的閾值,則之后的請求都被拒絕廉丽,直到單位時間已經過去倦微,再將計數器重置為零。
- 漏桶算法
漏桶算法可以很好地限制容量池的大小正压,從而防止流量暴增欣福。漏桶可以看作是一個帶有常量服務時間的單服務器隊列,如果漏桶(包緩存)溢出,那么數據包會被丟棄。 在網絡中仿滔,漏桶算法可以控制端口的流量輸出速率,平滑網絡上的突發(fā)流量郑临,實現流量整形,從而為網絡提供一個穩(wěn)定的流量屑宠。
為了更好的控制流量厢洞,漏桶算法需要通過兩個變量進行控制:一個是桶的大小,支持流量突發(fā)增多時可以存多少的水(burst)典奉,另一個是水桶漏洞的大刑煞(rate)。
- 令牌桶算法
令牌桶算法是對漏桶算法的一種改進秋柄,桶算法能夠限制請求調用的速率获枝,而令牌桶算法能夠在限制調用的平均速率的同時還允許一定程度的突發(fā)調用。在令牌桶算法中骇笔,存在一個桶省店,用來存放固定數量的令牌。算法中存在一種機制笨触,以一定的速率往桶中放令牌懦傍。每次請求調用需要先獲取令牌,只有拿到令牌芦劣,才有機會繼續(xù)執(zhí)行粗俱,否則選擇等待可用的令牌或者直接拒絕。放令牌這個動作是持續(xù)不斷的進行虚吟,如果桶中令牌數達到上限寸认,就丟棄令牌签财,所以就存在這種情況,桶中一直有大量的可用令牌偏塞,這時進來的請求就可以直接拿到令牌執(zhí)行唱蒸,比如設置qps為100,那么限流器初始化完成一秒后灸叼,桶中就已經有100個令牌了神汹,這時服務還沒完全啟動好,等啟動完成對外提供服務時古今,該限流器可以抵擋瞬時的100個請求屁魏。所以,只有桶中沒有令牌時捉腥,請求才會進行等待氓拼,最后相當于以一定的速率執(zhí)行。
- 基于Filter的限流
在過濾器工廠中是通過Redis和lua腳本結合的方式進行流量控制但狭。
- 導入redis依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
- 準備redis披诗,并啟動
- 修改yaml配置文件
server:
port: 8080
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: product
uri: lb://product # lb代表從注冊中心獲取服務
predicates:
- Path=/product-service/**
filters:
- RewritePath=/product-service/(?<segment>.*), /$\{segment}
- name: RequestRateLimiter
args:
# 使用SpEL從容器中獲取對象
key-resolver: '#{@pathKeyResolver}'
# 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
# 令牌桶的總容量
redis-rate-limiter.burstCapacity: 3
redis:
host: localhost
port: 6379
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
prefer-ip-address: true
- 配置KeyResolver
@Configuration
public class KeyResolverConfiguration {
/**
* 基于請求路徑的限流
*/
@Bean
public KeyResolver pathKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getPath().toString()
);
}
/**
* 基于請求ip地址的限流
*/
//@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getHeaders().getFirst("X-Forwarded-For")
);
}
/**
* 基于用戶的限流
*/
//@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getQueryParams().getFirst("user")
);
}
}
- 測試發(fā)現,當有大量請求時可以做到限流的功能效果
三 Gateway的高可用
- 啟動多臺gateway網關 (8080立磁,8081)
server:
port: 8080
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: product
uri: lb://product # lb代表從注冊中心獲取服務
predicates:
- Path=/product-service/**
filters:
- RewritePath=/product-service/(?<segment>.*), /$\{segment}
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
prefer-ip-address: true
- 配置ngnix
#配置多臺服務器(這里只在一臺服務器上的不同端口)
upstream gateway {
server 127.0.0.1:8081;
server 127.0.0.1:8080;
}
#請求轉向mysvr 定義的服務器列表
location / {
proxy_pass http://gateway;
}