一、 简介

  • Spring Cloud Gateway 是 Spring 官网基于 Spring 5.0Spring Boot 2.0Project Reactor 等技术开发的网关服务

  • Spring Cloud Gateway 基于Filter链提供网关基本功能:安全、监控/埋点、限流等。

  • Spring Cloud Gateway为微服务架构提供简单、有效且统一的API路由管理方式。

  • Spring Cloud Gateway是替代 Netflix Zuul 的一套解决方案。

 1.1、名词概念
  • 路由(route): 路由信息由 一个ID一个目的URL一组断言工厂一组Filter 组成。如果路由断言为 真,说明请求URL和配置路由匹配,就可以通过路由去匹配对应的服务,否则不允通过。

  • 断言(Predicate):断言函数输入类型是 Spring 5.0框架 中的 ServerWebExchange,允许开发者去定义匹配来自于HTTP Request中的任何信息比如请求头和参数。

  • 过滤器(Filter): Spring Cloud Gateway中的 Filter 分为两种类型,分别是Gateway Filter和Global Filter,过滤器将会对匹配到的 请求响应 进行修改处理 。

 1.2、服务核心
  • 网关的核心功能是:过滤器链路由转发

  • Spring Cloud Gateway 组件的 核心 是一系列的 过滤器,通过过滤器链可以将客户端发送的请求转发(路由)到对应的微服务。 网关是加在整个微服务最前沿的防火墙和代理器,隐藏微服务 结点 IP 端口信息, 从而加强对服务端口的安全保护。

  • 网关本身也是一个微服务,也需要将其注册到服务注册中心。

 1.3、服务架构

 不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都可经过网关,然后再由网关来实现 鉴权、动态路由 等等操作。

 网关就是整个服务的统一入口,来自各个地方请求通过网关之后在进行下一步的调度转发。

在这里插入图片描述

二、 部署

 在配置好 服务中心具体服务提供 之后,重新添加一个项目模块,项目名自定义。

 2.1、依赖管理

 网关只需要在服务中心进行注册,然后通过网关服务调取其他的服务接口,完成请求的转发即可。

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <parent>
        <artifactId>bill-manager-springCloud</artifactId>
        <groupId>com.beordie</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gateway</artifactId>

    <dependencies>
        <!--网关依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        
		<!--中心服务-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

</project>
 2.2、启动类

 通过 springboot 注解启动,添加服务中心的扫描注解。

@SpringBootApplication
@EnableDiscoveryClient
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
 2.3、配置文件

 主要配置相应的 服务中心地址路由规则 ,以及涉及 跨域资源请求 的配置。

