一 业务:多个微服务的应用可能部署在不同机房,不同地区,不同域名下。
二 需求:统一入口、路由、权限认证......
三 解决方案:
1 集群与架构(11)
(1)逻辑
(2)物理
图1
图2
2 功能“集”:网关
3 “神”工具:spring cloud gateway
四 完成学习
1 体系组织
图1
图2
- 基于:Spring Framework 5、Spring Boot 2.0、WebFlux框架(reactor-netty、Netty、Loop线程)、Project Reactor
- 集成:Hystrix 断路器、Spring Cloud DiscoveryClient
- 包含:Predicates 、 Filters链
- 提供:网关的高级功能:
- 跨域请求问题(jf-驾驶舱项目)
- 服务降级
- 熔断
- 路由
- 负载均衡
网关 = 路由转发 + 过滤器(编写额外功能)
简单说明一下三个术语:
- Filter(过滤器):
- 过滤器将会对请求和响应进行处理。
- 在Gateway运行过程中Filter负责在代理服务“之前”或“之后”去做一些事情。
- 所有生效的Filter都是GatewayFilter的实例
-
- Route(路由):
- 接收外界请求,通过网关的路由转发,转发到后端的服务上。
- 一个Route模块由一个 ID,一个目标 URI,一组断言和一组过滤器定义组成。
- ID是自定的
-
URI就是一个地址
-
Predicate过滤器
-
Filter过滤器
- 如果断言为真,则说明请求的 URI 和配置匹配,目标URI会被访问。
- 注意1:Zuul的路由配置模块类似。
- 注意2:只讨论路由功能的话,它和nginx反向代理服务器很像(外界访问nginx,由nginx做负载均衡,后把请求转发到对应服务器上)。
- Predicate(断言、谓词)
- 简单点理解谓词就是一些附加条件和内容,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。
- 断言的输入类型是一个 ServerWebExchange。
2 思想
- 类比应用的防火墙:系统的统一入口,主要起到隔离外部访问与内部系统的作用。
- 目标是替代 Zuul:基于servlet之上的一个阻塞式处理模型、高并发下会崩溃宕机
- Spring Cloud Gateway和Zuul最主要的区别,还是在底层的通信框架上。
3 原理
- 因为spring cloud gateway是一个单独、独立运行的服务器,所以它是一个中间件。
- 单体应用:浏览器发起请求到单体应用所在的机器,应用从数据库查询数据原路返回给浏览器,对于单体应用来说是不需要网关的。
- 微服务:微服务的应用可能部署在不同机房,不同地区,不同域名下。此时客户端(浏览器/手机/软件工具)想要请求对应的服务,都需要知道机器的具体 IP 或者域名 URL,当微服务实例众多时,这是非常难以记忆的,对于客户端来说也太复杂难以维护。此时就有了网关,所有外部请求率先经过微服务网关,客户端只需要与网关交互(只需要知道网关地址即可),然后由网关根据请求标识解析判断出具体的微服务地址,再把请求转发到微服务实例。这其中的记忆功能就全部交由网关来操作了。
4 流程
- 第一步:客户端向 Spring Cloud Gateway 发出请求。
- 第二步:请求首先会被HttpWebHandlerAdapter进行提取组装成网关上下文
- 第三步:Gateway中Handler Mapping映射中找到与请求相匹配的路由。
- 第四步:将路由发送到网关 Web 处理程序 Gateway Web Handler。
- 第五步:Gateway Web Handler 会被Filter进行过滤。
- 第六步:Gateway 基于 Filter 链的方式提供了网关基本的功能,Filter中前半部分代码是处理请求的代码。
- 第七步:处理完成后调用真实被代理的服务,被代理服务响应结果。
- 第八步:结果会被Filter中后半部分代码进行操作。
- 第九步:原路返回:
- 操作完成后把结果返回给Gateway Web Hanlder,
- 在返回给Gateway Handler Mapping,
- 最终响应给客户端。
5 实现技术
- 基于:Spring Framework 5、Spring Boot 2.0、WebFlux框架(reactor-netty、Netty、Loop线程)、Project Reactor
- 集成:Hystrix 断路器、Spring Cloud DiscoveryClient
- 包含:Predicates 、 Filters链
6 具体1:常用网关解决方案
- redis
- Nginx + Lua
- kong
- Traefik
- Spring Cloud Netflix Zuul
- Spring Cloud Gateway
7 具体2:高可用spring cloud gateway
为了保证 Gateway 的高可用性,可以同时启动多个 Gateway 实例进行负载,在 Gateway 的上游使用 Nginx 或者 F5 进行负载转发以达到高可用。
8 只官配play:nginx实现API网关【乐字节】
- 下载nginx:学习,windows版
- 安装nginx:解压,运行根路径下的 nginx.exe
- 测试nginx:Nginx 默认端口为 80,访问:http://localhost:80/ 看到下图说明安装成功:
- 配置路由规则:
进入 Nginx 的 conf 目录,打开 nginx.conf 文件,配置路由规则: - 测试
访问:http://localhost/api-product/product/1 结果如下:
访问:http://localhost/api-order/order/1 结果如下:
9 只官配play:动路由 = gateway + eureka【sxt】
- 第一步:注册中心服务端项目Eureka Server
- 动态路由其实就是面向服务的路由
- Spring Cloud Gateway 支持与 Eureka 整合开发
- Gateway整合Eureka,需要根据 serviceId 自动从Eureka中获取真实的服务地址,并转发请求,在进行访问。
- 这样做的好处不仅可以通过单个端点来访问应用的所有服务,而且在添加或移除服务实例时不用修改 Gateway 的路由配置。
- 第二步:DemoOne(项目名:demo-one),注册中心客户端项目Eureka Client
- 配置文件:
spring:application:name: demo-one
- 控制器:
@Controller public class DemoController {@RequestMapping("/one")@ResponseBodypublic String demo(){System.out.println("执行one控制器");return "one";}@RequestMapping("/demo/one")@ResponseBodypublic String demo2(){System.out.println("执行demo-one控制器");return "demo-one";} }
- 配置文件:
- 第三步:GatewayDemo(项目名:gateway-demo),网关项目
- pom.xml:
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.5.RELEASE</version> </parent> <dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR3</version><type>pom</type><scope>import</scope></dependency></dependencies> </dependencyManagement> <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>
- 配置文件:
谓词中Path一般会给每个项目前面起一个无意义的别名。
如Path = /abc/** 当看到abc是知道路由转发到项目abc,后面的内容才是真实要访问abc的内容
如Path=/jqk/** 表示需要转发到jqk项目。
要明白:uri中第一部分就是为了防止各个项目中出现重名路径导致路由转发失败。server:port: 9000 spring:application:name: gateway-democloud:gateway:discovery:locator:enabled: true # 开启当前项目服务注册与发现功能lower-case-service-id: true # 把服务名转换为小写,Eureka中默认都是大写routes:- id: demo #自定义唯一标识,只要不重复即可uri: lb://EUREKA-CLIENT # lb:loadBalance EUREKA-CLIENT 代理项目的名predicates: Path=/demo/** # 路径规则filters: StripPrefix=1 # 转发后忽略第一层
-
新建启动器:
@SpringBootApplication public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class,args);} }
- pom.xml:
-
第四步:访问GatewayDemo项目。
访问测试:http://localhost:9000/demo/one
页面会显示:
通过结果可以看出,用户虽然访问的GatewayDemo网关项目,但是最后真是访问的是DemoOne项目中/one 控制器
10 只官配play:动路由 = gateway + nacos
(1)集成nacos前:
- 第一步:引入依赖
<!-- gateway网关 --> <!-- 注意:会和spring-webmvc的依赖冲突,需要排除spring-webmvc --> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring‐cloud‐starter‐gateway</artifactId> </dependency>
- 第二步:编写yml配置文件
(2)集成Nacos:现在在配置文件中写死了转发路径的地址, 前面我们已经分析过地址写死带来的问题, 接下来我们从注册中心获取此地址
- 第一步:引入依赖
<!‐‐ nacos服务注册与发现 ‐‐> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring‐cloud‐starter‐alibaba‐nacos‐discovery</artifactId> </dependency>
- 第二步:修改yml配置文件
- 第三步:简写版
(3)测试
四 动态路由
1 路由配置方式
- 方式一:基础URI路由配置方式
如果请求的目标地址,是单个的URI资源路径,配置文件示例如下:server:port: 8080 spring:application:name: api-gatewaycloud:gateway:routes:- id: url-proxy-1 #id:我们自定义的路由 ID,保持唯一uri: https://blog.csdn.net #uri:目标服务地址predicates: #predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。- Path=/csdn #上面这段配置的意思是,配置了一个 id 为 url-proxy-1的URI代理规则,路由的规则为: #当访问地址http://localhost:8080/csdn/1.jsp时,会路由到上游地址https://blog.csdn.net/1.jsp。 #下面示例中 uri指向了我的CSDN博客,在实际项目使用中可以将 uri 指向对外提供服务的项目地址,统一对外输出接口。
- 方式二:基于代码的路由配置方式
转发功能同样可以通过代码来实现,我们可以在启动类 GateWayApplication 中添加方法 customRouteLocator() 来定制转发规则。package com.springcloud.gateway;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean;@SpringBootApplication public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}/**同理: 下面这段配置的意思是,配置了一个 id 为 url-proxy-1的URI代理规则,路由的规则为:当访问地址http://localhost:8080/csdn/1.jsp时,会路由到上游地址https://blog.csdn.net/1.jsp。下面示例中 uri指向了我的CSDN博客,在实际项目使用中可以将 uri 指向对外提供服务的项目地址,统一对外输出接口。*/@Beanpublic RouteLocator customRouteLocator(RouteLocatorBuilder builder) {return builder.routes().route("path_route", r -> r.path("/csdn").uri("https://blog.csdn.net")).build();}}
- 方式三:和注册中心相结合的路由配置方式
在uri的schema协议部分为自定义的lb:类型,表示从微服务注册中心(如zoolkepper、Eureka、nacos、阿波罗)订阅服务,并且进行服务的路由。
注册中心相结合的路由配置方式,与单个URI的路由配置,区别其实很小,仅仅在于URI的schema协议不同。单个URI的地址的schema协议,一般为http或者https协议server:port: 8084 spring:cloud:gateway:routes:- id: seckill-provider-routeuri: lb://seckill-providerpredicates:- Path=/seckill-provider/**- id: message-provider-routeuri: lb://message-providerpredicates:- Path=/message-provider/** application:name: cloud-gateway eureka:instance:prefer-ip-address: trueclient:service-url:defaultZone: http://localhost:8888/eureka/
2 路由匹配规则
(1)生活需求:更加复杂的匹配规则
(2)解决方案:Spring Cloud Gataway 帮我们内置了很多 Predicates 功能、还能使用filter
Spring Cloud Gateway 的功能很强大,我们仅仅通过 Predicates 的设计就可以看出来,前面我们只是使用了 predicates 进行了简单的条件匹配,其实 Spring Cloud Gataway 帮我们内置了很多 Predicates 功能。
Spring Cloud Gateway 是通过 Spring WebFlux 的 HandlerMapping 做为底层支持来匹配到转发路由,Spring Cloud Gateway 内置了很多 Predicates 工厂,这些 Predicates 工厂通过不同的 HTTP 请求参数来匹配,多个 Predicates 工厂可以组合使用。
1)Predicates谓词:当满足条件在进行路由转发。
- 其中Route和Predicate必须同时申明
- 所有的谓词都设置在predicates属性中,
- 当设置多个谓词时取逻辑与条件,
- 且一个谓词只能设置一组条件,如果需要有个多条件,添加多个相同谓词。
- 案例:Path=/demo/** 实际上使用的就是PathRoutePredicateFactory
- 网上有一张图总结了 Spring Cloud 内置的几种 Predicate 的实现:
- 假设 转发uri都设定为http://localhost:9023
2)过滤器规则(Filter)
-
Filter作用:在路由转发到代理服务之前和代理服务返回结果之后额外做的事情。
-
Filter执行了说明谓词条件通过了。
-
Spring Cloud Gateway中Filter分类:
-
内置Filter,都是GatewayFilter实现类
-
自定义GlobalFilter
-
-
GlobalFilter【sxt】:全局过滤器不需要工厂,也不需要配置,直接对所有的路由都生效。
-
自定义FilterFactory【sxt】:可以定义针对于Router的Filter。
- 类名必须叫做XXXGatewayFilterFactory.注入到Spring容器后使用时的名称就叫做XXX。
- 类必须继承AbstractGatewayFilterFactory
- 所有需要传递进来的参数都配置到当前类的内部类Config中
五 熔断降级:Gateway + Hystrix
1 生活问题
请求超时、异常。请求失败堆积于网关。需要快速失败并返回给客户端。不然系统崩溃。
2 解决方案:熔断降级
- Spring Cloud Gateway 可以利用Hystrix实现服务降级等功能。
- 当Gateway进行路由转发时,如果发现下游服务连接超时或产生异常,此时就允许进行服务降级。
- 实现原理:当连接超时时,使用Gateway自己的一个降级接口返回托底数据,保证程序继续运行
六 限流
1 生活问题
顾名思义,限流就是限制流量,就像你宽带包有 1 个 G 的流量,用完了就没了。通过限流,我们可以很好地控制系统的 QPS,从而达到保护系统的目的。
比如 Web 服务、对外 API,这种类型的服务有以下几种可能导致机器被拖垮:
2 解决方案:可以利用Gateway中RequestRateLimiter实现限流。
(1)常见的限流算法
- 计数器算法
- 漏桶算法
- 令牌桶算法
(2)Gateway中限流,RequestRateLimiter是基于Redis和Lua脚本实现的令牌桶算法。
七 健康检查配置:admin-client、actuator
八 统一配置跨域请求【网】【图灵】【乐字节】
现在的请求通过经过gateWay网关时,需要在网关统一配置跨域请求,需求:所有请求通过,有如下配置:
spring:cloud:gateway:globalcors:cors-configurations:'[/**]':allowed-origins: "*"allowed-headers: "*"allow-credentials: trueallowed-methods:- GET- POST- DELETE- PUT- OPTION
九 整合Nacos
十 整合Swagger聚合微服务系统API文档
十一 流控和降级:gateway + Sentinel
十二 参考
SpringCloud gateway (史上最全) - 疯狂创客圈 - 博客园
全网最全讲解 Spring Cloud Gateway,认真看完这一篇就够了!
一篇就看懂:SpringCloud网关 - GateWay详解
SpringCloud Gateway获取body参数(一篇就够了)
Spring Cloud Gateway解决跨域问题【理解跨域】