微服务架构➖SpringCloud➖Gateway(3)

微服务架构➖SpringCloud➖Gateway

关于作者

  • 作者介绍

🍓 博客主页:作者主页

🍓 简介:JAVA领域优质创作者🥇、一名在校大三学生🎓、在校期间参加各种省赛、国赛,斩获一系列荣誉 🏆、阿里云专家博主51CTO专家博主

🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨‍💻


9. Filter 过滤器工厂(重点)

gateway存在两种相似功能的过滤器,分别是Gateway中的过滤器和Servlet中的过滤器。这些过滤器可以用于修改进入的HTTP请求和返回的HTTP响应。

首先,按照生命周期的不同,这些过滤器可以分为两种类型:

  • "pre"类型:在业务逻辑之前执行。
  • "post"类型:在业务逻辑之后执行。

其次,按照种类的不同,过滤器可以进一步划分为两种类型:

  • GatewayFilter:需要配置特定的路由才能生效。如果需要应用到全局路由,需要配置DefaultFilters。
  • GlobalFilter:全局过滤器,无需配置特定路由,会在系统初始化时应用到所有路由上。全局过滤器可以用于统计请求次数、限流、验证令牌、拦截IP黑名单、处理跨域问题等。实际上,它们本质上都是过滤器。

通过使用这些过滤器,我们可以对进入的HTTP请求和返回的HTTP响应进行必要的修改和处理,例如限制以"135" 等其他开头的电话号码或限制特定IP的访问。

自定义过滤器

9.1 全局过滤器 myGlobalFilter

java 复制代码
package com.zmz.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.HashMap;

/**
 * @ProjectName: 04-hystrix
 * @Package: com.zmz.filter
 * @ClassName: GlobalFilter
 * @Author: 张晟睿
 * @Date: 2022/10/9 16:32
 * @Version: 1.0
 */

/**
 * 对过滤器进行排序
 */
@Component
public class myGlobalFilter implements GlobalFilter, Ordered {
    /**
     * 这个就是过滤方法
     * 过滤器链模式
     * 责任链模式
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //针对请求的过滤  拿到请求头   header  url  参数
        ServerHttpRequest request = exchange.getRequest();

        String path = request.getURI().getPath();
        System.out.println(path);
        HttpHeaders headers = request.getHeaders();
        System.out.println(headers);
        String name = request.getMethod().name();
        System.out.println(name);
        String hostName = request.getRemoteAddress().getHostName();
        //request.getHeaders().getHost().getHostString()  拿到ipv4  上图为0:0:0:0:0:0:0:1 为ipv6地址
        System.out.println(hostName);

        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().set("content-type","application/json;charset=utf-8");
        //组装业务返回值
        HashMap<String, Object> map = new HashMap<>();
        map.put("code", HttpStatus.UNAUTHORIZED.value());
        map.put("msg", "未授权token");

        ObjectMapper objectMapper = new ObjectMapper();
        byte[] bytes = new byte[0];
        try {
            bytes = objectMapper.writeValueAsBytes(map);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }


        //获取字节工厂-> 包装成databuffer
        DataBuffer wrap = response.bufferFactory().wrap(bytes);
// 放行       chain.filter(exchange);
        return response.writeWith(Mono.just(wrap));

//        return chain.filter(exchange);
    }

    /**
     * 指定顺序的方法,越小越先执行
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

9.2 IP黑名单过滤器IPCheckFilter

JAVA 复制代码
package com.zmz.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
 * 网关里面 过滤器
 * ip拦截
 * 请求都有一个源头
 * 电话 144  027  010
 * 请求------->gateway------->service
 * 黑名单 black_list
 * 白名单 white_list
 * 根据数量
 * 像具体的业务服务 一般黑名单
 * 一般像数据库 用白名单
 */

/**
 * @ProjectName: 04-hystrix
 * @Package: com.zmz.config
 * @ClassName: IPCheckFilter
 * @Author: 张晟睿
 * @Date: 2022/10/9 16:57
 * @Version: 1.0
 */
@Component
public class IPCheckFilter implements GlobalFilter, Ordered {

