摘要:位于微服务的前端、应用架构的边缘,充当系统的单一入口。
目录
[TOC]
API 网关
网关(:是一个网络连接到另一个网络的“关口”。在传输层上以实现网络互连,是最复杂的网络互连设备,仅用于两个高层协议不同的网络互连。Gateway)、网间连接器、协议转换器
API 网关:位于微服务的前端、应用架构的边缘,充当系统的单一入口。主要作用是接受来自客户端的 API 请求,(根据定义的策略处理请求),将请求路由(分配)给相应的后端服务处理,然后聚合结果并响应给请求者。
- 为什么 API 网关要在 Nginx 反向代理后面:
服务网关:而服务网关主要用于内部服务间的交互,更多地嵌入在服务架构内部,处理微服务间的通信。
- 将非功能性需求的实现卸载到基础设施层,并帮助开发人员专注于核心业务逻辑,从而加快应用程序的发布速度。
为什么需要网关?
传统的单体架构中只有一个服务开放给客户端调用,但是微服务架构中是将一个系统拆分成多个微服务,作为客户端去调用这些微服务,如果没有网关的存在,只能在本地记录每个微服务的调用地址。这种架构存在以下问题:
- 客户端多次请求不同的微服务:增加客户端代码或配置编写的复杂性。
- 认证复杂:每个服务都需要独立认证。
- 存在跨域请求:在一定场景下处理相对复杂。
基本功能
网关是所有微服务的门户,功能包括:统一入口,统一校验,统一分发
- 路由转发:接收到 Nginx 转发的 HTTP 请求。根据请求的路径和参数,将请求转发到相应的微服务实例。
- 请求统一认证、鉴权:统一在网关层面认证鉴权,微服务只专注于自身的业务,代码耦合性低。如,整合 Spring Cloud Gateway + Spring Cloud Security 统一认证鉴权。
- 请求校验、过滤 、拦截:网关对请求进行校验,如验证请求头、请求参数是否合法,是否包含必要的身份认证信息(如 Token )。过滤掉非法的、无资格的、低优先级的恶意流量(比如黑客攻击、恶意爬虫、黄牛、秒杀器等),不进行后面真正的业务逻辑处理,减轻系统的并发压力。
- 流量控制:为了保护后端微服务,网关可以配置 限流 和 熔断策略,防止请求过载导致服务崩溃。
- 协议转换、数据缓存
- 日志记录、监控:网关可以记录请求的日志信息,包括请求时间、请求路径、请求参数、响应状态等,以便于后续分析和监控。
认证和鉴权
大致的认证鉴权流程,架构如下图:
大致分为四个角色及对应服务,如下:
- 客户端:需要访问微服务资源
- 网关服务:负责转发、认证、鉴权,
oauth2-cloud-gateway - OAuth2.0 认证授权服务:负责认证授权颁发令牌,
oauth2-cloud-auth-server - 微服务集合:提供资源的一系列服务。
oauth2-cloud-order-service
大致流程如下:
1、客户端发出请求给网关,获取令牌
2、网关收到请求,直接转发给授权服务
3、授权服务验证用户名、密码等一系列身份,通过则颁发令牌给客户端
4、客户端携带令牌请求资源,请求直接到了网关层
5、网关对令牌进行校验(验签、过期时间校验….)、鉴权(对当前令牌携带的权限)和访问资源所需的权限进行比对,如果权限有交集则通过校验,直接转发给微服务
6、微服务进行逻辑处理
针对上述架构需要新建三个服务,案例源码目录如下:
常见的网关系统
- Spring Cloud Gateway:推荐。
- Zuul:主要通过过滤器(类似于 AOP)来过滤请求。成熟、简单。
:基于 Nginx 与 Lua 的高性能 Web 平台,内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。高性能(能处理超高并发)、扩展性极高。KongNginx:稳定可靠、低内存,偏运维、门槛高。- 美团百亿规模API网关服务Shepherd的设计与实现
比较分析:
- 性能:Spring Cloud Gateway基于Spring WebFlux实现,采用异步非阻塞模型,性能优于Zuul。
- 功能:Gateway提供了更丰富的路由规则和过滤器功能,如动态路由、权重路由等。
- 易用性:Zuul的配置相对简单,易于上手;而 Gateway 的配置更加灵活,但学习成本相对较高。
- 社区支持:Gateway作为Spring Cloud生态的一部分,得到了更广泛的社区支持。
综上所述,Spring Cloud Gateway在性能、功能和社区支持方面优于Zuul,但在易用性方面略逊一筹。在实际项目中,可以根据项目需求和团队技术栈选择合适的微服务网关方案。
Spring Cloud Zuul
Zuul提供基于配置的API表层。
作为网关,以便记录输入和输出日志,并可以根据各种参数来实现安全性或重定向请求。
-
支持自动路由映射到在
Eureka Server上注册的服务。使用注解@EnableZuulProxy来启用路由代理。 - 允许重定向REST请求以执行各种类型的过滤器。
- Spring Cloud Security通过Spring Cloud Zuul解决了许多安全问题。
Spring Cloud Gateway
Spring Cloud Gateway是Spring Cloud的新一代API网关,旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。
- 作为Spring Cloud生态系统中的网关,目标是替代Netflix ZUUL,具有更好的性能、更强的扩展性、以及更丰富的功能特性,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,限流等。
- 该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关。
- 基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。
功能
Spring Cloud Gateway 提供了丰富的功能,包括但不限于:
- 动态路由: 能够匹配任何请求属性,根据配置动态地将请求路由到不同的微服务实例
- 过滤器: 实现对请求和响应的各种操作,例如认证、授权、请求转发、限流等
- 集成Hystrix断路器: 处理微服务中的故障和延迟,防止故障扩散
- 集成Spring Cloud DiscoveryClient 服务发现功能
- 集成负载均衡: 将请求分发到多个服务实例,提高系统的性能和可用性
- 统一认证和授权: 通过集成 Spring Security 等机制,实现对微服务的统一认证和授权管理
- 监控和日志: 提供监控和日志功能,帮助理解网关的运行状况,分析请求流量
基于 Reactor 实现
Reactor 是一个运行在 Java8 之上满足 Reactice 规范的响应式框架,提供了一组响应式风格的 API。
Reactor 有两个核心类: Flux<T> 和 Mono<T>,这两个类都实现 Publisher 接口。
- Flux 类似 RxJava 的 Observable,它可以触发零到多个事件,并根据实际情况结束处理或触发错误。
- Mono 最多只触发一个事件,所以可以把 Mono 用于在异步任务完成时发出通知。