# 配置端口信息
server:
  port: 8086
  tomcat:
    uri-encoding: UTF-8

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        - id: bill-service-route
          # 代理的服务地址
          uri: lb://bill-service
          # 路由断言,可以配置映射路径
          predicates:
            - Path=/api/**
          filters:
          # 添加请求路径的前缀
            - StripPrefix=1
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods:
              - GET
              
eureka:
  client:
    service-url:
      defaultZone: HTTP://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

三、 路由

 3.1、id
- id: bill-service-route

 表示当前设置路由的标识,可以随便进行定义。

 3.2、uri
uri: HTTP://127.0.0.1:9091
uri: lb://bill-service

  将符合 Path 规则的一切请求,都代理到 uri 参数指定的地址,也就是代理到 http://127.0.0.1:9091 的服务。

  在刚才的路由规则中,把路径对应的服务地址写死了!如果同一服务有多个实例的话,这样做显然不合理。

  使用 lb://bill-service 这样的形式,通过 lb: 加上服务的名字,也就是在对应服务配置文件中指定的 application-name,也可以从服务中心注册的服务名找。

  • 路由配置中 uri 所用的协议为 lb 时(以 uri: lb://bill-service 为例),gateway 将使用 LoadBalancerClient 把 bill-service 通过 eureka 解析为实际的主机和端口,并进行 ribbon 负载均衡。
 3.3、predicates
- Path=/api/**

 定义请求当前路由的 url格式,也就是说 HTTP://127.0.0.1:8086/api/ 下面的所有请求都会朱发到当前路由进行处理。

 3.4、filters

 功能和名字意义相同,就是配置路由的过滤器的一些功能。

  • 添加前缀:对请求地址添加前缀路径之后再作为代理的服务地址。
- PrefixPath=/bill
  • 去除前缀:将请求地址中路径去除一些前缀路径之后再作为代理的服务地址,数字表示去除几个前缀。
- StripPrefix=1 

四、 跨域

  在 js 请求访问中,如果访问的地址与当前服务器的 域名ip 或者 端口号 不一致则称为跨域请求。若不解决则不能获取到对应地址的返回结果(能够请求,但是结果会被浏览器所拦截)。

 一般网关都是所有微服务的统一入口,必然在被调用的时候会出现跨域问题。

  表示对所有访问到网关服务器的请求地址都进行跨域设置。

globalcors:
  corsConfigurations:
    '[/**]':
 4.1、源地址

 指定允许访问的服务器地址,如:http://localhost:10000 也是可以的, 如果使用 * 就表示所有的访问都可以。

allowedOrigins: "*"
 4.2、请求方式

 指定可以访问的请求方式,可以指定多种。

allowedMethods:
  - GET
  - POST
  - HEAD

五、 过滤器

 5.1、简介

Gateway 作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作往往是通过网关提供的过滤器来实现的。前面的 路由功能 也是使用过滤器实现的。

Gateway自带过滤器有几十个,常见自带过滤器有:

过滤器名称功能
AddRequestHeader对匹配上的请求加上Header
AddRequestParameters对匹配上的请求路由添加参数
AddResponseHeader对从网关返回的响应添加Header
StripPrefix对匹配上的请求路径去除前缀

在这里插入图片描述

 5.2、 配置全局默认过滤器

  这些自带的过滤器的使用和路由配置时的方法一致,也可以将这些过滤器配置成不只是针对某个路由,而是可以对所有路由生效,也就是配置默认过滤器。

# 默认过滤器,对所有路由都生效
default-filters:
  - AddResponseHeader=X-Response-Foo, Bar
  - AddResponseHeader=name, beordie

  和上面一样的配置完成之后,每次的请求响应都会携带两个请求头信息,对应的名称和参数和配置的一致,可以在浏览器中对 Response Header 进行查看。

 5.3、自定义局部过滤器
  • 编写过滤器,需要继承 AbstractGatewayFilterFactory 并且需要定义内部类来完成参数的赋值。
  • 过滤器名需要以 GatewayFilterFactory 结尾。
package com.beordie.filters;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;

@Component
public class MyParamGatewayFilterFactory extends
        AbstractGatewayFilterFactory<MyParamGatewayFilterFactory.Config> {
    static final String PARAM_NAME = "param";
    public MyParamGatewayFilterFactory() {
        super(Config.class);
    }
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(PARAM_NAME);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            //获取请求参数中param对应的参数名 的参数值
            ServerHttpRequest request = exchange.getRequest();
            if(request.getQueryParams().containsKey(config.param)){
                request.getQueryParams().get(config.param).
                        forEach(value -> System.out.printf("------------局部过滤器--------%s = %s------",
                                config.param, value));
            }
            return chain.filter(exchange);
        };
    }

    public static class Config{
        //对应在配置过滤器的时候指定的参数名
        private String param;
        public String getParam() {
            return param;
        }
        public void setParam(String param) {
            this.param = param;
        }
    }
}

 然后就可以在配置文件对拦截器进行使用。

- MyParam=name

 请求在通过时,过滤去就会试图去查找参数名叫做 name 的携带参数,并且进行控制台的打印操作。

 5.4、 自定义全局过滤器
  • 全局过滤器不需要在配置文件中进行配置,直接定义将会配置在程序中

  • 需要实现 GlobalFilter, Ordered 两个接口。

    package com.beordie.filters;
    
    import org.apache.commons.lang.StringUtils;
    import org.springframework.boot.web.servlet.filter.OrderedFilter;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    /**
     * @Description
     * @Date 2021/8/22 17:21
     * @Created 30500
     */
    public class MyGlobalFilter implements GlobalFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            System.out.println("--------------全局过滤器MyGlobalFilter------------------");
            String token = exchange.getRequest().getHeaders().getFirst("token");
            if(StringUtils.isBlank(token)){
                //设置响应状态码为未授权
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
            return chain.filter(exchange);
        }
    
        @Override
        public int getOrder() {
            // 值越小,全局控制器的执行就越靠前
            return 1;
        }
    }
    

六、 扩展知识

 6.1、负载均衡和熔断

 Gateway中默认就已经集成了 Ribbon 负载均衡和 Hystrix 熔断机制。但是所有的超时策略都是走的默认值,比如熔断超时时间只有1S,很容易就触发了。因此建议手动进行配置.

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
			timeoutInMilliseconds: 6000 #服务降级超时时间,默认1S
ribbon:
  ConnectTimeout: 1000 # 连接超时时长
  ReadTimeout: 2000 # 数据通信超时时长
  MaxAutoRetries: 0 # 当前服务器的重试次数
  MaxAutoRetriesNextServer: 0 # 重试多少次服务
 6.2、Gateway的高可用
  • 启动多个 Gateway 服务,自动注册到Eureka,形成集群。如果是服务内部访问,访问Gateway,自动负载均衡,没问题。

  • 但是,Gateway更多是外部访问,PC端、移动端等。它们无法通过Eureka进行负载均衡,那么该怎么办? 此时, 可以使用其它的服务网关,来对Gateway进行代理。比如:Nginx 。

 6.3、Gateway与Feign的区别
  • Gateway 作为整个应用的流量入口,接收所有的请求,如PC、移动端等,并且将不同的请求转发至不同的处理微服务模块,其作用可视为nginx;大部分情况下用作权限鉴定、服务端流量控制。
  • Feign 则是将当前微服务的部分服务接口暴露出来,并且主要用于各个微服务之间的服务调用 。
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