gateway 只匹配post和get_SpringCloud学习笔记——新服务网关Gateway过滤器

论坛 期权论坛 脚本     
已经匿名di用户   2022-3-21 23:58   2663   0

2f910a8ad14542aab6e000f6cd52142e.png

概述

断言是为了服务路由的匹配,过滤器则是在服务实例调用之前或之后对HTTP请求和响应的拦截,以便对请求和响应作出相应的修改。在SpringCloud学习笔记——旧服务网关Zuul中,提到过可以通过不同类型的过滤器实现身份验证、服务鉴权、参数校验、调用响应等功能。

过滤器生命周期

Gateway中只提供"PRE"和"POST"两种类型的过滤器。

PRE:服务调用请求被路由前执行该类型过滤器。主要用于实现参数校验、权限校验、流量监控、日志输出、协议转换等功能。

POST:服务调用请求被路由到具体的服务实例之后执行该过滤器。主要用于实现响应头的修改、收集统计信息和指标、将响应内容发送给客户端、输出日志、流量监控等功能。

5d1e7c207eae3e516f68c73d4a9ae54c.png

过滤器类型

在Gateway中,过滤器分为局部过滤器和全局过滤器。全局过滤器对所有服务路由有效,需要实现GlobalFilter接口;局部过滤器只对部分路由有效,需要实现GatewayFilter接口。

局部过滤器

过滤器名过滤器作用
AddRequestHeader用于在服务路由添加请求头信息
AddRequestParam用于在服务路由中添加请求参数信息
AddResponseHeader用于添加响应头信息
Hystrix用于将熔断器引入Gateway,避免服务雪崩现象发生
PrefixPath为服务调用实例路径添加前缀参数
PreserveHostHeader用于设置路由过滤器的请求属性,决定是否发送原始主机头
RequestRateLimiter用于对服务调用请求进行限流,限流算法为令牌桶
RedirectTo用于将服务调用请求重定向至指定URL
RemoveRequestHeader删除某个指定的请求头信息
RemoveResponseHeader删除某个指定的响应头信息
RewritePath使用正则表达式重写请求路径
RewriteResponseHeader重写某个指定的响应头信息
SaveSession转发服务调用请求前,强制执行保存session操作
SecureHeader用于为响应信息添加安全头
SetPath用于修改原始请求路径
SetResponseHeader用于修改指定原始响应头信息
SetStatus用于修改原始响应的状态码
StripPrefix用于截断原始请求路径
Retry针对不同的响应进行重试操作

AddRequestHeader

用于为服务调用请求添加请求头参数信息。

配置类方式创建

@Configurationpublic class CustomAddRequestHeaderFilter{    @Bean    public RouteLocator customAddHeaderRouteLocator(RouteLocatorBuilder builder){        /**         *id: gateway路由标识         *path:服务请求路径         * filters:添加请求头信息过滤器         * uri:服务实例调用地址         * 本例中的路由规则,当服务请求路径为/user/**时为服务调用请求添加请求头参数信息并将服务调用请求路由至服务实例         */        return builder.routes().route("userAddHeaderFilter",                r->r.path("/user/**")                        .filters(f->f.addRequestHeader("userId","1"))                        .uri("localhost://8100/user")).build();    }}

配置文件方式创建

spring:  cloud:    gateway:      routes:        - id:user-addHeaderFilter-route        uri:localhost://8100/user        predicates:          - Path=/user/**        filters:          - AddRequestFilter=id,1

AddRequestParam

用于在服务路由中添加请求参数。

配置类方式创建

@Configurationpublic class CustomAddRequestParamFilter {    @Bean    public RouteLocator customAddRequestParamRouteLocator(RouteLocatorBuilder builder){        /**         * id:gateway路由标识         * path:服务请求路径         * filters:添加请求参数信息过滤器         * uri:服务实例调用地址         * 本例中的路由规则,当服务请求路径为/user/**时为服务调用请求添加请求头参数信息并将服务调用请求路由至服务实例         */        return builder.routes().route("userAddRequestParam",r->r.path("/user/**")                .filters(f->f.addRequestParameter("userId","1")).uri("localhost://8100/user")).build();    }}

