Spring Cloud GateWay网关基本使用

一、GateWay的介绍

1、为什么要使用网关

在springcloud有多个微服务,首先要考虑的是不是如果不配置网关各个服务的端口都暴露在外?还有过滤器和鉴权功能是不是在每个服务都配置一遍?所以网关主要作用是作为项目的统一入口

2、GateWay的特性

  • Spring WebFlux 基于 Reactor响应式框架,基于Netty通讯框架是NIO(同步非阻塞式IO)。
  • Gateway 提供了统一的路由方式(反向代理)
  • 为微服务架构提供种简单有效的统的API路由管理式,提供了Spring Cloud Discovery Client(如Eureka、Nacos)、Ribbon、Hystrix等组件的集成方案

3、核心内容

  • 路由(Route):一个 Route 由路由 ID,转发目标URI,多个断言(Predicates) 以及多个过滤器 (Filters )构成。Gateway 上可以配置多个 Routes。处理请求时会按优先级排序,找到第一个满足所有 Predicates 的 Route;
  • 断言(Predicate):SpringCloud Gateway 中内置了许多断言工厂,通过断言匹配http请求中的任何内容(请求头、请求参数等),如果匹配成功,则匹配断言所在路由。
  • 过滤器(Filter):在请求前后执行业务逻辑,可以设置局部过滤全局过滤。比如鉴权、日志监控、流量控制、修改请求头、修改响应等。

常用的局部过滤器说明:

过滤器前缀作用参数
StripPrefix用于截断原始请求的路径使用数字表示要截断的路径数量
AddRequestHeader为原始请求添加 HeaderHeader 的名称及值
AddRequestParameter为原始请求添加请求参数参数名称及值
Retry针对不同的响应进行重试reties、statuses、methods、series
RequestSize设置允许接收最大请求包的大小请求包大小,单位字节,默认5M
SetPath修改原始请求的路径修改后的路径
RewritePath重写原始的请求路径原始路径正则表达式以及重写后路径的正则表达式
PrefixPath为原始请求路径添加前缀前缀路径
RequestRateLimiter对请求限流,限流算法为令牌桶KeyResolver、reteLimiter、statusCode、denyEmptyKe

常用的全局过滤器

过滤器名称作用
ForwardPathFilter / ForwardRoutingFilter路径转发相关过滤器
LoadBalanceerClientFilter负载均衡客户端相关过滤器
NettyRoutingFilter / NettyWriteResponseFilterHttp 客户端相关过滤器
RouteToRequestUrlFilter路由 URL 相关过滤器
WebClientHttpRoutingFilter / WebClientWriteResponseFilter请求 WebClient 客户端转发请求真实的URL并将响应写入到当前的请求响应中
WebsocketRoutingFilterwebsocket 相关过滤器

4、核心流程图

三、简单网关搭建

我的环境是springcloud.alibaba.2.2.6.RELEASE,以nacos为注册中心

要测试网关所以需要至少需要两个服务一个网关server、一个业务服务nacos-order,当然在企业级项目中还会有很多微服务,比如oauth服务端等

1、 创建业务服务order-server 端口配置8082

pom依赖

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

<!-- 服务注册/发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

 创建简单的OrderController用于测试web请求

@RestController
@RequestMapping("order")
public class OrderController {
    private static final Logger logger = LoggerFactory.getLogger(OrderController.class);

    @RequestMapping("/info")
    public String info() {
        logger.info("/order/info==========================》");
        return "order no 100";
    }
}

 2、 创建GateWay网关服务 端口8099

pom依赖

<!--    网关    -->
        <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>

yml配置

server:
  port: 8099
  servlet:
    context-path: /

spring:
  cloud:
    gateway:
      #default-filters:我们可以方便的使用default-filters,在请求中加入一个自定义的header,我们加入一个KV为gateway-env:springcloud-gateway,来注明我们这个请求经过了此网关。这样做的好处是后续服务端也能够看到。
      default-filters:
        - AddRequestHeader=gateway-env, springcloud-gateway
 # 配置路由,可以配置多个
      routes:
        - id: "order-server" # id 自定义路由的id
          uri: "http://127.0.0.1:8082" # uri就是 目标服务地址
          predicates:# 断言,也就是路由条件 ,这里使用了path作为路由条件
            - Path=/order-server/**  #配合StripPrefix使用 如果不截取会造成转发路径多个/order http://localhost:8099/order/order/get
          filters:
            - PreserveHostHeader # 防止host被修改为localhost
            - StripPrefix=1 #过滤器StripPrefix,作用是去掉请求路径的最前面n个部分截取掉。
            #当上游的请求,进入了Hystrix熔断降级机制时,就会调用fallbackUri配置的降级地址
            - name: Hystrix
              args:
                name: fallbackCmdOrder
                fallbackUri: forward:/fallbackOrder
#            配置了CustomRequestRateLimiter的限流过滤器,该过滤器需要配置三个参数:如果无需处理限流响应可使用RequestRateLimiter 返回429
            - name: CustomRequestRateLimiter
              args:
                  #具体配置ResolverConfig 用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
                  key-resolver: '#{@apiKeyResolver}'
                  #r令牌桶每秒填充平均速率。
                  redis-rate-limiter.replenishRate: 1
                  #令牌桶总容量。
                  redis-rate-limiter.burstCapacity: 3
      #网关统一配置跨域请求
      globalcors:
        cors-configurations:
          '[/**]':
            allowed-origins: "*"
            allowed-headers: "*"
            allow-credentials: true
            allowed-methods:
                - GET
                - POST
                - DELETE
                - PUT
                - OPTION

3、测试路由(routes)

yml相关配置如下

        1)启动nacos-order和gateway-server

        2)访问:http://localhost:8099/order-server/order/info

         注意的是order-server为断言(predicates)条件,断言都返回真,才会真正执行预设定的路由

        3)成功返回

4、测试Hystrix降级

yml相关配置

1)GateWay网关服务创建降级处理类FallbackController

在多服务情况下可以根据实际情况配置fallbackUri: forward:/fallbackOrder

package com.na.fallback;

import com.na.base.BaseResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author kele
 * <p>
 * 网关统一降级
 */

@RestController
public class FallbackController {

    /**
     * 如果想针对mothed方式进行降级返回 可将@RequestMapping改成对应的post、get等
     * @return
     */
    @RequestMapping("/fallbackOrder")
    public BaseResponse fallbackOrder() {
        BaseResponse response = new BaseResponse();
        response.setCode(100);
        response.setMessage("服务暂时不可用");
        return response;
    }
}

2)启动gateway-server,关闭nacos-order

3) 可以看到返回的信息,证明降级接口/fallbackOrder成功触发了

 5、测试限流

限流使用是的令牌桶算法方便测试限流,令牌生成速率及总量设置得相对较低,生产环境当然要根据实际情况设置

yml相关配置

1)创建CustomRequestRateLimiterGatewayFilterFactory处理类,主要是为了被限流的友好的返回

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * RequestRateLimiter限流返回429
 * 重写apply()友好返回
 */
@Slf4j
@Component
public class CustomRequestRateLimiterGatewayFilterFactory extends RequestRateLimiterGatewayFilterFactory {

    private final RateLimiter defaultRateLimiter;

    private final KeyResolver defaultKeyResolver;

    public CustomRequestRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter, KeyResolver defaultKeyResolver) {
        super(defaultRateLimiter, defaultKeyResolver);
        this.defaultRateLimiter = defaultRateLimiter;
        this.defaultKeyResolver = defaultKeyResolver;
    }

    @Override
    public GatewayFilter apply(Config config) {
        KeyResolver resolver = getOrDefault(config.getKeyResolver(), defaultKeyResolver);
        RateLimiter<Object> limiter = getOrDefault(config.getRateLimiter(), defaultRateLimiter);
        return (exchange, chain) -> resolver.resolve(exchange).flatMap(key -> {
//            if (EMPTY_KEY.equals(key)) {
//                if (denyEmpty) {
//                    setResponseStatus(exchange, emptyKeyStatus);
//                    return exchange.getResponse().setComplete();
//                }
//                return chain.filter(exchange);
//            }
            String routeId = config.getRouteId();
            if (routeId == null) {
                Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
                routeId = route.getId();
            }

            String finalRouteId = routeId;
            return limiter.isAllowed(routeId, key).flatMap(response -> {

                for (Map.Entry<String, String> header : response.getHeaders().entrySet()) {
                    exchange.getResponse().getHeaders().add(header.getKey(), header.getValue());
                }

                if (response.isAllowed()) {
                    return chain.filter(exchange);
                }

                log.warn("已限流: {}", finalRouteId);
                ServerHttpResponse httpResponse = exchange.getResponse();
                httpResponse.setStatusCode(config.getStatusCode());
                if (!httpResponse.getHeaders().containsKey("Content-Type")) {
                    httpResponse.getHeaders().add("Content-Type", "application/json");
                }
                DataBuffer buffer = httpResponse.bufferFactory().wrap("{'msg':'访问已受限制,请稍候重试'}".getBytes(StandardCharsets.UTF_8));
                return httpResponse.writeWith(Mono.just(buffer));

                // return exchange.getResponse().setComplete();
            });
        });
    }

    private <T> T getOrDefault(T configValue, T defaultValue) {
        return (configValue != null) ? configValue : defaultValue;
    }
}

2)创建限流策略配置类ResolverConfig 

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;

/**
 * @Description
 * @Author kele
 * @Data 2023/8/5 16:37
 * 限流策略,只能注入一个 KeyResolver Bean
 */
@Configuration
public class ResolverConfig {

    /**
     * 这里根据用户ID限流,请求路径中必须携带userId参数
     */
//    @Bean
//    KeyResolver userKeyResolver() {
//        return new KeyResolver() {
//            @Override
//            public Mono<String> resolve(ServerWebExchange exchange) {
//                Mono mono;
//                try {
//                    mono = Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
//                } catch (NullPointerException e) {
//                    throw new RRException("请求路径中必须携带user参数");
//                }
//                return mono;
//            }
//        };
//    }

    /**
     * 如果需要根据IP限流 则开启注释
     */
//    @Bean
//    public KeyResolver ipKeyResolver() {
//        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
//    }

    /**
     * 如果需要根据接口的URI进行限流,则开启注释
     */
    @Bean
    KeyResolver apiKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getPath().value());
    }

}

3)重启nacos-order和gateway-server

4)短时间内进行多次访问,限流也被成功触发了

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值