我的架构梦:(三十九)SpringCloud核心组件之 GateWay网关组件

SpringCloud核心组件之 GateWay网关组件

    • 一、GateWay简介
    • 二、GateWay核心概念
    • 三、GateWay工作过程(How It Works)
    • 四、GateWay应用
    • 五、GateWay路由规则详解
    • 六、GateWay动态路由详解
    • 七、GateWay过滤器
    • 八、GateWay高可用

一、GateWay简介

Spring Cloud GateWaySpring Cloud的一个全新项目,目标是取代Netflix Zuul,它基于 Spring5.0+SpringBoot2.0+WebFlux(基于高性能的Reactor模式响应式通信框架Netty,异步非阻塞模型)等技术开发,性能高于Zuul,官方测试,GateWayZuul1.6倍,旨在为微服务架构提供一种简单有效的统一的API路由管理方式。

Spring Cloud GateWay不仅提供统一的路由方式(反向代理),并且基于 Filter(定义过滤器对请求过滤, 完成一些功能) 链的方式提供了网关基本的功能,例如:鉴权、流量控制、熔断、路径重写、日志监控 等。

网关在架构中的位置

我的架构梦:(三十九)SpringCloud核心组件之 GateWay网关组件_第1张图片

二、GateWay核心概念

Zuul1.x 阻塞式IO 2.x 基于Netty

Spring Cloud GateWay天生就是异步非阻塞的,基于Reactor模型。

一个请求—>网关根据一定的条件匹配—匹配成功之后可以将请求转发到指定的服务地址;而在这个过 程中,我们可以进行一些比较具体的控制(限流、日志、黑白名单)

  • 路由(route): 网关最基础的部分,也是网关比较基础的工作单元。路由由一个ID、一个目标 URL(最终路由到的地址)、一系列的断言(匹配条件判断)和Filter过滤器(精细化控制)组 成。如果断言为true,则匹配该路由。
  • 断言(predicates):参考了Java8中的断言java.util.function.Predicate,开发人员可以匹配Http 请求中的所有内容(包括请求头、请求参数等)(类似于nginx中的location匹配一样),如果断言与请求相匹配则路由。
  • 过滤器(filter):一个标准的Spring webFilter,使用过滤器,可以在请求之前或者之后执行业务逻辑。

我的架构梦:(三十九)SpringCloud核心组件之 GateWay网关组件_第2张图片

其中,Predicates断言就是我们的匹配条件,而Filter就可以理解为一个无所不能的拦截器,有了 这两个元素,结合目标URL,就可以实现一个具体的路由转发。

三、GateWay工作过程(How It Works)

我的架构梦:(三十九)SpringCloud核心组件之 GateWay网关组件_第3张图片

客户端向Spring Cloud GateWay发出请求,然后在GateWay Handler Mapping中找到与请求相匹配的路由,将其发送到GateWay Web Handler;Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前 (pre)或者之后(post)执行业务逻辑。

Filter在“pre”类型过滤器中可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在“post”类型的过滤器中可以做响应内容、响应头的修改、日志的输出、流量监控等。

GateWay核心逻辑:路由转发+执行过滤器链

四、GateWay应用

使用网关对自动投递微服务进行代理(添加在它的上游,相当于隐藏了具体微服务的信息,对外暴露的是网关)。

1、创建工程riemann-cloud-gateway-9002导入依赖

GateWay不需要使用web模块,它引入的是WebFlux(类似于SpringMVC)


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0modelVersion>

    <artifactId>riemann-cloud-gateway-9002artifactId>

    
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.1.6.RELEASEversion>
    parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-commonsartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-gatewayartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webfluxartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-loggingartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.4version>
            <scope>providedscope>
        dependency>

        
        <dependency>
            <groupId>com.sun.xml.bindgroupId>
            <artifactId>jaxb-coreartifactId>
            <version>2.2.11version>
        dependency>
        <dependency>
            <groupId>javax.xml.bindgroupId>
            <artifactId>jaxb-apiartifactId>
        dependency>
        <dependency>
            <groupId>com.sun.xml.bindgroupId>
            <artifactId>jaxb-implartifactId>
            <version>2.2.11version>
        dependency>
        <dependency>
            <groupId>org.glassfish.jaxbgroupId>
            <artifactId>jaxb-runtimeartifactId>
            <version>2.2.10-b140310.1920version>
        dependency>
        <dependency>
            <groupId>javax.activationgroupId>
            <artifactId>activationartifactId>
            <version>1.1.1version>
        dependency>
        

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-actuatorartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <optional>trueoptional>
        dependency>

        
        
    dependencies>

    <dependencyManagement>
        
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>Greenwich.RELEASEversion>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

    <build>
        <plugins>
            
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <configuration>
                    <source>1.8source>
                    <target>1.8target>
                    <encoding>utf-8encoding>
                configuration>
            plugin>
            
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