Flux 和 Mono 都是数据流的发布者,使用 Flux 和 Mono 都可以发出三种数据信号:元素值,错误信号,完成信号;
- 错误信号和完成信号都代表终止信号,终止信号用于告诉订阅者数据流结束了,错误信号终止数据流同时把错误信息传递给订阅者。
Mono
Mono 是一个发出(emit)0-1个元素的Publisher,可以被onComplete信号或者onError信号所终止。
mono 整体和Flux差不多,只不过这里只会发出0-1个元素。也就是说不是有就是没有。
演化过程
象Flux一样,我们来看看Mono的演化过程以帮助理解。
传统数据处理
1 | |
直接返回符合条件的对象或者null。
Optional的处理方式
1 | |
这个Optional我觉得就有反应式的那种味儿了,当然它并不是反应式。当我们不从返回值Optional取其中具体的对象时,我们不清楚里面到底有没有,但是Optional是一定客观存在的,不会出现NPE问题。
反应式数据处理
1 | |
和Optional有点类似的机制,当然Mono不是为了解决NPE问题的,它是为了处理响应流中单个值(也可能是Void)而存在的。
工作流程
- 客户端向
Spring Cloud Gateway发出请求, - 然后在
Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway Web Handler。 - Handler再通过指定的过滤器链来对请求进行过滤处理,
- 最后发送到我们实际的服务执行业务逻辑,然后返回。