配置文件方式创建

spring:  cloud:    gateway:      routes:        - id:user-addHeaderFilter-route        uri:localhost://8100/user        predicates:          - Path=/user/**        filters:          - AddRequestParam=userId,1

AddResponseHeader

用于添加响应头信息。

配置类方式创建

@Configurationpublic class CustomAddResponseHeaderFilter {    @Bean    public RouteLocator customAddResponseHeaderRouteLocator(RouteLocatorBuilder builder){        /**         * id:gateway路由标识         * path:服务请求路径         * filters:添加响应头参数信息过滤器         * uri:服务实例调用地址         * 本例中的路由规则,当服务请求路径为/user/**时为服务调用请求添加响应头参数信息并将服务调用请求路由至服务实例         */        return builder.routes().route("userAddResponseHeader",                r->r.path("/user/**").filters(                        f->f.addResponseHeader("token","123456789")).uri("localhost://8100/user")).build();    }}

配置文件方式创建

spring:  cloud:    gateway:      routes:        - id:user-addResponseHeader-route        uri:localhost://8100/user        predicates:          - Path=/user/**        filters:          - AddResponseHeader=token,123456789

Hystrix

提供熔断器功能,当服务请求调用出现失败或异常情况的时候,可以执行熔断操作。当熔断发生后,可以通过服务降级功能提高用户体验,往往还需要进行服务跳转返回服务调用失败信息。

控制器

@RestController@RequestMapping("/gateway")public class GatewayFallbackController {    @GetMapping("/getFallback")    public ResultMessage getFallback()    {        return new ResultMessage(false,"服务调用路由失败,请检查服务实例状态");    }}

配置类方式创建

@Configurationpublic class CustomHystrixFilter {    @Bean    public RouteLocator customHystrixRotueLocator(RouteLocatorBuilder builder){        return builder.routes().route("userHystrix",r->r.path("/hystrix/**").filters(                f->f.hystrix(config->{                    config.setName("hystrix-cmd");                    config.setFallbackUri("forward:/gateway/getFallback");                })).uri("localhost://8100/user")).build();    }}

配置文件方式创建

spring:  cloud:    gateway:      routes:        - id:user-Hystrix-route        uri:localhost://8100/user        predicates:          - Path=/user/**        filters:          - name: Hystrix            args:              name: hystrix-cmd              fallbackUri: forward:/gateway/getFallback

Retry

提供重试功能。该工厂配置类(RetryConfig)提供如下5个配置参数

参数名参数描述
retries重试次数
statuses根据HTTP响应状态决定是否进行重试
methods请求方法,如GET、POST、PUT、DELETE
series重试状态码集合,截取响应码的第一位(1:消息;2:成功;3:重定向;4:客户端错误;5:服务端错误)
exceptions请求异常列表,默认情况下包括IOException和TimeoutException

在GatewayFilterSpec中提供了三个重试方法执行重试操作

//重试次数public GatewayFilterSpec retry(int retries) {        return this.filter(((RetryGatewayFilterFactory)this.getBean(RetryGatewayFilterFactory.class)).apply(this.routeBuilder.getId(), (retryConfig) -> {            retryConfig.setRetries(retries);        }));    }//通过参数重试消费者定义重试配置内容    public GatewayFilterSpec retry(Consumer retryConsumer) {        return this.filter(((RetryGatewayFilterFactory)this.getBean(RetryGatewayFilterFactory.class)).apply(this.routeBuilder.getId(), retryConsumer));    }//Netty底层实现重试,不推荐使用    public GatewayFilterSpec retry(Repeat repeat, Retry retry) {        RetryGatewayFilterFactory filterFactory = (RetryGatewayFilterFactory)this.getBean(RetryGatewayFilterFactory.class);        return this.filter(filterFactory.apply(this.routeBuilder.getId(), repeat, retry));    }

