天天看点

spring-cloud-gateway(1)--->spring-cloud-gateway的基本使用

1、API网关

     API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。

网关应当具备以下功能:

  • 性能:API高可用,负载均衡,容错机制。
  • 安全:权限身份认证、脱敏,流量清洗,后端签名(保证全链路可信调用),黑名单(非法调用的限制)。
  • 日志:日志记录(spainid,traceid)一旦涉及分布式,全链路跟踪必不可少。
  • 缓存:数据缓存。
  • 监控:记录请求响应数据,api耗时分析,性能监控。
  • 限流:流量控制,错峰流控,可以定义多种限流规则。
  • 灰度:线上灰度部署,可以减小风险。
  • 路由:动态路由规则。

目前,比较流行的网关有:Nginx 、 Kong 、Orange等等,还有微服务网关Zuul 、Spring Cloud Gateway等等

对于 API Gateway,常见的选型有基于 Openresty 的 Kong、基于 Go 的 Tyk 和基于 Java 的 Zuul。这三个选型本身没有什么明显的区别,主要还是看技术栈是否能满足快速应用和二次开发。

2、spring-cloud-gateway 模型图:

spring-cloud-gateway(1)--->spring-cloud-gateway的基本使用

             在使用spring-cloud-gateway的时候,我们一般的做法是构建一个网关微服务,然后在里面配置各种路由信息。

3、实战案例:

       3.1、构建一个spring-boot项目引入如下依赖:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
           

        3.2、项目启动类:

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

      }
           

         3.3、配置路由:

                 我们配置一个简单的路由,主要是通过网关服务进行转发到我们的订单服务:

spring:
  cloud:
    gateway:
      routes:
      - id: orderapi                    #当前路由的id
        uri: http://localhost:7070      #当前路由转发的目标服务
        predicates:                     #当前路由的断言列表
        - Path=/orderapi/**                使用spring-cloud-gateway提供的Path断言
        filters:                        #当前路由的过滤器列表
        - StripPrefix=1  #表示在路径匹配的时候会去掉第一级,例如输入http://localhost:6060/orderapi/order/findAll
                         #将会转发到http://localhost:7070/order/findAll
                         #StripPrefix=1的含义就是会去掉/orderapi  如果等于2就会去掉/orderapi/order
           

         这就是一个简单的路由配置。在spring-cloud-gateway中提供了很多的断言、过滤器的实现,详细可见官网。

4、自定义断言:

      在spring-cloud-gateway中提供了很多的断言、过滤器的实现,我们也可以实现自己的断言或者过滤器,接下来我们以实现断言为案例进行讲解。

      我们实现一个断言,这个断言的核心业务是需要检查请求的Header中是否存在accesstoken这个属性,如果存在我们进行请求转发,如果不存在就不进行转发。

       实现的方式也比较简单,只需要实现 AbstractRoutePredicateFactory 这个接口即可,只是有一下约束而已,固定为断言Name + "RoutePredicateFactory"字符,然后配置使用的时候使用断言名称配置即可。

/**
 * 自定义断言,类名格式断言名称+ “RoutePredicateFactory”
 */
@Component
public class AuthRoutePredicateFactory extends AbstractRoutePredicateFactory<AuthRoutePredicateFactory.Config> {
    public AuthRoutePredicateFactory() {
        super(Config.class);
    }

    //定义配置值的顺序
    @Override
    public List <String> shortcutFieldOrder() {
        return Arrays.asList("headeKey");
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                HttpHeaders headers = serverWebExchange.getRequest().getHeaders();
                List <String> strings = headers.get(config.getHeadeKey());
                if (CollectionUtils.isEmpty(strings)){
                    return false;
                }else {
                    return true;
                }
            }
        };
    }

    @Validated
    public static class Config {

        @NotEmpty
        private String headeKey;

        public Config() {
        }

        public String getHeadeKey() {
            return headeKey;
        }

        public void setHeadeKey(String headeKey) {
            this.headeKey = headeKey;
        }
    }
}
           

       自定义断言的配置方式:

spring:
  cloud:
    gateway:
      routes:     
      - id: costomer_predicate
        uri: http://www.baidu.com
        predicates:
        - Path=/customer/predicate/**
        - Auth=accesstoken     //自定义的断言名称,断言中的配置实例的headeKey=accesstoken
        filters:
        - StripPrefix=2
           

5、网关层使用hystrix进行断路保护

     5.1、引入hystrix的如下依赖:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
           

   5.2、使用spring-cloud-gateway提供的Hystrix相关的过滤器,配置如下:

spring:
  cloud:
    gateway:
      routes:
#常规断言的使用
      - id: orderapi
        uri: http://localhost:7070
        predicates:
        - Path=/orderapi/**
        filters:
        - StripPrefix=1  #表示在路径匹配的时候会去掉第一级,例如输入http://localhost:6060/orderapi/order/findAll
                         #将会转发到http://localhost:7070/order/findAll
                         #StripPrefix=1的含义就是会去掉/orderapi  如果等于2就会去掉/orderapi/order

        配置hystrix过滤器进行断路保护            
        - name: Hystrix
          args:
            name: fallbackcmd       配置构建的hystrixCommand的id=fallbackcmd
            fallbackUri: forward:http://localhost:6060/fallback
            #fallbackUri: forward:/orderapi/order/mock  #转发请求到http://localhost:6060//orderapi/order/mock
                                                         #因此也会调到http://localhost:7070/order/mock, 也可以在
                                                         #网关服务中定义controller 进行转发过去。


可对id=fallbackcmd进行属性配置
hystrix:
    command:
        fallbackcmd:
            execution:
               isolation:
                  thread:
                     timeoutInMilliseconds: 1000
           

6、网关层使用redis进行网关层的限流

      6.1、先引入redis相关依赖如下:

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

       6.2、实现限流的key解析器,作用就是定义限流的规则,例如按照请求参数中的某个值进行限流,我们就以请求中的userId进行限流来实现一个key解析器:

@Bean
    KeyResolver userKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
    }
           

       6.3、配置redis:   

spring:  
   redis:
    host: localhost
    port: 6379
           

      6.4、给某个路由配置限流:

spring:
  application:
    name: gateway-server
  cloud:
    gateway:
      routes:
#常规断言的使用
      - id: orderapi
        uri: http://localhost:7070
        predicates:
        - Path=/orderapi/**
        filters:
        - StripPrefix=1  #表示在路径匹配的时候会去掉第一级,例如输入http://localhost:6060/orderapi/order/findAll
                         #将会转发到http://localhost:7070/order/findAll
                         #StripPrefix=1的含义就是会去掉/orderapi  如果等于2就会去掉/orderapi/order
        - name: Hystrix
          args:
            name: fallbackcmd
            fallbackUri: forward:http://localhost:6060/fallback
#            fallbackUri: forward:/orderapi/order/mock  #转发请求到http://localhost:6060//orderapi/order/mock
                                                         #因此也会调到http://localhost:7070/order/mock, 也可以在
                                                         #网关服务中定义controller 进行转发过去。
        - name: RequestRateLimiter
          args:
            # redis-rate-limiter是基于令牌桶来实现的
            #rate-limiter: "#{@redisRateLimiter}"       #指定限流器的beanName, 如果我们配置了redis-rate-limiter.*
                                                         #redis-rate-limiter: "#{@redisRateLimiter}"  redisRateLimiter 这个bean也是
                                                         #spring-cloud-gateway自动装配的,当然如果我们自定义限流器的话,我们是需要配置
                                                         #自定义的限流器beanName的。
            redis-rate-limiter.replenishRate: 10    #往令牌桶里放令牌的速率,3 表示每秒放3个令牌进去
            redis-rate-limiter.burstCapacity: 20    #令牌通中最多放多少个令牌,这个值就约等于每秒最大请求数。
            redis-rate-limiter.requestedTokens: 1  #每次请求消耗多少个令牌
            key-resolver: "#{@userKeyResolver}"        #限流键的解析器,此处配置的是实现了KeyResolver的bean userKeyResolver,
                                                            #例如userKeyResolver从请求中获取有查询参数的user的入参,例如 user=1
                                                            #userId=1  跟userId=2 的令牌通是隔离的,不通用的。
           

7、整合服务注册与发现在转发服务按照服务名称进行负载均衡的配置:

      我们部署微服务的时候基本上都是集群部署,并且将服务信息注册到服务注册中心,那么在网关层面就需要进行服务发现,然后进行转发的负载均衡。

     7.1、在网关服务中映入eureka-clientd 的相关依赖:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
           

     7.2、配置eureka-server信息:

eureka:
  client:
    service-url:
      defaultZone: http://localhost:9090/eureka
           

    7.3、配置激活器网关的服务发现定位器:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
           

     7.4、配置路由route的uri使其能够进行负载均衡:

spring:
  application:
    name: gateway-server
  cloud:
    gateway:
      routes:
      - id: userapi
        uri: lb://user-service    //使用lb://服务名称的方式
        predicates:
        - Path=/userapi/**
        filters:
        - StripPrefix=1
           

8、自定义全局过滤器

      在spring-cloud-gateway中有两种类型的filter,一种的局部的filter,只有配置到路由中的时候才生效,还有就是全局的filter,全局的不需要单独配置给某个route,而是所有的route都生效,接下来我们来实现一个全局的filter,这个filter的业务就是请求前后进行日志输出:这里的实现是跟reactor的知识点有关,需要有一定的响应式编程的基础知识。

/**
 * 全局过滤器是不需单独给predicate 进行配置,因为默认就是给所有的predicate配置上。
 */
@Component
public class CustomerLogGlobalFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("【CustomerLogGlobalFilter】 pre log 。 。 。");
        Mono <Void> mono = chain.filter(exchange).then(Mono.fromRunnable(() -> {
            MultiValueMap <String, HttpCookie> cookies = exchange.getRequest().getCookies();

            System.out.println("【CustomerLogGlobalFilter】 post log 。 。 。" + cookies.toSingleValueMap());
        }));
        return mono;
    }
}
           

继续阅读