project>

注意!!!不要引入starter-web模块,需要引入web-flux。

2、application.yml 配置文件部分内容

server:
  port: 9002

eureka:
  client:
    serviceUrl: # eureka server的路径
      defaultZone: http://RiemannCloudEurekaServerA:8761/eureka,http://RiemannCloudEurekaServerB:8762/eureka # 把 eureka 集群中的所有 url 都填写了进来,也可以只写一台,因为各个 eureka server 可以同步注册表
  instance:
    # 使用ip注册,否则会使用主机名注册了(此处考虑到对老版本的兼容,新版本经过实验都是ip)
    prefer-ip-address: true
    # 自定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@

spring:
  application:
  name: riemann-cloud-gateway
  cloud:
    gateway:
      routes: # 路由可以有多个
        - id: service-autodeliver-router # 我们自定义的路由 ID,保持唯一
          # uri: http://127.0.0.1:8096  # 目标服务地址  自动投递微服务(部署多实例)  动态路由:uri配置的应该是一个服务名称,而不应该是一个具体的服务实例的地址
          uri: lb://riemann-service-autodeliver # gateway网关从服务注册中心获取实例信息然后负载后路由
          predicates: # 断言:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默 认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
            - Path=/autodeliver/**
        - id: service-resume-router          # 我们自定义的路由 ID,保持唯一
          # uri: http://127.0.0.1:8081       # 目标服务地址
          # http://localhost:9002/resume/openstate/1545132
          # http://127.0.0.1:8081/openstate/1545132
          uri: lb://riemann-service-resume
          predicates: # 断言:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默 认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
            - Path=/resume/**
          filters:
            - StripPrefix=1

上面这段配置的意思是,配置了一个 idservice-autodeliver-router 的路由规则,当向网关发起请求 http://localhost:9002/autodeliver/checkAndBegin/1545132,请求会被分发路由到对应的微服务上。

在这里插入图片描述

五、GateWay路由规则详解

Spring Cloud GateWay 帮我们内置了很多 Predicates功能,实现了各种路由匹配规则(通过 Header、请求参数等作为条件)匹配到对应的路由。

我的架构梦:(三十九)SpringCloud核心组件之 GateWay网关组件_第4张图片

1、时间点后匹配

spring:
  cloud:
    gateway:
      routes: 
        - id: after_route
          uri: https://example.org
          predicates: 
            - After=2020-08-16T17:42:47.789-07:00[America/Denver]

2、时间点前匹配

spring:
  cloud:
    gateway:
      routes: 
        - id: before_route
          uri: https://example.org
          predicates: 
            - Before=2020-08-16T17:42:47.789-07:00[America/Denver]

3、时间区间匹配

spring:
  cloud:
    gateway:
      routes: 
        - id: between_route
          uri: https://example.org
          predicates: 
            - Between=2020-08-16T17:42:47.789-07:00[America/Denver], 2020-08-17T17:42:47.789-07:00[America/Denver]

4、指定Cookie正则匹配指定值

spring:
  cloud:
    gateway:
      routes: 
        - id: cookie_route
          uri: https://example.org
          predicates: 
            - Cookie=chocolate, ch.p

5、指定Header正则匹配指定值

spring:
  cloud:
    gateway:
      routes: 
        - id: header_route
          uri: https://example.org
          predicates: 
            - Header=X-Request-Id, \d+

6、请求Host匹配指定值

spring:
  cloud:
    gateway:
      routes: 
        - id: host_route
          uri: https://example.org
          predicates: 
            - Host=**.somehost.org,**.anotherhost.org

7、请求Method匹配指定请求方式

spring:
  cloud:
    gateway:
      routes: 
        - id: method_route
          uri: https://example.org
          predicates: 
            - Method=GET,POST

8、请求路径正则匹配

spring:
  cloud:
    gateway:
      routes: 
        - id: path_route
          uri: https://example.org
          predicates: 
            - Path=/red/{segment},/blue/{segment}

9、请求包含某参数

spring:
  cloud:
    gateway:
      routes: 
        - id: query_route
          uri: https://example.org
          predicates: 
            - Query=green

10、请求包含某参数并且参数值匹配正则表达式

spring:
  cloud:
    gateway:
      routes: 
        - id: query_route
          uri: https://example.org
          predicates: 
            - Query=red, gree.

11、远程地址匹配

spring:
  cloud:
    gateway:
      routes: 
        - id: remoteaddr_route
          uri: https://example.org
          predicates: 
            - RemoteAddr=192.168.1.1/24

六、GateWay动态路由详解

GateWay支持自动从注册中心中获取服务列表并访问,即所谓的动态路由。

实现步骤如下

1、pom.xml中添加注册中心客户端依赖(因为要获取注册中心服务列表,eureka客户端已经引入)

2、动态路由配置

我的架构梦:(三十九)SpringCloud核心组件之 GateWay网关组件_第5张图片

注意:动态路由设置时,urilb: //开头(lb代表从注册中心获取服务),后面是需要转发到的服务名称。

七、GateWay过滤器

1、GateWay过滤器简介

从过滤器生命周期(影响时机点)的⻆度来说,主要有两个prepost:

生命周期时机点 作用
pre 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
post 这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

从过滤器类型的⻆度,Spring Cloud GateWay的过滤器分为GateWayFilterGlobalFilter两种

过滤器类型 影响范围
GateWayFilter 应用到单个路由上
GlobalFilter 应用到所有的路由上

如Gateway Filter可以去掉url中的占位后转发路由,比如

predicates: 
	- Path=/resume/**
	filters:
		- StripPrefix=1 # 可以去掉resume之后转发

注意:GlobalFilter全局过滤器是程序员使用比较多的过滤器,我们主要讲解这种类型。

2、 自定义全局过滤器实现IP访问限制(黑白名单)

请求过来时,判断发送请求的客户端的ip,如果在黑名单中,拒绝访问

自定义GateWay全局过滤器时,我们实现Global Filter接口即可,通过全局过滤器可以实现黑白名单、 限流等功能。

/**
 * 定义全局过滤器,会对所有路由生效
 */