重试过滤器方式创建(设置重试次数)

@Configurationpublic class CustomRetryFilter {    @Bean    public RouteLocator customRetryRouteLocator(RouteLocatorBuilder builder){        /**         *id:gateway路由标识         *path:请求路径         * filters:重试工厂         * uri:服务实例地址         *          */       return builder.routes().route("hystrix-route",               r->r.path("/hystrix/**").filters(f->f.retry(2)).                       uri("localhost://8100/user")).build();    }}

重试过滤器方式创建(设置更多重试参数)

@Configurationpublic class CustomRetryFactory {    @Bean    public RouteLocator customRetryFactoryRouteLocator(RouteLocatorBuilder builder){        return builder.routes().route("retry-service",r->r.path("/hystrix/**").                filters(f->f.retry(retryConfig -> {                    retryConfig.setRetries(2);//设置重试次数                    retryConfig.setMethods(HttpMethod.GET,HttpMethod.POST,HttpMethod.PUT);//接受HTTP的请求方法,限制范围为GET、POST、PUT                    retryConfig.setStatuses(HttpStatus.INTERNAL_SERVER_ERROR,HttpStatus.BAD_REQUEST);//限制重试的响应码为服务器错误(500)和坏请求(400)                    retryConfig.setSeries(HttpStatus.Series.SERVER_ERROR);//发生500或400错误重试次数                })).uri("localhost://8100/user")).build();    }}

配置文件方式创建

spring:  cloud:    gateway:      routes:        - id:user-retry-route        uri:localhost://8100/user        predicates:          - Path=/user/**        filters:          - name: Retry            args:              retries: 2 #重试次数              methods: GET,POST,PUT #HTTP请求方法(默认只包含GET)              statuses:INTERNAL_SERVER_ERROR,BAD_REQUEST #状态限制              series: SERVER_ERROR #系列限制

RequestRateLimiter

RequestRateLimiter工厂用于限制请求流量,避免过大流量进入系统,从而保证系统在一个安全的流量下可用。在Gateway中提供了RateLimiter接口来定义限流器,该接口中的唯一非抽象实现类是RedisRateLimiter(当前只能通过Redis实现限流)。

引入依赖

                     org.springframework.boot            spring-boot-starter-data-redis-reactive                                    org.apache.commons            commons-pool2        

Redis配置

spring:  redis:    host:localhost    password:xxxxxx    lettuce:      pool:        max-active:20        max-wait:2000ms        min-idle:10        max-idle:15
Gateway配置RequestRateLimiter
spring:  cloud:    gateway:      routes:        id:rateLimiter-route        uri:localhost://8100/user        predicates:          -Path=/user/**          filters:            - name:RequestRateLimiter            args:              redis-rate-limiter.replenishRate: 10 #每秒发放的令牌数量              redis-rate-limiter.burstCapacity: 20 #令牌桶容量

RedisRateLimiter使用了令牌桶算法实现限流。令牌桶算法由三个部分组成,分别为令牌流、请求流、令牌桶。

2df5a591fe549e7c7fc35a02622e7420.png

令牌流按照一个速率流入到令牌桶中,请求流获取到令牌桶中的令牌后进行请求处理。令牌桶算法存在三种可能性:

可能性结果
请求流速率=令牌流速率每个到来的请求都能获得对应的一个令牌,交由后端服务进行请求处理(小概率)
请求流速率请求只消耗了部分令牌,未被及时消耗的令牌存储在令牌桶内,令牌桶容量装满后,桶内的令牌会溢出。(常见状态)
请求流速率>令牌桶速率消耗桶里的存量令牌,如果这种状况一直持续,会有一部分请求得不到令牌。未得到令牌的请求,会通过服务降级的方式进行处理(高并发)

上述场景仅限于通过Path的方式进行限流,有些场景下我们需要对用户进行限流。

@Configurationpublic class CustomUserRateLimiterFilter {    @Bean    public KeyResolver userKeyResolver(){        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user-id"));    }}

创建一个KeyResolver对象,并装配至IoC容器,内容是key为user-id的字符串。

spirng:  cloud:    gateway:      routes:       - id: user-rate-limiter         uri: localhost://8100/user         predicates:           - Path=/user/**           filters:             - name: RequestRateLimiter               args:                 redis-rate-limiter.replenishRate: 3 #每秒发送令牌数量                 redis-rate-limiter.burstCapacity: 2 #令牌桶容量                 key-resolver: "#{@userKeyResolver}"

在yml文件中,会按照用户编号为每个用户创建独立的令牌桶,对用户进行限流。

StripPrefix

为了区分路由的原服务器,我们需要在路径中加入前缀,此时可以使用StripPrefix执行去除前缀操作。

配置类方式创建

@Configurationpublic class StripPrefixFilter {    @Bean    public RouteLocator customStripPrefixRouteLocator(RouteLocatorBuilder builder){        /**         * id:gateway路由标识         * path:请求路径         * filter:去除前缀工厂         * uri:服务实例路径         */        return builder.routes().route("stripPrefix-route",                r->r.path("/u/**").filters(f->f.stripPrefix(1)).uri("localhost://8100/user")).build();    }}

配置文件方式创建

spirng:  cloud:    gateway:      routes:       - id: user-strip-prefix         uri: localhost://8100/user         predicates:           - Path=/user/**           filters:             - StripPrefix=1

RewritePath

用于重写服务请求路径。

配置类方式创建

@Configurationpublic class RewritePathFilter {    @Bean    public RouteLocator customRewritePathRouteLocator(RouteLocatorBuilder builder){        /**         * id:gateway路由标识         * path:请求路径         * filters:重写请求路径过滤器         * uri:服务实例路径         */        return builder.routes().route("rewritePath-route",                r->r.path("/u/**").filters(                        f->f.rewritePath("/u/(?.*)","/$\\{segment}"))                        .uri("localhost://8100/user")).build();    }}

配置文件方式创建

spirng:  cloud:    gateway:      routes:       - id: rewritePath-route         uri: localhost://8100/user         predicates:           - Path=/u/**           filters:             - RewritePath=/u/(?.*),/$\{segment}

SetStatus

在有些情况下,服务请求会调用失败,转到快速失败链接,此时需要在快速失败处理器中给出快速失败提示信息,并且将响应状态码设置为坏请求(400)。

配置类方式创建

@Configurationpublic class SetStatusFilter {    @Bean    public RouteLocator customSetStatusRouteLocator(RouteLocatorBuilder builder)    {        /**         * id:gateway路由标识         * path:请求路径         * filters:设置状态码过滤器         * uri:服务实例地址         */        return builder.routes().route("setStatus-filter",r->r.path("/user/**").                filters(f->f.setStatus(HttpStatus.BAD_REQUEST.value())).uri("localhost://8100/user")).build();    }}

配置文件方式创建

spirng:  cloud:    gateway:      routes:       - id: setStatus-filter         uri: localhost://8100/user         predicates:           - Path=/user/**           filters:             - SetStatus=BAD_REQUEST

全局过滤器

在Spring Cloud Gateway中,提供了一些内置的全局过滤器:

过滤器名称过滤器描述
AdaptCachedBodyGlobalFilter缓存请求体过滤器
ForwardPathFilter处理forward类型请求,对请求路径进行解析,结合ForwardRoutingFilter进行请求转发
ForwardRoutingFilter结合ForwardPathFilter完成请求转发,根据DispatcherHandler处理请求
GatewayMetricsFilter基于MicroMeter完成Spring Cloud Gateway Metrics信息的统计
LoadBalancerClientFilter处理LoadBalancer类型请求,基于Spring Cloud LoadBalancer和服务注册中心完成服务实例查询
NettyRoutingFilter处理Http或Https类型的请求,内部使用Netty HttpClient客户端完成请求调用
NettyWriteResponseFilter通过Netty HttpClient回写Response信息到客户端
WebClientHttpRoutingFilter处理Http或Https类型的请求,内部使用Spring WebClient完成请求调用。默认情况下使用NettyRoutingFilter,可通过修改配置信息改为使用WebClientHttpRoutingFilter
WebClientWriteResponseFilter通过Spring WebClient回写Response信息到客户端
WebsocketRoutingFilter代理WebSocket请求

自定义过滤器

自定义过滤器包括三个内容:自定义局部过滤器,自定义全局过滤器,自定义过滤器执行顺序。

自定义局部过滤器

在本节中我们将通过Resilience4j实现自定义局部过滤器限流功能。

项目依赖

                      io.github.resilience4j            resilience4j-spring-boot2            0.13.2        

在Gateway中,自定义局部过滤器需要实现GatewayFilter接口。

package org.springframework.cloud.gateway.filter;import org.springframework.cloud.gateway.support.ShortcutConfigurable;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Mono;public interface GatewayFilter extends ShortcutConfigurable {    String NAME_KEY = "name";    String VALUE_KEY = "value";    Mono filter(ServerWebExchange exchange, GatewayFilterChain chain);}

该接口仅仅有1个filter方法,接受两个参数:

exchange:数据交换对象,通过getRequest方法可以获取请求对象,通过getResponse方法可以获取响应对象。

chain:Gateway过滤器链,调用过滤器链filter方法,表示执行过滤器链中后续的过滤器。

Resilience4j限速器配置

#resilience4j配置resilience4j:  #限速器注册机  ratelimiter:    limiters:      #名称为user的限速器      user:        #时间戳内限制通过的请求数(默认50)        limitForPeriod: 3        #配置时间戳(单位毫秒,默认500)        limitRefreshPeriodInMillis: 5000        #超时时间        timeoutInMillis: 10

通过上述配置,SpringBoot会将resilience4j限速器装配到IoC容器,供我们直接使用。为了实现服务限流功能,在Gateway程序入口处添加如下代码。

@Autowired    private RateLimiterRegistry rateLimiterRegistry;    /**     * 限流操作逻辑     * @return 限流操作结果     */    private ResultMessage rateLimiterResult()    {        //获取配置文件中名为user的限速器        RateLimiter rateLimiter=rateLimiterRegistry.rateLimiter("user");        //绑定限速器        Callable callResult=RateLimiter.decorateCallable(rateLimiter,() -> new ResultMessage(true,"callSuccess"));        //尝试获取限速器执行结果        Try tryResult=Try.of(() -> callResult.call())                //服务降级(超过限速器限速)逻辑                .recover(ex ->new ResultMessage(false,"callFailed"));        ResultMessage resultMessage=tryResult.get();        return resultMessage;    }

在上述代码中,对Resilience4j限流器进行使用,如果在时间戳内请求数未超过配置参数,返回调用成功;请求数超过配置参数,执行服务降级操作,返回服务调用失败;自定义局部过滤器代码如下

/**     * 自定义局部过滤器执行服务限流操作     * @return Gatew局部过滤器     */ private GatewayFilter customResilience4jGatewayFilter()    {        return ((exchange, chain) -> {            ResultMessage resultMessage=rateLimiterResult();            //服务降级            if(!resultMessage.getResult())            {                ServerHttpResponse response=exchange.getResponse();                //设置响应码为429(请求数过多)                response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);                //设置响应头类型                response.getHeaders().setContentType(MediaType.APPLICATION_JSON);                //将服务调用响应结果转换为Json字符串                String message=resultToJson(resultMessage);                //将限流结果放入数据缓冲区                DataBuffer dataBuffer=response.bufferFactory().wrap(message.getBytes());                //使用限流结果响应请求,不在执行过滤器链中的其他过滤器                return  response.writeWith(Mono.just(dataBuffer));            }            //继续执行过滤器链中的其他过滤器            return chain.filter(exchange);        });    }    /**     * 将调用结果转换为Json字符串     * @param resultMessage:调用结果     * @return Json字符串     */    private String resultToJson(ResultMessage resultMessage)    {        ObjectMapper objectMapper=new ObjectMapper();        String message=null;        try {            message=objectMapper.writeValueAsString(resultMessage);        }catch (JsonProcessingException exception)        {            exception.printStackTrace();        }        return message;    }

自定义限速局部过滤器使用方式如下:

 @Bean    public RouteLocator customRouteLocator(RouteLocatorBuilder builder)    {        /**         * 为Gateway路由构建器创建路由规则         * id:微服务实例标识         * path:断言类型         * filters:自定义限速过滤器         * uri:服务实例调用地址         *本例中的路由规则:当服务请求路径为/user/**时,将服务路由到实际处理用户服务的服务实例localhost://8100/user         */        return builder.routes().route("user-route",r->r.path("/user/**")                .filters(f->f.filter(customResilience4jGatewayFilter())).uri("localhost://8100/user")).build();    }

自定义局部限速过滤器使用完整代码

package com.spring.cloud.gateway.config;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import com.spring.cloud.gateway.pojo.ResultMessage;import io.github.resilience4j.ratelimiter.RateLimiter;import io.github.resilience4j.ratelimiter.RateLimiterRegistry;import io.vavr.control.Try;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.gateway.filter.GatewayFilter;import org.springframework.cloud.gateway.route.RouteLocator;import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.buffer.DataBuffer;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.server.reactive.ServerHttpResponse;import reactor.core.publisher.Mono;import java.util.concurrent.Callable;@Configurationpublic class GatewayCustomResilience4jRoutePredicateConfig {    @Autowired    private RateLimiterRegistry rateLimiterRegistry;    @Bean    public RouteLocator customRouteLocator(RouteLocatorBuilder builder)    {        /**         * 为Gateway路由构建器创建路由规则         * id:微服务实例标识         * path:断言类型         * filters:自定义限速过滤器         * uri:服务实例调用地址         *本例中的路由规则:当服务请求路径为/user/**时,将服务路由到实际处理用户服务的服务实例localhost://8100/user         */        return builder.routes().route("user-route",r->r.path("/user/**")                .filters(f->f.filter(customResilience4jGatewayFilter())).uri("localhost://8100/user")).build();    }    /**     * 限流操作逻辑     * @return 限流操作结果     */    private ResultMessage rateLimiterResult()    {        //获取配置文件中名为user的限速器        RateLimiter rateLimiter=rateLimiterRegistry.rateLimiter("user");        //绑定限速器        Callable callResult=RateLimiter.decorateCallable(rateLimiter,() -> new ResultMessage(true,"callSuccess"));        //尝试获取限速器执行结果        Try tryResult=Try.of(() -> callResult.call())                //服务降级(超过限速器限速)逻辑                .recover(ex ->new ResultMessage(false,"callFailed"));        ResultMessage resultMessage=tryResult.get();        return resultMessage;    }    /**     * 自定义局部过滤器执行服务限流操作     * @return Gatew局部过滤器     */    private GatewayFilter customResilience4jGatewayFilter()    {        return ((exchange, chain) -> {            ResultMessage resultMessage=rateLimiterResult();            //服务降级            if(!resultMessage.getResult())            {                ServerHttpResponse response=exchange.getResponse();                //设置响应码为429(请求数过多)                response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);                //设置响应头类型                response.getHeaders().setContentType(MediaType.APPLICATION_JSON);                //将服务调用响应结果转换为Json字符串                String message=resultToJson(resultMessage);                //将限流结果放入数据缓冲区                DataBuffer dataBuffer=response.bufferFactory().wrap(message.getBytes());                //使用限流结果响应请求,不在执行过滤器链中的其他过滤器                return  response.writeWith(Mono.just(dataBuffer));            }            //继续执行过滤器链中的其他过滤器            return chain.filter(exchange);        });    }    /**     * 将调用结果转换为Json字符串     * @param resultMessage:调用结果     * @return Json字符串     */    private String resultToJson(ResultMessage resultMessage)    {        ObjectMapper objectMapper=new ObjectMapper();        String message=null;        try {            message=objectMapper.writeValueAsString(resultMessage);        }catch (JsonProcessingException exception)        {            exception.printStackTrace();        }        return message;    }}

自定义全局过滤器

在Gateway中实现自定义全局过滤器,需要实现GatewayFilter接口,并且装配到IoC容器中即可。在实际的应用中,我们通常需要携带token(令牌)信息在服务实例调用过程中进行鉴权,在发起服务请求通过服务网关时,需要获取携带的token信息进行权限校验,校验通过后将服务调用请求路由到具体的服务实例执行服务调用操作。

import io.micrometer.core.instrument.util.StringUtils;import org.springframework.cloud.gateway.filter.GlobalFilter;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.server.reactive.ServerHttpRequest;@Configurationpublic class GatewayCustomTokenRoutePredicateConfig {    @Bean    public GlobalFilter globalTokenFilter()    {        return ((exchange, chain) -> {            //判断请求头是否携带token信息            boolean tokenMessage= !StringUtils.isBlank(exchange.getRequest().getHeaders().getFirst("token"));            //存在直接放行服务路由            if(tokenMessage)            {                return chain.filter(exchange);            }            //从请求中获取token参数            String tokenParam=exchange.getRequest().getQueryParams().getFirst("token");            ServerHttpRequest request=null;            //请求中存在token参数信息            if(!StringUtils.isBlank(tokenParam))            {                //将token参数放进请求头并创建request对象                request=exchange.getRequest().mutate().header("token",tokenParam).build();                //创建request对象并构造请求头后放行服务路由                    return  chain.filter(exchange.mutate().request(request).build());            }            //放行服务路由            return chain.filter(exchange);        });    }}

该方法返回类型为GlobalFilter,Gateway认为该过滤器为全局过滤器,无需向局部过滤器一样在构建服务路由时执行过滤器注册相关的操作。

自定义过滤器执行顺序

过滤器名称执行顺序
AdaptCachedBodyGlobalFilterHIGHESC_PRECEDENCE+1000
ForwardPathFilter0
ForwardRoutingFilterLOWEST_PRECEDENCE-1
GatewayMetricsFilterHIGHESC_PRECEDENCE+10000
LoadBalancerClientFilter10100
NettyRoutingFilterLOWEST_PRECEDENCE
NettyWriteResponseFilter-1
WebClientHttpRoutingFilterLOWEST_PRECEDENCE
WebClientWriteResponseFilter-1
WebsocketRoutingFilterLOWEST_PRECEDENCE-1

在为自定义过滤器设置执行顺序的过程中,需要实现Ordered接口。

package org.springframework.core;public interface Ordered {    int HIGHEST_PRECEDENCE = -2147483648;    int LOWEST_PRECEDENCE = 2147483647;    int getOrder();}

重新实现全局过滤器并设置执行顺序。

import io.micrometer.core.instrument.util.StringUtils;import org.springframework.cloud.gateway.filter.GatewayFilterChain;import org.springframework.cloud.gateway.filter.GlobalFilter;import org.springframework.core.Ordered;import org.springframework.http.server.reactive.ServerHttpRequest;import org.springframework.stereotype.Component;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Mono;@Componentpublic class GatewayCustomTokenRoute implements GlobalFilter, Ordered {    @Override    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {            //判断请求头是否携带token信息            boolean tokenMessage= !StringUtils.isBlank(exchange.getRequest().getHeaders().getFirst("token"));            //存在直接放行服务路由            if(tokenMessage)            {                return chain.filter(exchange);            }            //从请求中获取token参数            String tokenParam=exchange.getRequest().getQueryParams().getFirst("token");            ServerHttpRequest request=null;            //请求中存在token参数信息            if(!StringUtils.isBlank(tokenParam))            {                //将token参数放进请求头并创建request对象                request=exchange.getRequest().mutate().header("token",tokenParam).build();                //创建request对象并构造请求头后放行服务路由                return  chain.filter(exchange.mutate().request(request).build());            }            //放行服务路由            return chain.filter(exchange);    }    @Override    public int getOrder() {        return HIGHEST_PRECEDENCE+10001;    }}

重新实现局部过滤器并设置排序顺序

import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import com.spring.cloud.gateway.pojo.ResultMessage;import io.github.resilience4j.ratelimiter.RateLimiter;import io.github.resilience4j.ratelimiter.RateLimiterRegistry;import io.vavr.control.Try;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.gateway.filter.GatewayFilter;import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;import org.springframework.cloud.gateway.route.RouteLocator;import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.buffer.DataBuffer;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.server.reactive.ServerHttpResponse;import reactor.core.publisher.Mono;import java.util.concurrent.Callable;@Configurationpublic class GatewayCustomResilience4jRoute {    @Autowired    private RateLimiterRegistry rateLimiterRegistry;    @Bean    public RouteLocator customRouteLocator(RouteLocatorBuilder builder)    {        /**         * 为Gateway路由构建器创建路由规则         * id:微服务实例标识         * path:断言类型         * filters:自定义限速过滤器         * uri:服务实例调用地址         *本例中的路由规则:当服务请求路径为/user/**时,将服务路由到实际处理用户服务的服务实例localhost://8100/user         */        return builder.routes().route("user-route",r->r.path("/user/**")                .filters(f->f.filter(customResilience4jGatewayFilter())).uri("localhost://8100/user")).build();    }    /**     * 限流操作逻辑     * @return 限流操作结果     */    private ResultMessage rateLimiterResult()    {        //获取配置文件中名为user的限速器        RateLimiter rateLimiter=rateLimiterRegistry.rateLimiter("user");        //绑定限速器        Callable callResult=RateLimiter.decorateCallable(rateLimiter,() -> new ResultMessage(true,"callSuccess"));        //尝试获取限速器执行结果        Try tryResult=Try.of(() -> callResult.call())                //服务降级(超过限速器限速)逻辑                .recover(ex ->new ResultMessage(false,"callFailed"));        ResultMessage resultMessage=tryResult.get();        return resultMessage;    }    /**     * 自定义局部过滤器执行服务限流操作并设置执行顺序     * @return OrderedGateway局部过滤器     */    private OrderedGatewayFilter customResilience4jGatewayFilter()    {        int filterOrder=-100;        GatewayFilter gatewayFilter=(exchange, chain) -> {            ResultMessage resultMessage=rateLimiterResult();            //服务降级            if(!resultMessage.getResult())            {                ServerHttpResponse response=exchange.getResponse();                //设置响应码为429(请求数过多)                response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);                //设置响应头类型                response.getHeaders().setContentType(MediaType.APPLICATION_JSON);                //将服务调用响应结果转换为Json字符串                String message=resultToJson(resultMessage);                //将限流结果放入数据缓冲区                DataBuffer dataBuffer=response.bufferFactory().wrap(message.getBytes());                //使用限流结果响应请求,不在执行过滤器链中的其他过滤器                return  response.writeWith(Mono.just(dataBuffer));            }            //继续执行过滤器链中的其他过滤器            return chain.filter(exchange);        };        return new OrderedGatewayFilter(gatewayFilter,filterOrder);    }    /**     * 将调用结果转换为Json字符串     * @param resultMessage:调用结果     * @return Json字符串     */    private String resultToJson(ResultMessage resultMessage)    {        ObjectMapper objectMapper=new ObjectMapper();        String message=null;        try {            message=objectMapper.writeValueAsString(resultMessage);        }catch (JsonProcessingException exception)        {            exception.printStackTrace();        }        return message;    }}
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:81
帖子:4969
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP