7.Gateway服务网关

293 阅读3分钟

7.1 Gateway服务网关的作用

  • 统一的入口来路由转发各个微服务
  • 安全认证、鉴权、统一拦截接口请求与响应、限流

现在比较主流的网关组件是Spring Cloud Gateway。

7.2 Spring Cloud Gateway入门使用

需求: 通过访问api网关,将请求路由转发到device设备微服务。

7.2.1 创建smart-gateway网关工程并添加依赖

添加Spring Cloud Gateway网关依赖、Nacos依赖

  <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>

7.2.2 创建启动类并添加注解以启动Nacos客户端

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

7.2.3 添加配置

主要配置:

  • Nacos地址,这样Gateway网关可以从Nacos中获取微服务

  • path路径转发到微服务的路由

server:
  port: 8000
spring:
  application:
    name: smart-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.56.200:8848
    gateway:
      discovery:
        locator:
          enabled: true # gateway发现nacos中的微服务
      routes:
        - id: device_route
          uri: lb://smart-device # 从nacos中获取微服务,并遵循负载均衡策略
          predicates:
            - Path=/device-serv/**
          filters:
            - StripPrefix=1

最后启动gateway网关服务和device设备服务,访问api地址:http://127.0.0.1:8000/device-serv/device/get/a1

网关路由转发到device服务

7.3 全局过滤器

Spring Cloud Gateway中的全局过滤器,常用来统一对请求进行token认证。下面简单的例子说明下。

全局过滤器具体的处理逻辑:对请求的token进行认证,如果认证成功则转发到下游服务;认证失败,则直接返回异常响应。

/**
 * @description: token认证过滤器
 * @author:xg
 * @date: 2025/1/5
 * @Copyright:
 */
@Component
@Slf4j
public class TokenAuthGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getHeaders().getFirst("token");
        if (StringUtils.isBlank(token)) {
            log.info("认证失败");
            // 认证失败,返回异常响应
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        // 调用chain.filter继续转发到下游服务
        return chain.filter(exchange);
    }

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

header头中没有token认证失败:

header头中没有token认证失败

token认证成功:

当然这个只是非常粗糙的对token认证,实际项目中一般会对token的有效性、时效性等进行认证,这个根据实际的业务来实现。

7.4 网关限流

所有请求都会经过网关,因此可以利用Sentinel组件在网关处对请求进行限流。

7.4.1 网关工程引入Sentinel限流依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>

7.4.2 配置过滤器以及限流规则

/**
 * @description: 限流配置
 * @author:xg
 * @date: 2025/1/5
 * @Copyright:
 */
@Configuration
public class FlowConfiguration {

    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

    public FlowConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                             ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /**
     * 初始化限流的过滤器
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /**
     * 初始化限流参数
     */
    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(
                new GatewayFlowRule("device_route") // 资源名称,对应路由id
                        .setCount(1) // 限流阈值
                        .setIntervalSec(1) // 统计时间窗口,默认是1秒
        );
        GatewayRuleManager.loadRules(rules);
    }

    /**
     * 配置限流的异常处理器
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    /**
     * 自定义限流异常页面
     */
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap<>();
                map.put("code", 0);
                map.put("message", "接口被限流");
                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON_UTF8).
                        body(BodyInserters.fromObject(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

最后,我们在1秒之内,多次访问接口:http://127.0.0.1:8000/device-serv/device/get/a1

1秒内多次调用被限流

我们看到接口被限流了。

7.4.3 针对api限流

在上面的配置中,添加针对具体的api限流,控制粒度更加的精确。

   /**
     * 初始化的限流参数
     */
    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("device_api1").setCount(1).setIntervalSec(1));
        rules.add(new GatewayFlowRule("device_api2").setCount(1).setIntervalSec(1));
        GatewayRuleManager.loadRules(rules);
    }

    /**
     * 自定义API分组
     */
    @PostConstruct
    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition("device_api1")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    // 以/device-serv/device/get 开头的请求
                    add(new ApiPathPredicateItem().setPattern("/device-serv/device/get/**").
                            setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        ApiDefinition api2 = new ApiDefinition("device_api2")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    // 以/device-serv/device/hello 完整的url路径匹配
                    add(new ApiPathPredicateItem().setPattern("/device-serv/device/hello"));
                }});
        definitions.add(api1);
        definitions.add(api2);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

我们看到,对于/device/hello接口,在1秒内访问多次被限流了。

api接口被限流