<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Gateway服務(wù)網(wǎng)關(guān)之過濾器

          共 8083字,需瀏覽 17分鐘

           ·

          2021-11-08 15:10

          文章已收錄到我的Github精選,歡迎Star:https://github.com/yehongzhi/learningSummary

          寫在前面

          前一篇文章寫了Gateway的Predicate(用于路由轉(zhuǎn)發(fā)),那么這篇文章就介紹另一個主要的核心,那就是Filter(過濾器)。

          過濾器有什么作用呢?工作流程是怎么樣的呢?請看下圖:

          從圖中很明顯可以看出,在請求后端服務(wù)前后都需要經(jīng)過Filter,于是乎Filter的作用就明確了,在PreFilter(請求前處理)可以做參數(shù)校驗、流量監(jiān)控、日志記錄、修改請求內(nèi)容等等,在PostFilter(請求后處理)可以做響應(yīng)內(nèi)容修改。

          過濾器

          Filter分為局部和全局兩種:

          • 局部Filter(GatewayFilter的子類)是作用于單個路由。如果需要使用全局路由,需要配置Default Filters。
          • 全局Filter(GlobalFilter的子類),不需要配置路由,系統(tǒng)初始化作用到所有路由上。

          局部過濾器

          SpringCloud Gateway內(nèi)置了很多路由過濾器,他們都是由GatewayFilter的工廠類產(chǎn)生。

          AddRequestParameter GatewayFilter

          該過濾器可以給請求添加參數(shù)。

          比如我在consumer服務(wù)有一個帶有userName參數(shù)的接口,我想請求網(wǎng)關(guān)路由轉(zhuǎn)發(fā)的時候給加上一個userName=yehongzhi的參數(shù)。

          @RequestMapping(value?=?"/getOrder",method?=?RequestMethod.GET)
          public?String?getOrder(@RequestParam(name?=?"userName")?String?userName)?{
          ????return?"獲取到傳入的用戶名稱:"?+?userName;
          }

          配置如下:

          spring:
          ??cloud:
          ????gateway:
          ??????routes:
          ????????-?id:?add_request_parameter
          ??????????uri:?http://localhost:8081/getOrder
          ??????????predicates:
          ????????????-?Method=GET
          ????????????-?Path=/getOrder
          ??????????filters:
          ????????????-?AddRequestParameter=userName,yehongzhi

          那么當我請求網(wǎng)關(guān)時,輸入http://localhost:9201/getOrder,我們能看到默認加上的userName。

          StripPrefix GatewayFilter

          該過濾器可以去除指定數(shù)量的路徑前綴。

          比如我想把請求網(wǎng)關(guān)的路徑前綴的第一級去掉,就可以這樣配置實現(xiàn):

          spring:
          ??cloud:
          ????gateway:
          ??????routes:
          ????????-?id:?strip_prefix_gateway
          ??????????uri:?http://localhost:8081
          ??????????predicates:
          ????????????-?Path=/consumer/**
          ??????????filters:
          ????????????-?StripPrefix=1

          當請求路徑http://localhost:9201/consumer/getDetail/1,能獲得結(jié)果。

          相當于請求http://localhost:8081/getDetail/1,結(jié)果是一樣的。

          PrefixPath GatewayFilter

          該過濾器與上一個過濾器相反,是給原有的路徑加上指定的前綴。

          spring:
          ??cloud:
          ????gateway:
          ??????routes:
          ????????-?id:?prefix_path_gateway
          ??????????uri:?http://localhost:8081
          ??????????predicates:
          ????????????-?Path=/getUserInfo/**
          ??????????filters:
          ????????????-?PrefixPath=/consumer

          當請求http://localhost:9201/getUserInfo/1時,跟請求http://localhost:8081/consumer/getUserInfo/1是一樣的。

          Hystrix GatewayFilter

          網(wǎng)關(guān)當然有熔斷機制,所以該過濾器集成了Hystrix,實現(xiàn)了熔斷的功能。怎么使用呢?首先需要引入Hystrix的maven依賴。

          <dependency>
          ????<groupId>org.springframework.cloudgroupId>
          ????<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
          dependency>

          在gateway服務(wù)添加fallback()方法

          @RestController
          public?class?FallBackController?{
          ????@RequestMapping("/fallback")
          ????public?String?fallback(){
          ????????return?"系統(tǒng)繁忙,請稍后再試!";
          ????}
          }

          在網(wǎng)關(guān)服務(wù)的配置如下,對GET請求方式的請求路由轉(zhuǎn)發(fā)出錯時,會觸發(fā)服務(wù)降級:

          spring:
          ??cloud:
          ????gateway:
          ??????routes:
          ????????-?id:?hystrix_filter
          ??????????uri:?http://localhost:8081
          ??????????predicates:
          ????????????-?Method=GET
          ??????????filters:
          ????????????-?name:?Hystrix
          ??????????????args:
          ????????????????name:?fallbackcmd
          ????????????????fallbackUri:?forward:/fallback

          這時我們把8081的服務(wù)停止,讓網(wǎng)關(guān)請求不到對應(yīng)的服務(wù),從而觸發(fā)服務(wù)降級。

          RequestRateLimiter GatewayFilter

          該過濾器可以提供限流的功能,使用RateLimiter實現(xiàn)來確定是否允許當前請求繼續(xù)進行,如果請求太大默認會返回HTTP 429-太多請求狀態(tài)。怎么用呢?首先還是得引入Maven依賴。

          <dependency>
          ????<groupId>org.springframework.bootgroupId>
          ????<artifactId>spring-boot-starter-data-redis-reactiveartifactId>
          dependency>

          接著增加個配置類。

          @Configuration
          public?class?LimiterConfig?{

          ????/**
          ?????*?ip限流器
          ?????*/

          ????@Bean
          ????public?KeyResolver?ipKeyResolver()?{
          ????????return?exchange?->?Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
          ????}
          }

          然后配置如下,對GET方式的請求增加限流策略:

          spring:
          ??cloud:
          ????gateway:
          ??????routes:
          ????????-?id:?hystrix_filter
          ??????????uri:?http://localhost:8081
          ??????????predicates:
          ????????????-?Method=GET
          ??????????filters:
          ????????????-?name:?RequestRateLimiter
          ??????????????args:
          ????????????????redis-rate-limiter.replenishRate:?1?#每秒允許處理的請求數(shù)量
          ????????????????redis-rate-limiter.burstCapacity:?2?#每秒最大處理的請求數(shù)量
          ????????????????key-resolver:?"#{@ipKeyResolver}"?#限流策略,對應(yīng)策略的Bean
          ??redis:
          ????port:?6379
          ????host:?192.168.1.5?#redis地址

          然后啟動服務(wù),連續(xù)請求地址http://localhost:9201/getDetail/1,就會觸發(fā)限流,報429錯誤。

          因為內(nèi)置的過濾器實在是太多了,這里就不一一列舉了,有興趣的同學(xué)可以到官網(wǎng)自行學(xué)習(xí)。

          自定義局部過濾器

          如果內(nèi)置的局部過濾器不能滿足需求,那么我們就得使用自定義過濾器,怎么用呢?下面用一個例子,我們自定義一個白名單的過濾器,userName在白名單內(nèi)的才可以訪問,不在白名單內(nèi)的就返回401錯誤碼(Unauthorized)。

          局部過濾器需要實現(xiàn)GatewayFilter和Ordered接口,代碼如下:

          public?class?WhiteListGatewayFilter?implements?GatewayFilter,?Ordered?{
          ?//白名單集合
          ????private?List?whiteList;
          ?//通過構(gòu)造器初始化白名單
          ????WhiteListGatewayFilter(List?whiteList)?{
          ????????this.whiteList?=?whiteList;
          ????}

          ????@Override
          ????public?Mono?filter(ServerWebExchange?exchange,?GatewayFilterChain?chain)?{
          ????????String?userName?=?exchange.getRequest().getQueryParams().getFirst("userName");
          ????????//白名單不為空,并且userName包含在白名單內(nèi),才可以訪問
          ????????if?(!CollectionUtils.isEmpty(whiteList)?&&?whiteList.contains(userName))?{
          ????????????return?chain.filter(exchange);
          ????????}
          ????????//如果白名單為空或者userName不在白名單內(nèi),則返回401
          ????????exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
          ????????return?exchange.getResponse().setComplete();
          ????}

          ????//優(yōu)先級,值越小優(yōu)先級越高
          ????@Override
          ????public?int?getOrder()?{
          ????????return?0;
          ????}
          }

          接著再定義一個過濾器工廠,注入到Spring容器中,代碼如下:

          @Component
          public?class?WhiteListGatewayFilterFactory?extends?AbstractConfigurable<WhiteListGatewayFilterFactory.Config>?implements?GatewayFilterFactory<WhiteListGatewayFilterFactory.Config>?{

          ????private?static?final?String?VALUE?=?"value";

          ????protected?WhiteListGatewayFilterFactory()?{
          ????????super(WhiteListGatewayFilterFactory.Config.class);
          ????}

          ????@Override
          ????public?List?shortcutFieldOrder()?{
          ????????return?Collections.singletonList(VALUE);
          ????}

          ????@Override
          ????public?GatewayFilter?apply(Config?config)?{
          ????????//獲取配置的白名單
          ????????String?whiteString?=?config.getValue();
          ????????List?whiteList?=?new?ArrayList<>(Arrays.asList(whiteString.split(",")));
          ????????//創(chuàng)建WhiteListGatewayFilter實例,返回
          ????????return?new?WhiteListGatewayFilter(whiteList);
          ????}
          ?//用于接收配置參數(shù)
          ????public?static?class?Config?{

          ????????private?String?value;

          ????????public?String?getValue()?{
          ????????????return?value;
          ????????}

          ????????public?void?setValue(String?value)?{
          ????????????this.value?=?value;
          ????????}
          ????}
          }

          最后,我們可以在application.yaml配置文件中加上配置使用:

          spring:
          ??cloud:
          ????gateway:
          ??????routes:
          ????????-?id:?white_list_filter
          ??????????uri:?http://localhost:8081
          ??????????predicates:
          ????????????-?Method=GET
          ??????????filters:
          ????????????-?WhiteList=yehongzhi?#等號后面配置的是白名單,用逗號隔開

          接著啟動項目,先請求localhost:9201/getDetail/1,不帶userName,按預(yù)期會返回401,不能訪問。

          請求帶有userName=yehongzhi的地址http://localhost:9201/getDetail/1?userName=yehongzhi,是在白名單內(nèi)的,所以能正常訪問。

          全局過濾器

          全局過濾器在系統(tǒng)初始化時就作用于所有的路由,不需要單獨去配置。全局過濾器的接口定義類是GlobalFilter,Gateway本身也有很多內(nèi)置的過濾器,我們打開類圖看看:

          我們拿幾個比較有代表性的來做介紹,比如負載均衡的全局過濾器LoadBalancerClientFilter。

          LoadBalancerClientFilter

          該過濾器會解析到以lb://開頭的uri,比如這樣的配置:

          spring:
          ??application:
          ????name:?api-gateway
          ??cloud:
          ????nacos:
          ??????discovery:
          ????????server-addr:?127.0.0.1:8848
          ????????service:?${spring.application.name}
          ????gateway:
          ??????routes:
          ????????-?id:?consumer
          ??????????uri:?lb://consumer?#使用lb協(xié)議,consumer是服務(wù)名,不再使用IP地址配置
          ??????????order:?1
          ??????????predicates:
          ????????????-?Path=/consumer/**?

          這個全局過濾器就會取到consumer這個服務(wù)名,然后通過LoadBalancerClient獲取到ServiceInstance服務(wù)實例。根據(jù)獲取到的服務(wù)實例,重新組裝請求的url。

          這就是一個全局過濾器應(yīng)用的例子,它是作用于全局,而且并不需要配置。下面我們探索一下自定義全局過濾器,假設(shè)需要統(tǒng)計用戶的IP地址訪問網(wǎng)關(guān)的總次數(shù),怎么做呢?

          自定義全局過濾器

          自定義全局過濾器需要實現(xiàn)GlobalFilter接口和Ordered接口。

          @Component
          public?class?IPAddressStatisticsFilter?implements?GlobalFilter,?Ordered?{

          ????@Override
          ????public?Mono?filter(ServerWebExchange?exchange,?GatewayFilterChain?chain)?{
          ????????InetSocketAddress?host?=?exchange.getRequest().getHeaders().getHost();
          ????????if?(host?==?null?||?host.getHostName()?==?null)?{
          ????????????exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
          ????????????return?exchange.getResponse().setComplete();
          ????????}
          ????????String?hostName?=?host.getHostName();
          ????????AtomicInteger?count?=?IpCache.CACHE.getOrDefault(hostName,?new?AtomicInteger(0));
          ????????count.incrementAndGet();
          ????????IpCache.CACHE.put(hostName,?count);
          ????????System.out.println("IP地址:"?+?hostName?+?",訪問次數(shù):"?+?count.intValue());
          ????????return?chain.filter(exchange);
          ????}

          ????@Override
          ????public?int?getOrder()?{
          ????????return?10101;
          ????}
          }

          //用于保存次數(shù)的緩存
          public?class?IpCache?{
          ????public?static?final?Map?CACHE?=?new?ConcurrentHashMap<>();
          }

          啟動項目,然后請求服務(wù),可以看到控制臺打印結(jié)果。

          IP地址:192.168.1.4,訪問次數(shù):1
          IP地址:192.168.1.4,訪問次數(shù):2
          IP地址:192.168.1.4,訪問次數(shù):3
          IP地址:localhost,訪問次數(shù):1
          IP地址:localhost,訪問次數(shù):2
          IP地址:localhost,訪問次數(shù):3
          IP地址:192.168.1.4,訪問次數(shù):4

          總結(jié)

          通過上一篇的Predicates和這篇的Filters基本上把服務(wù)網(wǎng)關(guān)的功能都實現(xiàn)了,包括路由轉(zhuǎn)發(fā)、權(quán)限攔截、流量統(tǒng)計、流量控制、服務(wù)熔斷、日志記錄等等。所以網(wǎng)關(guān)對于微服務(wù)架構(gòu)來說,網(wǎng)關(guān)服務(wù)是一個非常重要的部分,有很多一線的互聯(lián)網(wǎng)公司還會自研服務(wù)網(wǎng)關(guān)。因此掌握服務(wù)網(wǎng)關(guān)對于后端開發(fā)可以說是必備技能,感謝大家的閱讀。

          覺得有用就點個贊吧,你的點贊是我創(chuàng)作的最大動力~

          我是一個努力讓大家記住的程序員。我們下期再見?。?!

          能力有限,如果有什么錯誤或者不當之處,請大家批評指正,一起學(xué)習(xí)交流!

          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费看日韩毛片 | 影视先锋成人在线 | 欧美成人靠逼小视频 | 国产白丝自慰 | 成人aaa |