一、微服務網關Spring Cloud Gateway
1.1 導引
文中内容包含:微服務網關限流10萬QPS、跨域、過濾器、令牌桶算法。
在建構微服務系統中,必不可少的技術就是網關了,從早期的Zuul,到現在的Spring Cloud Gateway,網關我們用的不可少。
今天我就将沉澱下來的所有與網關相關的知識,用一篇文章總結清楚,希望對愛學習的小夥伴們有所幫助。
本篇文章主要介紹網關跨域配置,網關過濾器編寫,網關的令牌桶算法限流【每秒10萬QPS】
首先我們來看什麼是網關
1.2 什麼是微服務網關Gateway?
This project provides a library for building an API Gateway on top of Spring WebFlux.
gateway官網:
https://spring.io/projects/spring-cloud-gateway
實作微服務網關的技術有很多,
- nginx Nginx (engine x) 是一個高性能的HTTP和反向代理web伺服器,同時也提供了IMAP/POP3/SMTP服務
- zuul ,Zuul 是 Netflix 出品的一個基于 JVM 路由和服務端的負載均衡器。
- spring-cloud-gateway, 是spring 出品的 基于spring 的網關項目,內建斷路器,路徑重寫,性能比Zuul好。
我們使用gateway這個網關技術,無縫銜接到基于spring cloud的微服務開發中來。
1.3 微服務為什麼要使用網關呢?
不同的微服務一般會有不同的網絡位址,而外部用戶端可能需要調用多個服務的接口才能完成一個業務需求,如果讓用戶端直接與各個微服務通信,會有以下的問題:
- 用戶端會多次請求不同的微服務,增加了用戶端的複雜性
- 存在跨域請求,在一定場景下處理相對複雜
- 認證複雜,每個服務都需要獨立認證
- 難以重構,随着項目的疊代,可能需要重新劃分微服務。例如,可能将多個服務合并成一個或者将一個服務拆分成多個。如果用戶端直接與微服務通信,那麼重構将會很難實施
- 某些微服務可能使用了防火牆 / 浏覽器不友好的協定,直接通路會有一定的困難
以上這些問題可以借助網關解決。
網關是介于用戶端和伺服器端之間的中間層,所有的外部請求都會先經過 網關這一層。也就是說,API 的實作方面更多的考慮業務邏輯,而安全、性能、監控可以交由 網關來做,這樣既提高業務靈活性又不缺安全性,典型的架構圖如圖所示:
1.4 微服務網關優點
- 安全 ,隻有網關系統對外進行暴露,微服務可以隐藏在内網,通過防火牆保護。
- 易于監控。可以在網關收集監控資料并将其推送到外部系統進行分析。
- 易于認證。可以在網關上進行認證,然後再将請求轉發到後端的微服務,而無須在每個微服務中進行認證。
- 減少了用戶端與各個微服務之間的互動次數
- 易于統一授權。
1.5 總結
微服務網關就是一個系統,通過暴露該微服務網關系統,友善我們進行相關的鑒權,安全控制,日志統一處理,易于監控的相關功能。
二、微服務網關搭建及配置
2.1 微服務網關微服務搭建
由于我們開發的系統 有包括前台系統和背景系統,背景的系統給管理者使用。那麼也需要調用各種微服務,是以我們針對管理背景搭建一個網關微服務。分析如下:
搭建步驟:
(1)依賴坐标pom.xml:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
(2)啟動引導類:GatewayApplication
@SpringBootApplication
@EnableEurekaClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
(3)在resources下建立application.yml
spring:
application:
name: apigateway
cloud:
gateway:
routes:
- id: open
uri: lb://open
predicates:
- Path=/open/**
filters:
- StripPrefix= 1
- id: system
uri: lb://system
predicates:
- Path=/system/**
filters:
- StripPrefix= 1
server:
port: 9999
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
prefer-ip-address: true
參考官方手冊:
https://cloud.spring.io/spring-cloud-gateway/spring-cloud-gateway.html#_stripprefix_gatewayfilter_factory
2.2 微服務網關跨域
在啟動類GatewayApplication中,加入跨域配置代碼如下
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");//支援所有方法
config.addAllowedOrigin("*");//跨域處理 允許所有的域
config.addAllowedHeader("*");//支援所有請求頭
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);//比對所有請求
return new CorsWebFilter(source);
}
三、微服務網關過濾器
我們可以通過網關過濾器,實作一些邏輯的處理,比如ip黑白名單攔截、特定位址的攔截等。下面的代碼中做了兩個過濾器,并且設定的先後順序。
(1)在網關微服務中建立IpFilter,無需配置其他,注冊到Spring容器即可生效
@Component
public class IpFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
InetSocketAddress remoteAddress = request.getRemoteAddress();
//TODO 設定ip白名單
System.out.println("ip:"+remoteAddress.getHostName());
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 1;
}
}
(2)在網關微服務中建立UrlFilter,無需配置其他,注冊到Spring容器即可生效
@Component
public class UrlFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String url = request.getURI().getPath();
//TODO 攔截特定URL位址
System.out.println("url:"+url);
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 2;
}
}
四、網關限流每秒10萬請求
我們之前說過,網關可以做很多的事情,比如,限流,當我們的系統 被頻繁的請求的時候,就有可能 将系統壓垮,是以 為了解決這個問題,需要在每一個微服務中做限流操作,但是如果有了網關,那麼就可以在網關系統做限流,因為所有的請求都需要先通過網關系統才能路由到微服務中。
4.1 限流實作思路分析
看圖就完了,非常簡單!
4.2 令牌桶算法 介紹
令牌桶算法是比較常見的限流算法之一,大概描述如下:
1)所有的請求在處理之前都需要拿到一個可用的令牌才會被處理;
2)根據限流大小,設定按照一定的速率往桶裡添加令牌;
3)桶設定最大的放置令牌限制,當桶滿時、新添加的令牌就被丢棄或者拒絕;
4)請求達到後首先要擷取令牌桶中的令牌,拿着令牌才可以進行其他的業務邏輯,處理完業務邏輯之後,将令牌直接删除;
5)令牌桶有最低限額,當桶中的令牌達到最低限額的時候,請求處理完之後将不會删除令牌,以此保證足夠的限流
如下圖:
這個算法的實作,有很多技術,Guava(讀音: 瓜哇)是其中之一,redis用戶端也有其實作。
4.3 網關限流代碼實作
需求:每個ip位址1秒内隻能發送10萬請求,多出來的請求傳回429錯誤。
代碼實作:
(1)spring cloud gateway 預設使用redis的RateLimter限流算法來實作。是以我們要使用首先需要引入redis的依賴
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
(2)定義KeyResolver
在GatewayApplicatioin引導類中添加如下代碼,KeyResolver用于計算某一個類型的限流的KEY也就是說,可以通過KeyResolver來指定限流的Key。
//定義一個KeyResolver
@Bean
public KeyResolver ipKeyResolver() {
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
};
}
(3)修改application.yml中配置項,指定限制流量的配置以及REDIS的配置,修改後最終配置如下:
spring:
application:
name: apigateway
cloud:
gateway:
routes:
- id: open
uri: lb://open
predicates:
- Path=/open/**
filters:
- StripPrefix= 1
- name: RequestRateLimiter #請求數限流 名字不能随便寫
args:
key-resolver: "#{@ipKeyResolver}"
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 1
- id: system
uri: lb://system
predicates:
- Path=/system/**
filters:
- StripPrefix= 1
# 配置Redis 127.0.0.1可以省略配置
redis:
host: 101.57.2.128
port: 6379
server:
port: 9999
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:100/eureka
instance:
prefer-ip-address: true
解釋:
- burstCapacity:令牌桶總容量。
- replenishRate:令牌桶每秒填充平均速率。
- key-resolver:用于限流的鍵的解析器的 Bean 對象的名字。它使用 SpEL 表達式根據#{@beanName}從 Spring 容器中擷取 Bean 對象。
通過在
replenishRate
和中設定相同的值來實作穩定的速率
burstCapacity
。設定
burstCapacity
高于時,可以允許臨時突發
replenishRate
。在這種情況下,需要在突發之間允許速率限制器一段時間(根據
replenishRate
),因為2次連續突發将導緻請求被丢棄(
HTTP 429 - Too Many Requests
)
key-resolver: "#{@userKeyResolver}" 用于通過SPEL表達式來指定使用哪一個KeyResolver.
如上配置:
表示 一秒内,允許 一個請求通過,令牌桶的填充速率也是一秒鐘添加一個令牌。
最大突發狀況 也隻允許 一秒内有一次請求,可以根據業務來調整 。
(4)測試時需要注意服務啟動順序,這裡需要依賴于Redis,是以首先要啟動redis
- 啟動redis
- 啟動注冊中心
- 啟動商品微服務
- 啟動gateway網關
- 打開浏覽器 http://localhost:9999/open
- 快速重新整理,當1秒内發送超過10萬次請求,就會傳回429錯誤。
總結
Java 的知識面非常廣,面試問的涉及也非常廣泛,重點包括:Java 基礎、Java 并發,JVM、MySQL、資料結構、算法、Spring、微服務、MQ 等等,涉及的知識點何其龐大,是以我們在複習的時候也往往無從下手,今天小編給大家帶來一套 Java 面試題,題庫非常全面,包括 Java 基礎、Java 集合、JVM、Java 并發、Spring全家桶、Redis、MySQL、Dubbo、Netty、MQ 等等,包含 Java 後端知識點 2000 + ,部分如下:
資料擷取方式:關注公種浩:“程式員白楠楠”擷取上述資料