网关Gateway

2024-01-07 17:57:58

什么是网关?

????????网关实质上是一个网络通向其他网络的 IP 地址,是当前微服务项目的"统一入口"。

网关能做什么?

????????反向代理 、鉴权、 流量控制、 熔断、?日志监控等

图片原文:http://t.csdnimg.cn/SvUJh

核心概念

Router(路由):路由时构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。

Predicate(断言):断言说简单点,就是请求匹配条件。断言是定义匹配条件,如果请求符合条件,则该请求匹配断言所属的路由。

Filter( 过滤): 指的是 Spring 框架中 GatewayFilter 的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。

Spring Cloud Gateway 网关的搭建:

创建 springboot 项目,引入 jar
        <!--网关jar-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>${spring-cloud-alibaba-version}</version>
        </dependency>
        <!--引入nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>${spring-cloud-alibaba-version}</version>
        </dependency>
    <!--注意我这里没有引入父工程,而是springboot父启动依赖,也不要引入web依赖而是webflux-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
Spring Cloud GateWay是基于 webflux 的,如果非要 web 支持的话需要导入spring-boot-starter-webflux而不是spring-boot-start-web
应用启动类添加注解 @EnableDiscoveryClient
编写application.yml
server:
    port: 8070
spring:
    application:
        name: alibaba-gateway
cloud:
    nacos:
        discovery:
            server-addr: 127.0.0.1:3333 #注册服务到nacos,注意nacos的端口号配置
    gateway:
        discovery:
            locator:
                enabled: true #表明gateway开启服务注册和发现的功能,gateway为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务。
                lower-case-service-id: true #请求路径的服务g名改为小写
启动项目,输入路径 ip+GateWay 端口号 + 要请求的服务名 + 地址
如: http://127.0.0.1:8070/alibaba-user-consumer-8090/user/findUserById?id=3

自定义路由

修改yml文件信息 :
server:
    port: 8070
spring:
    application:
        name: alibaba-gateway
cloud:
    nacos:
        discovery:
            server-addr: 127.0.0.1:3333 #注册服务到nacos,注意nacos的端口号配置
    gateway:
        discovery:
            locator:
                enabled: false #表明gateway开启服务注册和发现的功能,gateway为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务。
                lower-case-service-id: true #请求路径的服务g名改为小写
        routes:    l
        - id: user-consumer #唯一 路由id
          uri: http://127.0.0.1:7780 #使用http:IP+服务端口 或lb://alibaba-user-consumer-8090 也可以,lb为负载均衡
          predicates: # 断言
            - Path=/ts/** # 路径需携带ts
          filters:
            - StripPrefix=1 #路由去掉前缀信息 使用内置过滤器
路由的主要四个配置:
  • 路由idid
  • 路由目标(uri
  • 路由断言(predicates):判断路由的规则,
  • 路由过滤器(filters):对请求或响应做处理
再次启动项目 输入路径:ip+GateWay 端口号+ts+ 路径
如: http://127.0.0.1:8070/ts/user/findUserById?id=3

路由过滤器

客户端请求先找到路由,路由匹配时经过过滤器层层筛选,最终访问到微服务。
?

请求头过滤器配置示例(局部过滤器)

在yml中配置相关信息:
????????
给所有进入 user-consumer 的请求添加一个请求头。
请求头的 key token value test。
默认过滤器配置示例(全局过滤器)
default - filters 的配置和 routes 平级。
只要配置在 default - filters 下面的过滤器,会对 routes 配置的所有路由都生效。
自定义全局路由过滤器
????????

有时候SpringCloudGateWay提供的过滤器工厂不能满足自己的要求。?,可能有时候需要在过滤时做一些其它的逻辑操作。 那么这时候可以选择使用Java代码自定义全局过滤器

yml配置如图:

创建 GateWayFilter 实现接口 GlobalFilter Ordered:
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @Author 
 * @Date 2023/12/25 11:11
 * @Description:
 */
//@Component
//@Order(1)
public class GateWayFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token=exchange.getRequest().getHeaders().getFirst("token");
        System.out.println("token1====="+token);
        if ("123".equals(token)){
            return chain.filter(exchange);
        }
        System.out.println("错误,不放行");
        exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        return exchange.getResponse().setComplete();
    }
}

这里请求头携带的token为123时才允许放行。

当有多个过滤器时,Order的值决定了过滤器的执行顺序。

数值越大优先级越低, 负的越多, 优先级越高。

设置 Order 的值有两种方式:
1. 实现 Ordered 接口,并且重写 getOrder 方法
2. 使用@Order注解

如:GateWayFilter1
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @Author 
 * @Date 2023/12/25 11:31
 * @Description:
 */
//@Component
public class GateWayFilter1  implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        return null;
    }

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

