springcloud-gateway全局过滤、限流与熔断

路由断言

Spring Cloud Gateway可以进行多种方式的路由断言,以路径方式为例,在application.properties配置文件中增加以下配置:

spring.cloud.gateway.routes[0].id=route_test1
spring.cloud.gateway.routes[0].uri=lb://springCloud-test
spring.cloud.gateway.routes[0].predicates[0]=Path=/test/**

其中:

  • id:该过滤器ID
  • uri:path匹配的请求自动转发到此地址
  • path:路径匹配内容

实现效果为
在这里插入图片描述

GlobalFilter全局过滤

在SpringCloud生态系中,可以通过GlobalFilter在Spring Cloud Gateway对所有http请求进行全局过滤,实现包括请求鉴权等功能在内的业务需求。
Spring Cloud Gateway使用的是webflux模式,如何从请求参数中提取到相关数据,特别是针对POST请求如何提取数据,根据springboot和springcloud版本的不同有不同的处理方式,本文采用的版本为:

<spring-boot.version>2.1.2.RELEASE</spring-boot.version>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>

此版本下经过参考过多个教程,最终确定采用的是Springcloud gateway获取post请求内容这篇文章的处理方法,以请求鉴权为例,代码为:

@Component
public class AuthTestFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        String method = serverHttpRequest.getMethodValue();
        System.out.println(method);
        String contentType = serverHttpRequest.getHeaders().getFirst("Content-Type");
        if (StringUtil.isNullOrEmpty(contentType)){
            contentType = "DEFAULT";
        }
        if(method.equals("POST")||contentType.startsWith("multipart/form-data")){
            return DataBufferUtils.join(exchange.getRequest().getBody())
                    .flatMap(dataBuffer -> {
                        byte[] bytes = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(bytes);
                        try {
                            String bodyString = new String(bytes, "utf-8");
                            System.out.println(formatStr(bodyString));
                            JSONObject json = JSON.parseObject(bodyString);
                            String token = json.getString("token");
                            if(authCheck(token)){
                                exchange.getAttributes().put("POST_BODY",bodyString);
                            }else {
                                ServerHttpResponse response = exchange.getResponse();
                                JSONObject responseBody = new JSONObject();
                                responseBody.put("msg","鉴权失败");
                                byte[] bits = responseBody.toJSONString().getBytes(StandardCharsets.UTF_8);
                                DataBuffer buffer2 = response.bufferFactory().wrap(bits);
                                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                                response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
                                return response.writeWith(Mono.just(buffer2));
                            }

                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                        DataBufferUtils.release(dataBuffer);
                        Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
                            DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
                            return Mono.just(buffer);
                        });

                        ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
                                exchange.getRequest()) {
                            @Override
                            public Flux<DataBuffer> getBody() {
                                return cachedFlux;
                            }
                        };
                        return chain.filter(exchange.mutate().request(mutatedRequest).build());
                    });

        }else if(method.equals("GET")){
            String token = serverHttpRequest.getQueryParams().getFirst("token");
            System.out.println(token);
            if(authCheck(token)){
                return chain.filter(exchange);
            }else {
                ServerHttpResponse response = exchange.getResponse();
                JSONObject responseBody = new JSONObject();
                responseBody.put("msg","鉴权失败");
                byte[] bits = responseBody.toJSONString().getBytes(StandardCharsets.UTF_8);
                DataBuffer buffer2 = response.bufferFactory().wrap(bits);
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
                return response.writeWith(Mono.just(buffer2));
            }
        }
        return null;
    }

    @Override
    public int getOrder() {
        return 0;
    }

    private String formatStr(String str){
        if (str != null && str.length() > 0) {
            Pattern p = Pattern.compile("\\s*|\t|\r|\n");
            Matcher m = p.matcher(str);
            return m.replaceAll("");
        }
        return str;
    }

    public boolean authCheck(String token){
        if(token.equals("qwe")){
            return true;
        }else {
            return false;
        }
    }
}

测试结果为:
GET:
在这里插入图片描述
POST:
在这里插入图片描述

限流

Spring Cloud Gateway可以通过Redis实现限流,限流的基本原理与实现方式可以参考SpringCloudGateWay系列四:限流
首先在pom.xml中增加如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

并且需要安装配置Redis

之后设计限流类型,这里以请求地址为限流依据,指定具体的限流key:

public class ApiKeyResolver implements KeyResolver {

    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getPath().value());
    }
}

@Component
public class RequestLimitCustomTest {

    @Bean(name = "apiKeyResolver")
    public ApiKeyResolver apiKeyResolver(){
        return new ApiKeyResolver();
    }

}

之后需要进行redis配置

spring.redis.host=10.18.226.209
spring.redis.port=6379
spring.redis.database=0

特定路径限流

在application.properties配置文件中增加以下配置

spring.cloud.gateway.routes[0].id=route_test1
spring.cloud.gateway.routes[0].uri=lb://springCloud-test
spring.cloud.gateway.routes[0].predicates[0]=Path=/test/**
spring.cloud.gateway.routes[0].filters[0].name=RequestRateLimiter2
#自定义的bean
spring.cloud.gateway.routes[0].filters[0].args[key-resolver]=#{@apiKeyResolver}
#每秒允许访问次数
spring.cloud.gateway.routes[0].filters[0].args[redis-rate-limiter.replenishRate]=3
#令牌桶容量
spring.cloud.gateway.routes[0].filters[0].args[redis-rate-limiter.burstCapacity]=10

凡是符合过滤要求的请求会受到限流

全局限流

在application.properties配置文件中增加以下配置

spring.cloud.gateway.default-filters[0].name=RequestRateLimiter
spring.cloud.gateway.default-filters[0].args[key-resolver]=#{@apiKeyResolver}
#每秒允许访问次数
spring.cloud.gateway.default-filters[0].args[redis-rate-limiter.replenishRate]=3
#令牌桶容量
spring.cloud.gateway.default-filters[0].args[redis-rate-limiter.burstCapacity]=10

默认对所有请求进行过滤,测试效果为:
在这里插入图片描述
可以看到一秒内请求数超过限制,之后的请求即返回失败

熔断

Spring Cloud Gateway可以通过Hystrix实现熔断
首先增加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

之后自定义熔断返回结果

@RestController
public class fallbackController {

    @RequestMapping("/hystrixfallback")
    public Map<String,String> defaultfallback(){
        System.out.println("熔断");
        Map<String,String> map = new HashMap<>();
        map.put("resultCode","1");
        map.put("resultMessage","服务异常");
        map.put("resultObj","null");
        return map;
    }

}

在配置文件中配置hystrix

hystrix.metrics.enabled=true
#超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=30000
#转移线程
hystrix.shareSecurityContext=true

此时关闭微服务,测试熔断机制:
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值