Spring Cloud Gateway网关整合


介绍

Spring Cloud Gateway为Spring生态系统上的一个API网关组件,主要提供一种简单而有效的方式路由映射到指定的API,并为他们提供安全性、监控和限流等等。

创建项目

创建一个gmaya-gateway项目。

修改pom文件

       <!--gateway网关,内置webflux 依赖-->
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-gateway</artifactId>
       </dependency>
       <!--eureka客户端-->
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
       </dependency>
       <!--hystrix容错降级-->
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
       </dependency>
       <!--健康监控-->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-actuator</artifactId>
       </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>

启动类修改

@SpringBootApplication
@EnableDiscoveryClient

配置文件修改

server:
  port: 9200

spring:
  application:
    name: gmaya-geteway
  cloud:
    gateway:
      discovery:
        locator:
          # 开启服务注册和发现
          # 如果为true,访问路径有两个:
          # 1.ip:9200/gmaya-service-admin/user/test
          # 2.ip:9200/admin/user/test  (这个是Path自己定义的/admin/**)
          # 如果为false,访问路径有一个ip:9200/admin/user/test
          enabled: false
          # 服务名配置为小写
          lower-case-service-id: true
      routes:
        # 系统服务
        - id: gmaya-service-admin # 不重复即可
          uri: lb://gmaya-service-admin # 需要转发到的服务名称
          predicates:
            # 以 /admin/开头的路径全部转发到lb://gmaya-service-admin的服务上,此时gmaya-service-admin可以负载均衡
            - Path=/admin/**
          filters:
            # 去掉前面1个前缀,也就是真正转发访问的时候不带/admin/
            - StripPrefix=1
# 注册服务中心
eureka:
  instance:
    # 心跳时间,即服务续约间隔时间(缺省为30s)
    lease-renewal-interval-in-seconds: 5
    # 发呆时间,即服务续约到期时间(缺省为90s)
    lease-expiration-duration-in-seconds: 10
  client:
    service-url:
      defaultZone: http://localhost:8000/eureka/
    healthcheck:
      enabled: true # 开启健康检查
    # 表示eureka client间隔多久去拉取服务注册信息,默认为30秒
    registry-fetch-interval-seconds: 5
# 监控
management:
  endpoints:
    web:
      exposure:
        # 通过HTTP公开所有的端点, 默认是info,health
        include: '*'
  endpoint:
    health:
      # 显示完整信息,#默认是never(简要信息)
      show-details: always

简单测试

注册中心:gmaya-service-center8000
系统服务:gmaya-service-admin9001
网关服务:gmaya-gateway9200

启动三个项目,访问:

http://localhost:9200/admin/user/test

没问题!

测一下gateway 的负载均衡,现在再把系统服务启动一个9011端口。

-Dserver.port=9011

启动之后查看效果

此时已经有了一个服务名一样,但是端口不一样的两个服务了。
再次访问接口。
查看后端打印情况,确实是轮询方式实现了负载均衡。

添加hystrix熔断降级

在分布式系统中,网关作为流量的入口,因此会有大量的请求进入网关,向其他服务发起调用,其他服务不可避免的会出现调用失败(超时、异常),失败时不能让请求堆积在网关上,需要快速失败并返回给客户端,想要实现这个要求,就必须在网关上做熔断、降级操作。

有两种方式进行熔断、降级

webflux方式

添加HystrixFallbackHandler

package top.gmaya.gmayagateway.handler;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR;

/**
 * 熔断、降级类
 * @author GMaya
 * @dateTime 2020/5/13 16:19
 */
@Component
@Slf4j
public class HystrixFallbackHandler implements HandlerFunction<ServerResponse>
{
    @Override
    public Mono<ServerResponse> handle(ServerRequest serverRequest)
    {
        Optional<Object> originalUris = serverRequest.attribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
        originalUris.ifPresent(originalUri -> log.error("网关执行请求:{}失败,hystrix服务降级处理", originalUri));
        // 可以根据自己的工具类返回统一格式
        Map<String,String> map = new HashMap();
        map.put("code","500");
        map.put("msg","服务出现异常,降级操作。");
        return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR.value()).contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(JSON.toJSONString(map)));
    }
}

添加GatewayRoutesConfig

package top.gmaya.gmayagateway.config;

import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import top.gmaya.gmayagateway.handler.HystrixFallbackHandler;

/**
 * 路由配置信息
 * @author GMaya
 * @dateTime 2020/5/13 16:19
 */
@Configuration
// 全参构造方法
@AllArgsConstructor
public class GatewayRoutesConfig {

    private final HystrixFallbackHandler hystrixFallbackHandler;

    @Bean
    public RouterFunction<?> routerFunction(){
        return RouterFunctions.route(RequestPredicates.path("/defaultFallback").and(RequestPredicates.accept(
                MediaType.TEXT_PLAIN)),hystrixFallbackHandler);
    }
}

修改配置文件

大部分都省略了,和上面的一样。

