Gateway网关的实现

API网关

  • 网关路由必须支持负载均衡,服务列表是从注册中心拉取的
  • 客户端发出请求的URL指向的是网关,URL还必须要包含目标信息
  • 网关收到URL,通过一定的规则,要能识别出交给哪个实例去处理
  • 网关有能力对请求响应进行修改

引入依赖包

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
</dependencies>

配置启动类

@SpringBootApplication
@EnableDiscoveryClient
public class GateWayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GateWayApplication.class, args);
    }
}

编写application.yaml的配置文件

server:
  port: 8341
Spring:
  application:
    name: gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.200.130:8848
        namespace: ce6fa22e-fc35-4fd3-9568-2cf609059088
        group: JIAGOU_GROUP
        file-extension: yaml
        prefix: ${Spring.application.name}
        heart-beat-interval: 5
    gateway:
      discovery:
        locator:
          enabled: true #启用DiscoveryClient的标志
      routes:
        - id: course-service-route # 路由的唯一标识 http://localhost:8341/course/api/courses/1
          uri: lb://course-agg-service #网关收到请求后将请求转发到 lb://course-agg-service
          predicates:
            - Path=/course/** # 断言匹配请求路径
          filters: # 过滤器,可以做认证、限流、日志等
            - StripPrefix=1 # 针对URL剪切前缀,这里去掉了前缀/course

主要概念

  • routes:路由定义了匹配请求的条件,以及如何将请求转发到下游服务
  • predicates:用于匹配请求的条件,比如请求路径、请求参数、头部信息等。
  • filter:过滤器,进行模式匹配

路由谓词工厂

- After=2017-01-20T17:42:47.789-07:00[America/Denver] # 在某个时间之后
- Before=2017-01-20T17:42:47.789-07:00[America/Denver] #在某个时间之前
 - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver] # 两者之间
 - Cookie=chocolate, ch.p #cookie谓词工厂,此谓词匹配具有给定名称且其值与正则表达式匹配的 Cookie。
 - Header=X-Request-Id, \d+
 - Host=**.somehost.org,**.anotherhost.org
 - Method=GET,POST
  - Path=/red/{segment},/blue/{segment}
  - Query=green
  - RemoteAddr=192.168.1.1/24

Filter工厂

  • AddRequestHeader:向请求添加一个 HTTP 头。
  • AddResponseHeader:向响应添加一个HTTP 头。
  • DedupeResponseHeader:去除响应头中的重复值。
  • PrefixPath:为请求路径添加前缀。
  • PreserveHostHeader:保留原始请求的 Host 头。
  • RedirectTo:重定向请求到另一个 URL。章
  • RemoveRequestHeader:移除请求中的一个 HTTP 头。
  • RemoveResponseHeader:移除响应中的一个HTTP 头。
  • RewritePath:重写请求路径。
  • SetPath:设置请求路径。
  • SetRequestHeader:设置请求头。
  • SetResponseHeader:设置响应头。
  • SetStatus:设置响应状态码。
  • StripPrefix:去除请求路径中的前缀

1网关全局过滤器

@Component
@Order(1)
@Slf4j
public class LoggingGlobalFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        long startTime = System.currentTimeMillis();
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            //Mono.fromRunnable此时所有的请求都处理完成后才执行
            ServerHttpResponse response = exchange.getResponse();
            long endTime = System.currentTimeMillis();
            log.info("请求地址:{},请求方法:{},响应状态码:{},响应时间:{}",request.getURI()
                    ,request.getMethod()
                    ,response.getStatusCode().value(),endTime-startTime);
        }));
    }
}

2实现认证过滤器

在微服务中,服务数量较多,不能再服务中进行认证,会进行统一的认证,认证成功后颁发Token,前端会将令牌放在RequestHeader中

创建一个对所有的请求进行认证的全局过滤器

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class AuthenticationGlobalFilter implements GlobalFilter {

    // 认证头
    private  static final String AUTHENTICATION_HEADER = "Authorization";

    // 登录URL
    private static final String LOGIN_URL = "/api/login";
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if(exchange.getRequest().getPath().toString().contains(LOGIN_URL)){
            return chain.filter(exchange);
        }
        String token = exchange.getRequest().getHeaders().getFirst(AUTHENTICATION_HEADER);
        if(token == null || IsValid(token)){
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            // 请求结束
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    private boolean IsValid(String token) {
        //此处通过工具类校验
        return true;
    }
}

3网关的限流

此处集成了redis

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

自定义一个Config类:但要注意此处的限流策略只能选择一种,也就是说@Bean只能生效一个方法

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

@Configuration
public class RateLimiterConfiguration {
    /**
     * 使用ip地址作为限流
     * @return
     */
    @Bean
    public KeyResolver ipKeyResolver(){
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }

    /**
     * 使用用户id作为限流,需要在请求中携带userId参数
     * @return
     */
    //@Bean
    public KeyResolver userKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
    }

    /**
     * 返回一个KeyResolver对象,用于解析请求的路径
     *
     * @return KeyResolver路径解析器,通过请求对象获取路径值
     */
    //@Bean
    public KeyResolver pathResolver() {
        // Lambda表达式用于创建KeyResolver的实例
        // Mono.just确保返回一个包含请求路径的Mono Flux
        return exchange -> Mono.just(exchange.getRequest().getPath().value());
    }
}

在application.yaml文件中修改策略配置

server:
  port: 8341
Spring:
  application:
    name: gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.200.130:8848
        namespace: ce6fa22e-fc35-4fd3-9568-2cf609059088
        group: JIAGOU_GROUP
        file-extension: yaml
        prefix: ${Spring.application.name}
        heart-beat-interval: 5
    gateway:
      discovery:
        locator:
          enabled: true #启用DiscoveryClient的标志
      routes:
        - id: course-service-route # 路由的唯一标识 http://localhost:8341/course/api/courses/1
          uri: lb://course-agg-service #网关收到请求后将请求转发到 lb://course-agg-service
          predicates:
            - Path=/course/** # 断言匹配请求路径
          filters: # 过滤器,可以做认证、限流、日志等
            - StripPrefix=1 # 针对URL剪切前缀,这里去掉了前缀/course
            - name: RequestRateLimiter # 定义限流策略
              args:
                redis-rate-limiter.replenishRate: 1 # 每秒填充的令牌数
                redis-rate-limiter.burstCapacity: 2 # 令牌桶的容量
                key-resolver: "#{@ipKeyResolver}" # 限流key的生成策略
  redis:
    host: 43.143.110.136
    port: 6379
    database: 0
    pool:
      max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
      max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
      max-idle: 8 # 连接池中的最大空闲连接
    password: 1234

当请求数量大于令牌个数时,触发限流策略,状态码显示429
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值