微服务架构➖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: '*' #暴露检查的端点
相关推荐
追逐时光者1 小时前
免费、简单、直观的数据库设计工具和 SQL 生成器
后端·mysql
初晴~2 小时前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·
盖世英雄酱581362 小时前
InnoDB 的页分裂和页合并
数据库·后端
小_太_阳2 小时前
Scala_【2】变量和数据类型
开发语言·后端·scala·intellij-idea
直裾2 小时前
scala借阅图书保存记录(三)
开发语言·后端·scala
星就前端叭3 小时前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
小林coding4 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
AI理性派思考者4 小时前
【保姆教程】手把手教你在Linux系统搭建早期alpha项目cysic的验证者&证明者
后端·github·gpu
从善若水4 小时前
【2024】Merry Christmas!一起用Rust绘制一颗圣诞树吧
开发语言·后端·rust