spring:
  cloud:
    gateway:
      routes:
        # 系统服务
        - id: gmaya-service-admin # 不重复即可
          filters:
            # 去掉前面1个前缀,也就是真正转发访问的时候不带/admin/
            - StripPrefix=1
            # 降级配置
            - name: Hystrix
              args:
                name: default
                fallbackUri: 'forward:/defaultFallback'

hystrix:
  command:
    default:  #default全局有效
      execution:
        timeout:
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 3000 #断路器超时时间,默认1000ms

测试

系统服务接口中添加延迟,模拟超时

此时访问,3秒后返回设置好的熔断信息

或者将系统服务直接关闭。


web请求方式

这种方式和正常controller中方法一样

新建DefaultFallbackController

package top.gmaya.gmayagateway.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/**
 * web方式进行熔断降级
 * @author GMaya
 * @dateTime 2020/5/13 17:05
 */
@RestController
@Slf4j
public class DefaultFallbackController {

    @GetMapping("defaultFallback")
    public Map<String,String> defaultFallback(){

        // 可以根据自己的工具类返回统一格式
        Map<String,String> map = new HashMap();
        map.put("code","500");
        map.put("msg","服务出现异常,降级操作。2");
        log.error("网关执行请求失败,web方式记录");
        return map;
    }

}

将第一种方式配置先去掉,重启项目

测试

浏览器访问


注意: 如果两个都开启了, 就会默认使用第一种方式。

添加限流

在高并发的系统中,往往需要在系统中做限流,一方面是为了防止大量的请求使服务器过载,导致服务不可用,另一方面是为了防止网络攻击。

常见的限流纬度有比如通过Ip来限流、通过uri来限流、通过用户访问频次来限流。

采用springcloud gateway 为我们提供了限流过滤器RequestRateLimiterGatewayFilterFactory。使用Redislua脚本实现了令牌桶的方式限流。

修改pom

引入

       <!-- redis配置 -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
       </dependency>
       <!--使用redis的lettuce连接池使用到,如果不用可不加-->
       <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-pool2</artifactId>
       </dependency>    

添加配置类

创建KeyResolverConfig

package top.gmaya.gmayagateway.config;

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;

/**
 * 路由限流配置
 * 配置文件中使用的那种限流 就是真正的限流方式,三种任选其一,不能同时存在
 * @author GMaya
 * @dateTime 2020/5/14 9:19
 */
@Configuration
public class KeyResolverConfig {

    /**
     * 根据ip限流
     * @return
     */
    @Bean(value = "hostAddrKeyResolver")
    public KeyResolver hostAddrKeyResolver(){
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }
    /**
     * 根据路径限流
     * @return
     */
   /* @Bean(value = "uriKeyResolver")
    public KeyResolver uriKeyResolver(){
        return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
    }*/
    /**
     * 根据用户限流,参数中必须有user字段
     * @return
     */
    /*@Bean(value = "userKeyResolver")
    public KeyResolver userKeyResolver(){
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
    }*/
}

修改配置文件

spring:
  cloud:
    gateway:
      routes:
        # 系统服务
        - id: gmaya-service-admin # 不重复即可
          uri: lb://gmaya-service-admin # 需要转发到的服务名称
          predicates:
            # 以 /admin/开头的路径全部转发到lb://gmaya-service-admin的服务上,此时gmaya-service-admin可以负载均衡
            - Path=/admin/**
          filters:
            # 去掉前面1个前缀,也就是真正转发访问的时候不带/admin/
            - StripPrefix=1
            # 降级配置
            - name: Hystrix
              args:
                name: default
                fallbackUri: 'forward:/defaultFallback'
            # 限流配置
            - name: RequestRateLimiter
              args:
                # 用于限流的键的解析器的 Bean 对象的名字
                key-resolver: '#{@hostAddrKeyResolver}'
                # 令牌桶每秒填充平均速率(实际情况适当加大即可:10)
                redis-rate-limiter.replenishRate: 1
                # 令牌桶总容量(实际情况适当加大即可:20)
                redis-rate-limiter.burstCapacity: 3

测试

使用测试工具测试,每秒两次访问。

查看redis中的值

将令牌桶总容量中的3个值消耗完毕,再多次访问即页面就是一片空白。


TODO : 后续有时间,研究一下自定义限流返回信息。


文章作者: GMaya
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 GMaya !
评论
 上一篇
Yapi的安装与使用 Yapi的安装与使用
前言 YApi 是高效、易用、功能强大的 api 管理平台,旨在为开发、产品、测试人员提供更优雅的接口管理服务。可以帮助开发者轻松创建、发布、维护 API,YApi 还为用户提供了优秀的交互体验,开发人员只需利用平台提供的接口数据写入工具以
2020-05-18
下一篇 
Spring Boot Admin 监控 Spring Boot Admin 监控
简介 Spring Boot Admin 用于监控基于 Spring Boot 的应用,它是在 Spring Boot Actuator 的基础上提供简洁的可视化 WEB UI。Spring Boot Admin 提供了很多功能,如显示 n
2020-05-09
  目录