小言_互联网的博客

SpringCloud系列之API Gateway开发手册(Hoxton版本)

409人阅读  评论(0)

SpringCloud系列之API Gateway开发手册(Hoxton版本)

1、API Gateway简单介绍

1.1 什么是API网关?

API网关是所有请求的入口,承载了所有的流量,API Gateway是一个门户一样,也可以说是进入系统的唯一节点。这跟面向对象设计模式中的Facet模式很像。API Gateway封装内部系统的架构,并且提供API给各个客户端。它还可能有其他功能,如授权、监控、负载均衡、缓存、请求分片和管理、静态响应处理等

画图表示,没有网关的情况,客户端的请求会直接落到后端的各个服务中,无法集中统一管理

画图表示,有网关的情况,所有的请求都先经过网关,然后进行分发到对应服务

1.2 API网关的作用

网关可以用于授权、监控、负载均衡、缓存、请求分片和管理、静态响应处理等,挑几个介绍

  • 动态路由
    网关可以做路由转发,假如服务信息变了,只要改网关配置既可,所以说网关有动态路由(Dynamic Routing)的作用,如图:
  • 请求监控
    请求监控可以对整个系统的请求进行监控,详细地记录请求响应日志,如图,可以将日志丢到消息队列,如果没有使用网关的话,记录请求信息需要在各个服务中去做
  • 认证鉴权
    认证鉴权可以对每一个访问请求做认证,拒绝非法请求,保护后端的服务,不需要每个服务都做鉴权,在项目中经常有加上OAuth2.0、JWT,Spring Security进行权限校验
  • 压力测试
    有网关的系统,如果要要对某个服务进行压力测试,可以如图所示,改下网关配置既可,测试请求路由到测试服务,测试服务会有单独的测试数据库,这样测试的请求就不会影响到正式的服务和数据库

2、SpringCloud Gateway

2.1 What is SpringCloud Gateway?

用公网的解释是:SpringCloud Gateway是一个在Spring生态系统之上构建的API网关,包括:Spring 5,Spring Boot 2,Project Reactor(基于高性能的Reactor模式响应式通信框架Netty,异步阻塞模型)。Spring Cloud Gateway旨在提供一种简单而有效的方法来路由到API,并为它们提供跨领域的关注,例如:安全性,监视/度量和弹性。

SpringCloud Gateway采用了WebFlux,所以使用时不需要引入web场景启动器对应jar,而需要依赖于****WebFlux,当然高版本,肯定不需要自己引入,在对应的starter已经集成

2.2 SpringCloud Gateway结构

引用官方图例如图,SpringCloud Gateway的底层基于Netty,主要组成有Predicates(谓词或者断言)、Route(路由)、Filter(过滤器)

画张思维导图表示SpringCloud Gateway的组成:

  • 路由(route):网关的基本构建块。它由ID,目标URI,谓词集合和过滤器集合定义
  • 过滤器(Filter):这些过滤器是使用特定工厂构造的Spring FrameworkGatewayFilter实例
  • 谓词(Predicates): 引用了java8的函数谓词,输入类型是Spring FrameworkServerWebExchange。谓词可以匹配HTTP请求中的所有内容,例如标头或参数

2.3 SpringCloud Gateway工作方式

从总体上概述了Spring Cloud Gateway的工作方式,引用官网的图例:

从官网的图来看,并不是特别复杂,首先客户端请求都会先经过Gateway Handler Mapping,匹配上就通过Gateway Web Handler转给过滤器处理,过滤器分为PreFilter(前置过滤器)、PostFilter(后置过滤器)。过滤器由虚线分隔的原因是,筛选器可以在发送代理请求之前和之后运行逻辑。所有“前置”过滤器逻辑均被执行。然后发出代理请求。发出代理请求后,将运行“后置”过滤器逻辑

3、Gateway实验环境准备

环境准备:

  • JDK 1.8

  • SpringBoot2.2.3

  • SpringCloud(Hoxton.SR7)

  • Maven 3.2+

  • 开发工具

    • IntelliJ IDEA
    • smartGit

新增SpringBoot Initializer项目:New Module->Spring Initializer,选择jdk版本,至少jdk8

packaging选择jar,java version选择jdk8的,然后点next

选择Gateway的依赖,选择之后会自动加上对应pom配置