    public static final List<String> BLACK_LIST = Arrays.asList("127.0.0.1","8.120.3.58");


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {


        ServerHttpRequest request = exchange.getRequest();

        String hostString = request.getHeaders().getHost().getHostString();

        if (!BLACK_LIST.contains(hostString)) {
            return chain.filter(exchange);//放行
        }


        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().set("content-type", "application/json;charset=utf-8");

        HashMap<String, Object> result = new HashMap<>(4);
        result.put("code",601);
        result.put("msg","你是黑名单");

        ObjectMapper objectMapper = new ObjectMapper();
        byte[] bytes = new byte[0];
        try {
            bytes = objectMapper.writeValueAsBytes(result);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        DataBuffer wrap = response.bufferFactory().wrap(bytes);

        return response.writeWith(Mono.just(wrap));

//        return chain.filter(exchange);
    }

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

9.3 Token校验过滤器 TokenCheckFilter

JAVA 复制代码
package com.zmz.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

/**
 * token校验
 * @ProjectName: 04-hystrix
 * @Package: com.zmz.filter
 * @ClassName: TokenCheckFilter
 * @Author: 张晟睿
 * @Date: 2022/10/9 19:04
 * @Version: 1.0
 */
@Component
public class TokenCheckFilter implements GlobalFilter, Ordered {



    public static final List<String> ALLOW_URL = Arrays.asList("/login-service/doLogin","/myUrl","/doLogin");


    @Autowired
    public StringRedisTemplate redisTemplate;

    /**
     * 前提是? 和前端约定好 一般放在请求头里面 一般key   Authorization   value bearer token
     * 1.拿到请求url
     * 2.判断放行
     * 3.拿到请求头
     * 4.拿到token
     * 5.校验
     * 6.放行/拦截
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        if (ALLOW_URL.contains(path)) {
            return chain.filter(exchange);
        }

        HttpHeaders headers = request.getHeaders();
        List<String> authorization = headers.get("Authorization");
        if (!CollectionUtils.isEmpty(authorization)) {
            String token = authorization.get(0);
            if (StringUtils.hasText(token)) {
                //约定好的token  去掉头部的bearer
                /*
                在截取真正的token时,一定注意bearer后面有一个空格
                 */
                String realtoken = token.replaceFirst("bearer ", "");
                if (StringUtils.hasText(realtoken) && redisTemplate.hasKey(realtoken)) {
                    return chain.filter(exchange);
                }

            }

        }

        //校验失败,进行拦截处理
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().set("content-type","application/json;charset=utf-8");

        HashMap<String, Object> result = new HashMap<>(4);
        result.put("code", HttpStatus.UNAUTHORIZED.value());
        result.put("msg","未授权token");

        ObjectMapper objectMapper = new ObjectMapper();
        byte[] bytes = new byte[0];
        try {
            bytes = objectMapper.writeValueAsBytes(result);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        DataBuffer wrap = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(wrap));
    }

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

10. 限流

通俗地说,限流是一种控制用户在一段时间内访问资源次数的方法,以减轻服务器的压力。限流大致可以分为两种类型:

  1. IP限流:如果在10秒内同一个IP地址的访问次数超过3次,将限制其继续访问,需要在一段时间后才能再次访问。
  2. 请求量限流:在一段时间内(窗口期),如果请求次数达到设定的阈值,将直接拒绝后续的访问,需要在一段时间后才能继续访问。这种限流粒度可以细化到每个API(URL)或每个服务。

常用的限流算法包括漏斗算法、令牌桶算法和窗口滑动算法。此外,还有计数器算法等其他限流模型可供选择。

入不敷出

  • 在处理之前,所有的请求需要获取一个可用的令牌才能被处理。
  • 根据限流大小,按照一定的速率向令牌桶中添加令牌。
  • 令牌桶设置了最大的令牌容量限制,当令牌桶已满时,新添加的令牌将被丢弃或拒绝。
  • 当请求到达时,首先需要从令牌桶中获取令牌,只有拿到令牌后才能进行其他的业务逻辑处理。在处理完业务逻辑之后,将令牌直接删除。
  • 令牌桶还设有最低限额,当令牌桶中的令牌数量达到最低限额时,请求处理完后不会删除令牌,从而确保有足够的限流。

10.1 Gateway 结合 redis 实现请求量限流

Spring Cloud Gateway 已经内置了一个 RequestRateLimiterGatewayFilterFactory,我们 可以直接使用。 目前 RequestRateLimiterGatewayFilterFactory 的实现依赖于 Redis,所以我们还要引入 spring-boot-starter-data-redis-reactive

添加依赖

xml 复制代码
<!--限流要引入 Redis-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

修改配置文件

yml 复制代码
spring:
  application:
    name: gateway-server
  cloud:
    gateway:
      enabled: true #只要加了依赖,默认开启
      routes:
        - id: login-service-route  #路由id,保持唯一
          uri: http://localhost:8088  #  或者直接使用uri: lb://login-service 来进行动态路由  #uri 统一资源定位符   url统一资源标识符
          predicates:  #断言是给某个路由来做的  断言不能给动态路由来配置,只能在写好的predicates断言内才能生效
            - Path=/doLogin   #断言匹配规则,只要匹配上/doLogin   就往uri转发 并且将路径带上
            - After=2022-10-09T17:18:39.831+08:00[Asia/Shanghai]
            - Method=GET,POST
#            - Query=name,admin.   #正则表达式的值
#            - Path=/mySerivice/**  #多个路径进行匹配
          filters:
            - name: RequestRateLimiter  #这个是过滤器的名称
              args: #过滤器的参数
                key-resolver: '#{@ipKeyResolver}'  #通过spel表达式取IOC容器中的值
                redis-rate-limiter.replenishRate: 1 #生成令牌的速度
                redis-rate-limiter.burstCapacity: 3 #桶容量
      discovery:
        locator:
          enabled: true  #开启动态路由  开启通过业务名称找到对应服务的功能
          lower-case-service-id: true   #服务名称小写开启
#      globalcors:
#          corsConfigurations:
#              '[/**]':
#                  allowCredentials: true  # 可以携带cookie
#                  allowedHeaders: '*'
#                  allowedMethods: '*'
#                  allowedOrigins: '*'

配置文件说明

在上述配置文件中,包含了对Redis信息的配置,并配置了RequestRateLimiter的限流过滤器。该过滤器需要配置三个参数:

  • burstCapacity:令牌桶的总容量。
  • replenishRate:令牌桶每秒的平均填充速率。
  • key-resolver:用于限流的键解析器的Bean对象的名称。它使用SpEL表达式,通过@beanName从Spring容器中获取相应的Bean。

创建配置类 RequestRateLimiterConfig

java 复制代码
package com.zmz.config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;

/**
 * @ProjectName: 04-hystrix
 * @Package: com.zmz.config
 * @ClassName: RequestLimitConfig
 * @Author: 张晟睿
 * @Date: 2022/10/10 8:09
 * @Version: 1.0
 */

/**
 * 自定义限制的一个类
 */
@Configuration
public class RequestLimitConfig {
    /**
     * Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
     * 报错是IOC中存在两个bean实例,需要选择一个进行注入
     */

    //针对某个ip来进行限流 每个ip只能访问3次
    //ioc注入默认以方法名称进行注入的
    @Bean
    @Primary
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getHeaders().getHost().getHostString());
    }


    //针对某个路径来进行限流  每个路径在规定时间内只能访问3次
    //api 接口限制  新一代网关
    @Bean
    public KeyResolver apiKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getPath().value());
    }
}

10.2 跨域配置

跨域? ajax 同源策略 8080、8081,因为网关是微服务的边缘 所有的请求都要走网关 跨域的配置只需要写在网关。

java 复制代码
package com.zmz.config;

import org.springframework.context.annotation.Bean;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

/**
 * @ProjectName: 04-hystrix
 * @Package: com.zmz.filter
 * @ClassName: CorsConfig
 * @Author: 张晟睿
 * @Date: 2022/10/10 8:43
 * @Version: 1.0
 */
//跨域配置  通过bean注入 或者 通过yml配置文件进行配置
public class CorsConfig {
    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration cors = new CorsConfiguration();
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        cors.addAllowedOrigin("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**",cors);
        return new CorsWebFilter(source);
    }
}
yml 复制代码
spring:
	cloud:
    	gateway:
			globalcors:
          		corsConfigurations:
              		'[/**]':
                      allowCredentials: true  # 可以携带cookie
                      allowedHeaders: '*'
                      allowedMethods: '*'
                      allowedOrigins: '*'

11. 健康状态检查

导入依赖

xml 复制代码
<!-- 健康检查的依赖-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

添加配置文件

yml 复制代码
management:
	endpoints:
		web:
			exposure:
				include: '*' #暴露检查的端点
相关推荐
AlienZHOU4 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
狗头大军之江苏分军6 小时前
《潮水漫过十七岁》开学了
后端
苏三说技术7 小时前
如何看待GPT-6在UP主众测中碾压夺冠?它是现在最强大模型吗?
后端
mldong7 小时前
一份 JSON,一条能跑的审批流:把报销流程送上工作流引擎
后端·架构
wno7047 小时前
Spring Boot WebFlux增删改查
java·spring boot·后端
Captaincc7 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
aramae8 小时前
模拟实现strlen()函数 (C语言)
c语言·开发语言·后端
行百里er9 小时前
Spring Insight 里如何把 Span 收成一条链路
spring boot·spring cloud·监控
IT_陈寒9 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
分支预测失败10 小时前
RISC-V 时间子系统深度专题:mtime 访问路径、SBI 定时器与 Linux tickless 协同
后端