网关服务搭建
参考:
网关服务搭建过程如下:
代码逻辑如下:
- 检查是否是白名单,白名单直接放行
- 检验令牌是否存在
- 解析令牌中的用户信息
- 封装用户信息到JSON数据中
- 加密JSON数据
- 将加密后的JSON数据放入到请求头中
新建一个oauth2-cloud-gateway模块,目录如下图:
application.yml 配置
添加 nacos 与 网关的相关配置如下:
1 | |
三大核心概念
routes(路由):路由是网关最基础的部分,路由信息由一个ID、一个目标URI、一组断言和过滤器组成。路由断言Predicate用于匹配请求,过滤器Filter用于修改请求和响应。如果断言为true,则说明请求URI和配置匹配,则执行路由。predicates(断言):满足断言的请求(则执行该路由)都会被路由到uri中。参考Java8中的断言Predicate,用于实现请求匹配逻辑,例如匹配路径、请求头、请求参数等。可以配置多个。filter(过滤器):可以在返回请求之前或之后修改请求和响应的内容。从作用范围可分为GatewayFilter(局部过滤器)和 GlobalFilter(全局过滤器)
routes(路由)
id:路由的唯一id,名称任意uri:路由转发的目标地址,http://localhost:8081就是固定地址lb://service-name:lb:指从nacos中按照名称获取微服务,并遵循负载均衡策略service-name:nacos注册中心中的服务名称,这里并不是IP地址形式的
predicates:filter:
predicates(断言)
断言(Predicate )属于 Java8 语言中的 Predicate 函数,参数为 ServerWebExchange 对象。可以让开发者(通过请求头、或请求参数)匹配到任意的 HTTP 请求。
-
简单理解的话,就是一个条件判断工具。
-
可以用于接口请求参数校验:
- 如果符合当前配置的规则则通过验证,进而使得服务网关将该请求路由到微服务中。
- 如果不符合当前配置的规则,就无法通过验证,返回错误信息。
Predicate接受输入参数,返回一个布尔值结果。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)。
内置Predicate Factory
在Gateway中,有一些的内置Predicate Factory,有了这些,在运行时,Gateway会 自动根据需要创建其对应的Pridicate对象测试路由条件。
Path 路由断言 Factory: 根据请求路径匹配的路由条件工厂
1 | |
Weight 路由断言 Factory: 请求按照权重分组到不同的路由
默认的路由转发如果路由到了两个,则是按照配置先后顺序转发,
- 如果没有配置权重,则肯定是转发到 uri。
- 如果配置了权重并且相同的分组,则按照权重比例进行分配流量。
datetime 路由断言 Factory: 在指定日期时间(After、Before、Between)发生的请求都将被匹配
1 | |
Cookie 路由断言 Factory: 有两个参数,cookie名称和正则表达式。请求包含此cookie名称且正则表达式为真的将会被匹配。
1 | |
Header 路由断言 Factory: 有两个参数,header名称和正则表达式。请求包含此header名称且正则表达式为真的将会被匹配。
1 | |
Host 路由断言 Factory: 包括一个参数:host name列表。使用Ant路径匹配规则, . 作为分隔符。
1 | |
Method 路由断言 Factory:只包含一个参数:需要匹配的HTTP请求方式
1 | |
QueryParam 路由断言 Factory:
RemoteAddr 路由断言 Factory:

自定义Predicate
可以自定义Predicate来实现复杂的路由匹配规则。
Spring Cloud Gateway中的断言命名都是有规范的,格式:xxxRoutePredicateFactory。
- 比如权重的断言:
WeightRoutePredicateFactory,那么配置时直接取前面的Weight。
1 | |
filter(过滤器)

名称只需要写前缀,过滤器命名必须是xxxGatewayFilterFactory(包括自定义),如AddResponseHeaderGatewayFilterFactory,为原始响应添加Header。
从生命周期可分为:
- PRE 过滤器:在请求被路由之前调用。可实现身份验证、在集群中选择请求的微服务、记录调试信息等。
- POST 过滤器:在路由到微服务以后执行。可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
从作用范围可分为两种:
- GatewayFilter(局部过滤器):应用到单个路由或者一个分组的路由上。需要在指定路由配置才能生效。
- GlobalFilter(全局过滤器):应用到所有的路由上。无需配置,全局生效。
GatewayFilter(局部)
Spring Cloud Gateway内置了多种过滤器,例如:
AddRequestHeader GatewayFilter:在请求头中添加参数PrefixPath GatewayFilter:请求路径前缀Hystrix GatewayFilter:断路器RateLimit GatewayFilter:限流Retry GatewayFilter:重试
Spring Cloud Gateway中内置了许多的局部过滤器,如下图:
模拟一个授权验证的过程,如果请求头或者请求参数中携带token则放行,否则直接拦截返回401:
AuthorizeGatewayFilterFactory只是涉及到了过滤器的前置处理,后置处理是在chain.filter().then()中的then()方法中完成的,具体可以看下项目源码中的TimeGatewayFilterFactory。
1 | |
GlobalFilter(全局)
全局过滤器应用到全部路由上,不必在路由上配置,注入到IOC容器中即可全局生效。
功能其实和GatewayFilter是相同的,只是作用域是所有的路由配置,而不是绑定在指定的路由配置上。
- 多个
GlobalFilter可以通过@Order或者getOrder()方法指定执行顺序,order值越小,执行的优先级越高。 - 注意:由于过滤器有pre和post两种类型,
- pre类型过滤器如果order值越小,那么它就应该在pre过滤器链的顶层,最先执行。
- post类型过滤器如果order值越小,那么它就应该在pre过滤器链的底层。最后执行。