网关的跨域问题
使用 CORS 方式。
CORS是一个 W3C 标准,全称是 " 跨域资源共享 " Cross-origin resource sharing )。
它允许浏览器向跨源服务器,发出XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制。
方式一:配置 application.yml 文件:
spring:
    cloud:
        gateway:
            globalcors: # 全局的跨域配置
                add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
                    # options请求 就是一种询问服务器是否浏览器可以跨域的请求
                    # 如果每次跨域都有询问服务器是否浏览器可以跨域对性能也是损耗
                    # 可以配置本次跨域检测的有效期maxAge
                    # 在maxAge设置的时间范围内,不去询问,统统允许跨域
                corsConfigurations:
                 '[/**]':
                    allowedOrigins: # 允许哪些网站的跨域请求
                        - "http://localhost:8090"
                    allowedMethods: # 允许的跨域ajax的请求方式
                        - "GET"
                        - "POST"
                        - "DELETE"
                        - "PUT"
                        - "OPTIONS"
                    allowedHeaders: "*" # 允许在请求中携带的头信息
                    allowCredentials: true # 允许在请求中携带cookie
                    maxAge: 360000 # 本次跨域检测的有效期(单位毫秒)
                        # 有效期内,跨域请求不会一直发option请求去增大服务器压力

方式二:使用编码方式定义配置类

@Configuration
public class CorsConfig {
    private static final String MAX_AGE = "18000L";
    @Bean
    public WebFilter corsFilter() {
        return (ServerWebExchange ctx, WebFilterChain chain) -> {
            ServerHttpRequest request = ctx.getRequest();
            // 使用SpringMvc自带的跨域检测工具类判断当前请求是否跨域
            if (!CorsUtils.isCorsRequest(request)) {
                return chain.filter(ctx);
            }
            HttpHeaders requestHeaders = request.getHeaders();
            // 获取请求头
            ServerHttpResponse response = ctx.getResponse();
            // 获取响应对象
            HttpMethod requestMethod =
                    requestHeaders.getAccessControlRequestMethod(); // 获取请求方式对象
            HttpHeaders headers = response.getHeaders();
            // 获取响应头
            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN,
                    requestHeaders.getOrigin()); // 把请求头中的请求源(协议+ip+端口)添加到响应头中(相当于yml中的allowedOrigins)
            headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,
                    requestHeaders.getAccessControlRequestHeaders());
            if (requestMethod != null) {
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS,
                        requestMethod.name()); // 允许被响应的方法(GET/POST等,相当于yml中的allowedMethods)
            }
            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
            // 允许在请求中携带cookie(相当于yml中的allowCredentials)
            headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
            // 允许在请求中携带的头信息(相当于yml中的allowedHeaders)
            headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
            // 本次跨域检测的有效期(单位毫秒,相当于yml中的maxAge)
            if (request.getMethod() == HttpMethod.OPTIONS) {
                // 直接给option请求反回结果
                response.setStatusCode(HttpStatus.OK);
                return Mono.empty();
            }
            return chain.filter(ctx);
            // 不是option请求则放行
        };
    }
}

网关限流

????????网关可以做很多的事情,比如,限流,当我们的系统被频繁的请求的时候,就有可能将系统压垮,所以为了解决这个问题,需要在每一个微服务中做限流操作,但是如果有了网关,那么就可以在网关系统做限流,因为所有的请求都需要先通过网关系统才能路由到微服务中。
令牌桶算法是比较常见的限流算法之一,大概描述如下:
  • 所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
  • 根据限流大小,设置按照一定的速率往桶里添加令牌;
  • 桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
  • 请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;
  • 令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流
网关限流代码实现
需求:每个 ip 地址 1 秒内只能发送 1 次请求,多出来的请求返回 429 错误。
spring cloud gateway 默认使用 redis RateLimter 限流算法来实现。所以我们要使用首先需要引
redis 的依赖
jar包引入:
       <!--redis 限流-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>
定义 KeyResolver
@Configuration
public class KeyResolverConfiguration {

    @Bean
    public KeyResolver ipKeyResolver(){
        return new KeyResolver() {
            @Override
            public Mono<String> resolve(ServerWebExchange exchange) {
                return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
            }
        };
    }
}

修改yml中的配置项,指定限制流量的配置以及redis的配置

      routes:
      - id: cloud-user-consumer
        uri: http://127.0.0.1:7780
        predicates: # 断言
          - Path=/ts/** # 路径需携带ts
        filters:
          - StripPrefix=1 #路由去掉前缀信息 使用内置过滤器
          - AddRequestHeader=token,123 #添加请求头
          - name: RequestRateLimiter #请求数限流 名字不能随便写
            args:
              key-resolver: "#{@ipKeyResolver}" #用于限流的键的解析器的 Bean 对象的名字。它使用SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象
              redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充平均速率
              redis-rate-limiter.burstCapacity: 1 #令牌桶总容量。

  • burstCapacity :令牌桶总容量。
  • replenishRate :令牌桶每秒填充平均速率。
  • key - resolver 用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据 #{@beanName} Spring 容器中获取 Bean 对象。

文章来源:https://blog.csdn.net/qq_44114187/article/details/135439046
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。