Eureka客户端的依赖也可以加上,这样就可以注册服务到eureka服务端

新建项目之后,检查pom是否有spring-cloud-starter-gateway

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

检查pom是否有spring-cloud-starter-netflix-eureka-client

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

如果不通过idea的Spring Initializer新建项目的,需要自己加上:

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

本博客的是基于spring-cloud-starter-netflix-eureka-client进行试验,试验前要运行eureka服务端,eureka服务提供者,代码请参考上一章博客

4、API Gateway简单实现

4.1 YAML配置Eureka和Gateway

Eureka客户端配置:ps,注意在Application加上@EnableEurekaClient

eureka:
  client:
    # 配置eureka服务地址
    service-url:
      defaultZone: http://localhost:8761/eureka/
    # 关闭eureka健康检查
    healthcheck:
      enabled: false
    # Eureka服务注册开启
    register-with-eureka: true
    # Eureka服务发现开启
    fetch-registry: true
  instance:
    status-page-url-path: http://localhost:8761/actuator/info
    health-check-url-path: http://localhost:8761/actuator//health
    # 显示ip
    prefer-ip-address: true
    # eureka实例id
    instance-id: api-gateway8082

API Gateway配置:

spring:
  application:
    # 指定application name
    name: api-gateway
  cloud:
    gateway:
      routes:
        # 路由id
        - id: user-service-provider
          # 路由到的地址
          uri: http://127.0.0.1:8083/api/users/{username}
          # 设置谓词,路径匹配的进行路由
          predicates:
            - Path=/api/users/{username}

可能遇到:Unable to find RoutePredicateFactory with name Path ,谓词Path必须大写,而且等号之间不能有空格

4.2 Bean注册方式配置网关

除了配置文件,也可以通过配置类进行网关配置:

package com.example.springcloud.gateway.configuration;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * <pre>
 *     spring cloud gateway configuration
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2020/09/14 15:00  修改内容:
 * </pre>
 */
@Configuration
public class GatewayConfiguration {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
        return routeLocatorBuilder.routes()
                .route("user-service-provider1",
                        r->r.path("/api/findUser").uri("http://127.0.0.1:8083/api/findUser"))
                .build();
    }
}

4.3 CURL测试Gateway的接口

curl http://127.0.0.1:gateway_port/api/users/admin

5、Gateway谓词工厂分类

SpringCloud Gateway的谓词工厂分类如图,所谓谓词或者说断言,其实就是一种匹配的规则,根据这些匹配,匹配到就经过过滤器

SpringCloud Gateway的谓词分类比较多,在SpringCloud官方手册也有进行比较详细的介绍,所以本文章挑几个进行介绍既可

  • After谓词配置

在指定的datetime后触发过滤器

spring:
  cloud:
    gateway:
      routes:
        - id: after_route
            uri: http://127.0.0.1:8083/api/findUser
            predicates:
                - After=2019-01-01T16:30:00+08:00[Asia/Shanghai]
  • Before谓词配置
spring:
  cloud:
    gateway:
      routes:
        - id: before_route
          uri: http://127.0.0.1:8083/api/findUser
          predicates:
            - Before=2018-01-01T16:30:00+08:00[Asia/Shanghai]
  • Between谓词配置
spring:
  cloud:
    gateway:
      routes:
        - id: between_route
          uri: http://127.0.0.1:8083/api/findUser
          predicates:
            - Between=2018-01-01T16:30:00+08:00[Asia/Shanghai], 2019-01-01T16:30:00+08:00[Asia/Shanghai]
  • Cookie谓词配置

加上cookie “chocolate=ch.p” 经过过滤器

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: http://127.0.0.1:8083/api/findUser
        predicates:
        - Cookie=chocolate, ch.p

linux curl测试接口:

curl http://192.168.9.10:8082/api/findUser?username=nicky --cookie "chocolate=ch.p"

  • Header谓词配置

加上请求头参数“X-Request-Id”, \d+标识数字

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: http://127.0.0.1:8083/api/findUser
        predicates:
        - Header=X-Request-Id, \d+
curl http://192.168.9.10:8082/api/findUser?username=nicky -H "X-Request-Id:123" 
  • HOST谓词配置

加上域名host匹配

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://127.0.0.1:8083/api/findUser
        predicates:
        - Host=**.csdn.net
curl http://192.168.9.10:8082/api/findUser?username=nicky -H "Host:smilenicky.csdn.net" 
  • Method谓词配置

method是http请求方式,eg:GET、POST、PUT等等

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: http://127.0.0.1:8083/api/findUser
        predicates:
        - Method=GET
 curl -X POST http://192.168.9.30:8082/api/findUser?username=nicky
{"timestamp":"2020-09-15T02:37:30.224+00:00","status":405,"error":"Method Not Allowed","message":"","path":"/api/findUser"}
  • Query谓词配置

请求参数匹配,url?username=???

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: http://127.0.0.1:8083/api/findUser
        predicates:
        - Query=username
curl http://192.168.9.30:8082/api/findUser?username=nicky
  • 远程地址谓词

在远程机192.168.1.1调用接口,都会匹配

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: http://127.0.0.1:8083/api/findUser
        predicates:
        - RemoteAddr=192.168.1.1/22
curl http://192.168.9.30:8082/api/findUser?username=nicky
  • 权重谓词配置

权重匹配比较像nginx的权重配置,8083 url接收80%的请求,8084接收20%请求

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri:http://127.0.0.1:8083
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: http://127.0.0.1:8084
        predicates:
        - Weight=group1, 2
curl http://192.168.9.30:8082/api/findUser?username=nicky

6、Gateway过滤器类型分类

7、GatewayFilter工厂分类

SpringCloud的gatewayFilter有很多分类,详情参考官方手册,官方手册的介绍也相对比较详细,所以本博客挑几个介绍:

  • PrefixPath GatewayFilter

这个网关过滤器是在请求链接时候加上前缀,如下配置,加上/api的前缀

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: http://127.0.0.1:8083
        filters:
        - PrefixPath=/api
curl http://192.168.9.30:8082/findUser?username=nicky

相当于:

curl http://192.168.9.30:8082/api/findUser?username=nicky
  • AddRequestParameter GatewayFilter

AddRequestParameter GatewayFilter自动带上请求参数的过滤器,加上username=admin参数

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_parameter_route
        uri: http://127.0.0.1:8083
        filters:
        - AddRequestParameter=username, admin
curl http://192.168.9.30:8082/api/findUser

相当于:

curl http://192.168.9.30:8082/api/findUser?username=admin
  • Hystrix GatewayFilter

Hystrix分布式服务容错保护的过滤器

pom加上配置:

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

写个ResultBean类:

package com.example.springcloud.gateway.bean;

import lombok.Data;
import org.springframework.http.HttpStatus;

/**
 * <pre>
 *      ResultBean
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2020/08/10 17:07  修改内容:
 * </pre>
 */
@Data
public class ResultBean {

    /**
     * 状态
     * */
    private int status;
    /**
     * 描述
     * */
    private String desc;
    /**
     * 数据返回
     * */
    private Object data;

    public ResultBean(int status, String desc, Object data) {
        this.status = status;
        this.desc = desc;
        this.data = data;
    }

    public ResultBean(Object data) {
        this.status = HttpStatus.OK.value();
        this.desc = "处理成功";
        this.data = data;
    }

    public static ResultBean ok(Object data) {
        return new ResultBean(data);
    }

    public static ResultBean ok() {
        return new ResultBean(null);
    }

    public static ResultBean badRequest(String desc,Object data) {
        return new ResultBean(HttpStatus.BAD_REQUEST.value(), desc, data);
    }

    public static ResultBean badRequest(String desc) {
        return new ResultBean(HttpStatus.BAD_REQUEST.value(), desc, null);
    }

    public static ResultBean serverError(String desc, Object data){
        return new ResultBean(HttpStatus.INTERNAL_SERVER_ERROR.value(),"服务器内部异常:"+desc,data);
    }

    public static ResultBean serverError(String desc){
        return new ResultBean(HttpStatus.INTERNAL_SERVER_ERROR.value(),"服务器内部异常:"+desc,null);
    }

}

接口出错,回调这个接口,避免一直请求,造成服务雪崩

package com.example.springcloud.gateway.rest.controller;

import com.example.springcloud.gateway.bean.ResultBean;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * <pre>
 *      HystrixRestController
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2020/09/15 11:46  修改内容:
 * </pre>
 */
@RestController
public class HystrixRestController {

    @GetMapping(value = {"/fallback"})
    public ResultBean fallback() {
        return ResultBean.badRequest("Hystrix fallback");
    }
}

加上配置:

spring:
  cloud:
    gateway:
      routes:
                - id: hystrix_route
          uri: http://127.0.0.1:8083/api/findUser
          predicates:
            - Method=GET
          filters:
            - name: Hystrix
              args:
                name: fallbackcmd
                fallbackUri: forward:/fallback

关了服务请求,让接口报错:

curl http://192.168.9.10:8082/api/findUser?username=nicky
{"status":400,"desc":"Hystrix fallback","data":null}

ps:spring cloud gateway:Unable to find GatewayFilterFactory with name Hystrix

项目里有引入eureka客户端,跟其源码,可以指定其实是有引入Hystrix的,所以原本,我就不加上Hystrix配置,不过项目是一直有报错:spring cloud gateway:Unable to find GatewayFilterFactory with name Hystrix,所以maven查看jar,发现eureka client只是引入部分的jar:

所以总的来说,还是要自己配置;

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

requestratelimiter用于简单的实现限流,基于Redis实现

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

@Bean
public KeyResolver ipKeyResolver() {
    return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}

KeyResolver需要加上@Primary

spring:
  redis:
    host: 127.0.0.1
    password:
    port: 6379
  cloud:
    gateway:
      routes:
        ## RequestRateLimiter GatewayFilter简单限流
        - id: requestratelimiter_route
          uri: http://192.168.9.30:8083/api/findUser
          predicates:
            - Method=GET
          filters:
            - name: RequestRateLimiter
              args:
                # 每秒允许处理的请求数量
                redis-rate-limiter.replenishRate: 1
                # 每秒最大处理的请求数量
                redis-rate-limiter.burstCapacity: 2
                #每秒最大处理token数量
                redis-rate-limiter.requestedTokens: 1
                # 限流策略,对应策略的BeanName
                key-resolver: "#{@ipKeyResolver}"

  • Retry GatewayFilter

retry GatewayFilter是用于重试的过滤器

spring:
  cloud:
      gateway:
        routes:
                # RetryFilter 重试过滤器
        - id: retry_test
          uri: http://127.0.0.1:8083/api/findUser
          predicates:
            - Method=GET
          filters:
            - name: Retry
              args:
                retries: 3 # 重试次数
                statuses: BAD_GATEWAY
                methods: GET,POST
                backoff:
                  firstBackoff: 10ms
                  maxBackoff: 50ms
                  factor: 2
                  basedOnPreviousValue: false

8、GlobalFilter工厂分类介绍

SpringCloud Gateway的GlobalFilter分类如图,详情可以参考官方手册,GlobalFilter是全局的过滤器,作用于所有的路由

8.1 全局过滤器分类

8.2 自定义全局过滤器

package com.example.springcloud.gateway.filter.global;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Slf4j
public class CustomGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("custom global filter");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

配置类进行配置:

@Bean
    public GlobalFilter customFilter() {
        return new CustomGlobalFilter();
    }

9、SpringCloud官方手册和博客

9.1 SpringCloud Gateway官方手册

  • SpringCloud 2.0系列的官方参考手册:
    https://docs.spring.io/spring-cloud-config/docs/2.2.x/reference/html/
  • SpringCloud gateway的官方参考手册:
    https://docs.spring.io/spring-cloud-gateway/docs/2.2.x-SNAPSHOT/reference/html

9.2 SpringCloud Gateway优质参考博客

  • 方志鹏大佬系列Spring Cloud博客:https://www.fangzhipeng.com/spring-cloud.html
  • 使用Spring Cloud与Docker实战微服务:https://eacdy.gitbooks.io/spring-cloud-book/content/
  • 程序员DD大佬系列Spring Cloud博客:http://blog.didispace.com/spring-cloud-learning/
  • Spring Cloud GateWay 应用 -> 高可用:https://juejin.im/post/6854573221329846280#heading-6
  • Spring Cloud Gateway:SpringCloud API网关服务:https://juejin.im/post/6844903982599684103

转载:https://blog.csdn.net/u014427391/article/details/108584446
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场