Spring Cloud Gateway也内置了一些全局过滤器,如下图:
自定义过滤器
场景:模拟Nginx的Access Log 功能,记录每次请求的相关信息。
集成注册中心、动态路由
网关接收外部请求,按照一定的规则,将请求转发给其他服务或者应用。如果站在服务调用的角度,网关就扮演着服务消费者的角色。
此时,如果再来看看服务调用的目标URI配置,就会很自然的发现一个问题,服务提供者调用的地址是写死的(每次路由配置都是指定固定的服务uri),即网关没有动态的发现服务,缺点:
- 服务的IP的地址一旦修改了,路由配置中的uri必须修改
- 服务集群中无法实现负载均衡
这就涉及到了服务的自动发现问题,以及发现服务后,所涉及到的服务调用的负载均衡的问题。
此时就需要集成注册中心(如 Nacos),使得网关能够从注册中心自动获取uri(负载均衡)。
- 可以通过Nacos或者Eureka注册中心动态发现服务,通过Ribbon进行服务调用的负载均衡。
- 同样,Gateway也可以整合Nacos或者Eureka,Ribbon从而实现动态路由的功能。
想要使用动态路由的功能,首先要整合注册中心,这里以Nacos为例
例如 Eureka:
在 Spring Boot 主类上添加 @EnableDiscoveryClient 注解,以启用服务发现功能。
集成配置中心
将网关的一系列配置写到项目的配置文件中,一旦路由发生改变必须要重新配置项目,这样维护成本很高。
其实可以将网关的配置存放到配置中心中,这样由配置中心统一管理,一旦路由发生改变,只需要在配置中心修改,这样便能达到一处修改,多出生效的目的。
这里当然要使用Nacos作为配置中心了。
在bootstrap.yml文件中指定Nacos作为配置中心的一些相关配置:
1 | |
在nacos中的public命名空间中创建dataId为cloud-gateway.yaml的配置(未指定环境),配置内容如下:

自定义全局异常处理
通过前面的测试可以看到一个现象:一旦路由的微服务下线或者失联了,Spring Cloud Gateway直接返回了一个错误页面,Whitelabel Error Page。
- 显然这种异常信息不友好,前后端分离架构中必须定制返回的异常信息。
传统的Spring Boot 服务中都是使用@ControllerAdvice来包装全局异常处理的,但是由于服务下线,请求并没有到达。
因此必须在网关中也要定制一层全局异常处理,这样才能更加友好的和客户端交互。
Spring Cloud Gateway提供了多种全局处理的方式,今天只介绍其中一种方式,实现还算比较优雅。
直接创建一个类GlobalErrorExceptionHandler,实现ErrorWebExceptionHandler,重写其中的handle方法,返回JSON数据。代码如下:
1 | |
OAuth2.0 认证和授权服务搭建
很多企业是将认证授权服务直接集成到网关中,这么做耦合性太高了,这里直接将认证授权服务抽离出来。
认证服务的搭建这里就不再细说了,上一篇文章中已经介绍的很清楚了:OAuth2.0实战!使用JWT令牌认证!
接口测试

Knife4j API 文档生成工具
请求过滤
请求过滤的方法:
- 封禁攻击者来源 IP
- 拒绝带有非法参数的请求
- 按来源 IP 限流
- 按用户 ID 限流等