@Slf4j
@Component  // 让容器扫描到,等同于注册了
public class BlackListFilter implements GlobalFilter, Ordered {

    // 模拟黑名单(实际可以去数据库或者redis中查询)
    private static List<String> blackList = new ArrayList<>();

    static {
        blackList.add("0:0:0:0:0:0:0:1");  // 模拟本机地址
    }

    /**
     * 过滤器核心方法
     * @param exchange 封装了request和response对象的上下文
     * @param chain 网关过滤器链(包含全局过滤器和单路由过滤器)
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 思路:获取客户端ip,判断是否在黑名单中,在的话就拒绝访问,不在的话就放行
        // 从上下文中取出request和response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        // 从request对象中获取客户端ip
        String clientIp = request.getRemoteAddress().getHostString();
        // 拿着clientIp去黑名单中查询,存在的话就决绝访问
        if(blackList.contains(clientIp)) {
            // 决绝访问,返回
            response.setStatusCode(HttpStatus.UNAUTHORIZED); // 状态码
            log.debug("=====>IP:" + clientIp + " 在黑名单中,将被拒绝访问!");
            String data = "Request be denied!";
            DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
            return response.writeWith(Mono.just(wrap));
        }

        // 合法请求,放行,执行后续的过滤器
        return chain.filter(exchange);
    }
    
    /**
     * 返回值表示当前过滤器的顺序(优先级),数值越小,优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }

}

八、GateWay高可用

网关作为非常核心的一个部件,如果挂掉,那么所有请求都可能无法路由处理,因此我们需要做
GateWay的高可用。

GateWay的高可用很简单:可以启动多个GateWay实例来实现高可用,在GateWay的上游使用Nginx
等负载均衡设备进行负载转发以达到高可用的目的。

启动多个GateWay实例(假如说两个,一个端口9002,一个端口9003),剩下的就是使用Nginx等完 成负载代理即可。示例如下:

#配置多个GateWay实例 
upstream gateway {
	server 127.0.0.1:9002;
	server 127.0.0.1:9003; 
}
location / {
	proxy_pass http://gateway;
}

你可能感兴趣的:(我的架构梦)