点赞再看,养成习惯,微信搜寻【牧小农】关注我获取更多资讯,风里雨里,小农等你,很快乐可能成为你的敌人。
我的项目源码地址:公众号回复 sentinel,即可收费获取源码
背景
在微服务架构中,通常一个零碎会被拆分为多个微服务,面对这么多微服务客户端应该如何去调用呢?如果没有其余更优办法,咱们只能记录每个微服务对应的地址,别离去调用,然而这样会有很多的问题和潜在因素。
- 客户端屡次申请不同的微服务,会减少客户端代码和配置的复杂性,保护老本比价高。
- 认证简单,每个微服务可能存在不同的认证形式,客户端去调用,要去适配不同的认证,
- 存在跨域的申请,调用链有肯定的绝对复杂性(防火墙 / 浏览器不敌对的协定)。
- 难以重构,随着我的项目的迭代,可能须要从新划分微服务
为了解决下面的问题,微服务引入了 网关 的概念,网关为微服务架构的零碎提供简略、无效且对立的API路由治理,作为零碎的对立入口,提供外部服务的路由直达,给客户端提供对立的服务,能够实现一些和业务没有耦合的专用逻辑,次要性能蕴含认证、鉴权、路由转发、安全策略、防刷、流量管制、监控日志等。
网关在微服务中的地位:
网关比照
- Zuul 1.0 : Netflix开源的网关,应用Java开发,基于Servlet架构构建,便于二次开发。因为基于Servlet外部提早重大,并发场景不敌对,一个线程只能解决一次连贯申请。
- Zuul 2.0 : 采纳Netty实现异步非阻塞编程模型,一个CPU一个线程,可能解决所有的申请和响应,申请响应的生命周期通过事件和回调进行解决,缩小线程数量,开销较小
- GateWay : 是Spring Cloud的一个全新的API网关我的项目,替换Zuul开发的网关服务,基于Spring5.0 + SpringBoot2.0 + WebFlux(基于⾼性能的Reactor模式响应式通信框架Netty,异步⾮阻塞模型)等技术开发,性能高于Zuul
- Nginx+lua : 性能要比下面的强很多,应用Nginx的反向代码和负载平衡实现对API服务器的负载平衡以及高可用,lua作为一款脚本语言,能够编写一些简略的逻辑,然而无奈嵌入到微服务架构中
- Kong : 基于OpenResty(Nginx + Lua模块)编写的高可用、易扩大的,性能高效且稳固,反对多个可用插件(限流、鉴权)等,开箱即可用,只反对HTTP协定,且二次开发扩大难,不足更易用的治理和配置形式
GateWay
官网文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-starter
Spring Cloud Gateway 是Spring Cloud的一个全新的API网关我的项目,目标是为了替换掉Zuul1,它基于Spring5.0 + SpringBoot2.0 + WebFlux(基于⾼性能的Reactor模式响应式通信框架Netty,异步⾮阻塞模型)等技术开发,性能⾼于Zuul,官⽅测试,Spring Cloud GateWay是Zuul的1.6倍 ,旨在为微服务架构提供⼀种简略无效的统⼀的API路由治理⽅式。
- 能够与Spring Cloud Discovery Client(如Eureka)、Ribbon、Hystrix等组件配合应用,实现路由转发、负载平衡、熔断、鉴权、门路重写、⽇志监控等
- Gateway还内置了限流过滤器,实现了限流的性能。
- 设计优雅,容易拓展
基本概念
路由(Route)是GateWay中最根本的组件之一,示意一个具体的路由信息载体,次要由上面几个局部组成:
- id:路由惟一标识,区别于其余的route
- url: 路由指向的目的地URL,客户端申请最终被转发到的微服务
- order: 用于多个Route之间的排序,数值越小越靠前,匹配优先级越高
- predicate:断言的作用是进行条件判断,只有断言为true,才执行路由
- filter: 过滤器用于批改申请和响应信息
外围流程
外围概念:
Gateway Client
向Spring Cloud Gateway
发送申请- 申请首先会被
HttpWebHandlerAdapter
进行提取组装成网关上下文 - 而后网关的上下文会传递到
DispatcherHandler
,它负责将申请分发给RoutePredicateHandlerMapping
RoutePredicateHandlerMapping
负责路由查找,并依据路由断言判断路由是否可用- 如果过断言胜利,由
FilteringWebHandler
创立过滤器链并调用 - 通过特定于申请的
Fliter
链运行申请,Filter
被虚线分隔的起因是Filter能够在发送代理申请之前(pre)和之后(post)运行逻辑 - 执行所有pre过滤器逻辑。而后进行代理申请。收回代理申请后,将运行“post”过滤器逻辑。
- 处理完毕之后将
Response
返回到Gateway
客户端
Filter过滤器:
- Filter在pre类型的过滤器能够做参数效验、权限效验、流量监控、日志输入、协定转换等。
- Filter在post类型的过滤器能够做响应内容、响应头的批改、日志输入、流量监控等
核心思想
当用户发出请求达到 GateWay
之后,会通过一些匹配条件,定位到真正的服务节点,并且在这个转发过程前后,进行一些细粒度的管制,其中 Predicate(断言) 是咱们的匹配条件,Filter 是一个拦截器,有了这两点,再加上URL,就能够实现一个具体的路由,核心思想:路由转发+执行过滤器链
这个过程就好比考试,咱们考试首先要找到对应的考场,咱们须要晓得考场的地址和名称(id和url),而后咱们进入考场之前会有考官查看咱们的准考证是否匹配(断言),如果匹配才会进入考场,咱们进入考场之后,(路由之前)会进行身份的注销和考试的科目,填写考试信息,当咱们考试实现之后(路由之后)会进行签字交卷,走出考场,这个就相似咱们的过滤器
Route(路由) :构建网关的根底模块,由ID、指标URL、过滤器等组成
Predicate(断言) :开发人员能够匹配HTTP申请中的内容(申请头和申请参数),如果申请断言匹配贼进行路由
Filter(过滤) :GateWayFilter的实例,应用过滤器,能够在申请被路由之前或者之后对申请进行批改
框架搭建
通过上述解说曾经理解了根底概念,咱们来入手搭建一个GateWay
我的项目,来看看它到底是如何运行的
新建我的项目:cloud-alibaba-gateway-9006
版本对应:
GateWay属于SprinigCloud且有web依赖,在咱们导入对应依赖时,要留神版本关系,咱们这里应用的版本是 2.2.x的版本,所以配合应用的Hoxton.SR5
版本
在这里咱们要留神的是引入GateWay肯定要删除spring-boot-starter-web依赖,否则会有抵触无奈启动
父类pom援用:
<spring-cloud-gateway-varsion>Hoxton.SR5</spring-cloud-gateway-varsion> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud-gateway-varsion}</version> <type>pom</type> <scope>import</scope></dependency>
子类POM援用:
<dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> <version>2.2.5.RELEASE</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency></dependencies>
yml配置
server: port: 9006spring: application: name: cloud-gateway-service cloud: nacos: discovery: server-addr: localhost:8848 gateway: discovery: locator: enabled: false #开启注册核心路由性能 routes: # 路由 - id: nacos-provider #路由ID,没有固定要求,然而要保障惟一,倡议配合服务名 uri: http://localhost:9001/nacos-provider # 匹配提供服务的路由地址 lb://示意开启负载平衡 predicates: # 断言 - Path=/mxn/** # 断言,门路相匹配进行路由
咱们在之前的cloud-alibaba-nacos-9001
我的项目中增加上面测试代码
@RestController@RequestMapping("/mxn")public class DemoController { @Value("${server.port}") private String serverPort; @GetMapping(value = "/hello") public String hello(){ return "hello world ,my port is :"+serverPort; } }
启动Nacos、cloud-alibaba-nacos-9001
、cloud-alibaba-gateway-9006
通过gateway网关去拜访9001的mxn/order看看。
首先咱们在Nacos中看到咱们服务是注册到Nacos中了
而后咱们拜访http://localhost:9001/mxn/hello
,确保是胜利的,在通过http://localhost:9006/mxn/hello
去拜访,也是OK,阐明咱们GateWay搭建胜利,咱们进入下一步
在上述办法中咱们是通过YML去实现的配置,GateWay
还提供了另外一种配置形式,就是通过代码的形式进行配置,@Bean
注入一个 RouteLocator
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;@Configurationpublic class GateWayConfig { /* 配置了一个id为path_mxn的路由规定 当拜访地址http://localhost:9999/mxn/** 就会转发到http://localhost:9001/nacos-provider/mxn/任何地址 */ @Bean public RouteLocator gateWayConfigInfo(RouteLocatorBuilder routeLocatorBuilder){ // 构建多个路由routes RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes(); // 具体路由地址 routes.route("path_mxn",r -> r.path("/mxn/**").uri("http://localhost:9001/nacos-provider")).build(); // 返回所有路由规定 return routes.build(); }}
咱们能够将路由正文掉之后看一下,重启9006服务,拜访地址http://localhost:9006/mxn/hello
就能够转发到9001中具体的接口中
这里并不举荐,应用代码的形式来进行配置gateWay
,大家有个理解就能够,因为代码的配置保护的老本比拟高,而且对于一些须要批改的项,须要改代码才能够实现,这样不利于保护和拓展,所以还是举荐大家应用yml进行配置。
GateWay负载平衡
在上述的解说中,咱们曾经把握了 GateWay
的一些根本配置和两种应用形式,上面咱们就来解说一下 GateWay
如何实现负载平衡
咱们只须要在9006中增加lb://nacos-provider
就能够显示负载平衡。
当咱们去拜访http://localhost:9006/mxn/hello
的时候,就能够看到9001和9002不停的切换
Predicate 断言
在这一篇中咱们来钻研一下 断言 ,咱们能够了解为:当满足条件后才会进行转发路由,如果是多个,那么多个条件须要同时满足
在官网提供的断言品种有11种(最新的有12种类型):
具体地址:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories
- After:匹配在指定日期工夫之后产生的申请。
- Before:匹配在指定日期之前产生的申请。
- Between:须要指定两个日期参数,设定一个工夫区间,匹配此工夫区间内的申请。
- Cookie:须要指定两个参数,别离为name和regexp(正则表达式),也能够了解Key和Value,匹配具备给定名称且其值与正则表达式匹配的Cookie。
- Header:须要两个参数header和regexp(正则表达式),也能够了解为Key和Value,匹配申请携带信息。
- Host:匹配以后申请是否来自于设置的主机。
- Method:能够设置一个或多个参数,匹配HTTP申请,比方GET、POST
- Path:匹配指定门路下的申请,能够是多个用逗号分隔
- Query:须要指定一个或者多个参数,一个必须参数和一个可选的正则表达式,匹配申请中是否蕴含第一个参数,如果有两个参数,则匹配申请中第一个参数的值是否合乎正则表达式。
- RemoteAddr:匹配指定IP或IP段,符合条件转发。
- Weight:须要两个参数group和weight(int),实现了路由权重性能,依照路由权重抉择同一个分组中的路由
1. After : 示意配置工夫之后才进行转发
工夫戳获取代码,用于工夫代码的获取:
public static void main(String[] args) { ZonedDateTime zbj = ZonedDateTime.now();//默认时区 System.out.println(zbj); }
spring: application: name: cloud-gateway-service cloud: nacos: discovery: server-addr: localhost:8848 gateway: discovery: locator: enabled: true #开启注册核心路由性能 routes: # 路由 - id: nacos-provider #路由ID,没有固定要求,然而要保障惟一,倡议配合服务名 uri: lb://nacos-provider # 匹配提供服务的路由地址 lb://示意开启负载平衡 predicates: # 断言 - Path=/mxn/** # 断言,门路相匹配进行路由 - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在这个工夫之后的申请够能够进行通过,之前的则不能进行拜访
如果在时间段之前拜访则404
Before
匹配ZonedDateTime
类型的工夫,示意匹配在指定日期工夫之前的申请,之后的申请则回绝404谬误
predicates: # 断言 - Path=/mxn/** # 断言,门路相匹配进行路由# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在这个工夫之后的申请够能够进行通过,之前的则不能进行拜访 - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]
Between
Between
能够匹配ZonedDateTime
类型的工夫,由两个ZonedDateTime
参数组成,第一个参数为开始工夫,第二参数为完结工夫,逗号进行分隔,匹配在指定的开始工夫与完结工夫之内的申请,配置如下:
predicates: # 断言 - Path=/mxn/** # 断言,门路相匹配进行路由# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在这个工夫之后的申请够能够进行通过,之前的则不能进行拜访# - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai] - Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai]
Cookie
由两个参数组成,别离为name(Key)
和regexp(正则表达式)(Value
),匹配具备给定名称且其值与正则表达式匹配的Cookie。
路由规定会通过获取Cookie name值和正则表达式去匹配,如果匹配上就会执行路由,如果匹配不上则不执行。
predicates: # 断言 - Path=/mxn/** # 断言,门路相匹配进行路由# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在这个工夫之后的申请够能够进行通过,之前的则不能进行拜访# - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]# - Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai] - Cookie=muxiaonong,[a-z]+ # 匹配Cookie的key和value(正则表达式)示意任意字母
小写字母匹配胜利:
数字匹配不胜利:
Header
由两个参数组成,第一个参数为Header名称
,第二参数为Header的Value值
,指定名称的其值和正则表达式相匹配的Header的申请
predicates: # 断言 - Path=/mxn/** # 断言,门路相匹配进行路由# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在这个工夫之后的申请够能够进行通过,之前的则不能进行拜访# - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]# - Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai]# - Cookie=muxiaonong,[a-z]+ # 匹配Cookie的key和value(正则表达式)示意任意字母 - Header=headerName, \d+ # \d示意数字
申请头携带数字断言申请胜利,
断言字母匹配失败:
Host
匹配以后申请是否来自于设置的主机。
predicates: # 断言 - Path=/mxn/** # 断言,门路相匹配进行路由# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在这个工夫之后的申请够能够进行通过,之前的则不能进行拜访# - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]# - Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai]# - Cookie=muxiaonong,[a-z]+ # 匹配Cookie的key和value(正则表达式)示意任意字母# - Header=headerName, \d+ # \d示意数字 - Host=**.muxiaonong.com #匹配以后的主机地址收回的申请
满足Host断言,申请胜利
不满足Host断言失败
Method
能够设置一个或多个参数,匹配HTTP申请,比方POST,PUT,GET,DELETE
predicates: # 断言 - Path=/mxn/** # 断言,门路相匹配进行路由# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在这个工夫之后的申请够能够进行通过,之前的则不能进行拜访# - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]# - Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai]# - Cookie=muxiaonong,[a-z]+ # 匹配Cookie的key和value(正则表达式)示意任意字母# - Header=headerName, \d+ # \d示意数字# - Host=**.muxiaonong.com #匹配以后的主机地址收回的申请 - Method=POST,GET
GET断言胜利
PUT断言申请失败
Query
由两个参数组成,第一个为参数名称(必须),第二个为参数值(可选-正则表达式),匹配申请中是否蕴含第一个参数,如果有两个参数,则匹配申请中第一个参数的值是否合乎第二个正则表达式。
predicates: # 断言 - Path=/mxn/** # 断言,门路相匹配进行路由# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在这个工夫之后的申请够能够进行通过,之前的则不能进行拜访# - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]# - Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai]# - Cookie=muxiaonong,[a-z]+ # 匹配Cookie的key和value(正则表达式)示意任意字母# - Header=headerName, \d+ # \d示意数字# - Host=**.muxiaonong.com #匹配以后的主机地址收回的申请# - Method=POST,GET - Query=id,.+ # 匹配任意申请参数,这里如果须要匹配多个参数,能够写多个- Query=
断言匹配 申请胜利
RemoteAddr
参数由CIDR 表示法(IPv4 或 IPv6)字符串组成,也就是匹配的ID地址,配置如下:
predicates: # 断言 - Path=/mxn/** # 断言,门路相匹配进行路由# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在这个工夫之后的申请够能够进行通过,之前的则不能进行拜访# - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]# - Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai]# - Cookie=muxiaonong,[a-z]+ # 匹配Cookie的key和value(正则表达式)示意任意字母# - Header=headerName, \d+ # \d示意数字# - Host=**.muxiaonong.com #匹配以后的主机地址收回的申请# - Method=POST,GET# - Query=id,.+ # 匹配任意申请参数,这里如果须要匹配多个参数,能够写多个Query - RemoteAddr=192.168.1.1/24
RemoteAddr
须要两个参数group和weight(int)权重数值,实现了路由权重性能,示意将雷同的申请依据权重跳转到不同的uri地址,要求group的名称必须统一
routes: # 路由 - id: weight_high #路由ID,没有固定要求,然而要保障惟一,倡议配合服务名 uri: https://blog.csdn.net/qq_14996421 predicates: # 断言 - Weight=groupName,8 - id: weight_low #路由ID,没有固定要求,然而要保障惟一,倡议配合服务名 uri: https://juejin.cn/user/2700056290405815 predicates: # 断言 - Weight=groupName,2
间接拜访http://localhost:9006/
能够看到咱们申请的地址成8/2比例交替显示, 80% 的流量转发到https://blog.csdn.net/qq_14996421,将约 20% 的流量转发到https://juejin.cn/user/2700056290405815
Predicate就是为了实现一组匹配规定,让申请过去找到对应的Route进行解决。如果有多个断言则全副命中后进行解决
GateWay Filter
路由过滤器容许批改传入的HTTP申请或者返回的HTTP响应,路由过滤器的范畴是特定的路由.
Spring Cloud GateWay 内置的Filter生命周期有两种:pre(业务逻辑之前)、post(业务逻辑之后)
GateWay自身自带的Filter分为两种: GateWayFilter(繁多)、GlobalFilter(全局)
GateWay Filter提供了丰盛的过滤器的应用,繁多的有32种,全局的有9种,有趣味的小伙伴能够理解一下。
官网参考网址:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters
StripPrefix
StripPrefix 在咱们以后申请中,通过规定值去掉某一部分地址,比方咱们有一台服务中退出了一个前端nacos-provider
想要通过这个去拜访,咱们在我的项目cloud-alibaba-nacos-9001
中退出 context-path
server: port: 9001 servlet: context-path: /nacos-provider
当初9001的拜访门路变为http://localhost:9001/nacos-provider/mxn/hello
,然而如果咱们通过网关去拜访门路就会变成http://localhost:9006/mxn/nacos-provider/mxn/hello
这个时候咱们通过这个门路去拜访是拜访不胜利的,想要解决这个办法,这个就用到了咱们FIlter
中的 StripPrefix
routes: # 路由 - id: nacos-provider #路由ID,没有固定要求,然而要保障惟一,倡议配合服务名 uri: lb://nacos-provider predicates: # 断言 - Path=/mxn/** # 匹配对应地址 filters: - StripPrefix=1 # 去掉地址中的第一局部
咱们重新启动9006我的项目,再去拜访
自定义Filter
尽管Gateway给咱们提供了丰盛的内置Filter,然而理论我的项目中,自定义Filter的场景十分常见,因而独自介绍下自定义FIlter的应用。
想要实现GateWay自定义过滤器,那么咱们须要实现GatewayFilter接口和Ordered接口
@Slf4j@Componentpublic class MyFilter implements Ordered, GlobalFilter { /** * @param exchange 能够拿到对应的request和response * @param chain 过滤器链 * @return 是否放行 */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //获取第一个参数 String id = exchange.getRequest().getQueryParams().getFirst("id"); //打印以后工夫 log.info("MyFilter 以后申请工夫为:"+new Date()); //判断用户是否存在 if(StringUtils.isEmpty(id)){ log.info("用户名不存在,非法申请!"); //如果username为空,返回状态码为407,须要代理身份验证 exchange.getResponse().setStatusCode(HttpStatus.PROXY_AUTHENTICATION_REQUIRED); // 后置过滤器 return exchange.getResponse().setComplete(); } return chain.filter(exchange); } /** * 设定过滤器的优先级,值越小则优先级越高 * @return */ @Override public int getOrder() { return 0; }}
当咱们拜访http://localhost:9006/mxn/nacos-provider/mxn/hello
申请,没有携带ID参数,申请失败
当咱们拜访http://localhost:9006/mxn/nacos-provider/mxn/hello?id=1
申请,申请胜利
总结
到这里咱们的GateWay
就解说完了,对于GateWay的外围点次要有三个Route\Predicate\Filter
,咱们搞懂了这三点,基本上对于GateWay
的常识就把握的差不多了,GateWay外围的流程就是:路由转发+执行过滤器链,如果对文中有疑难的小伙伴,欢送留言探讨。
创作不易,如果文中对你有帮忙,记得点赞关注,您的反对是我创作的最大能源。
我是牧小农,怕什么真谛无穷,进一步有进一步的欢喜~