首页 > 其他分享 >SpringCloud-服务网关-Gateway

SpringCloud-服务网关-Gateway

时间:2024-07-04 08:59:07浏览次数:17  
标签:网关 断言 SpringCloud 过滤器 Gateway 路由 请求

1.服务网关在微服务中的应用

(1)对外提供服务的难题分析:
微服务架构下的应用系统体系很庞大,光是需要独立部署的基础组件就有注册中心、配置中心和服务总线、Turbine异常聚合和监控大盘、调用链追踪器和链路聚合,还有Kaka和MQ之类的中间件,再加上拆分后的零散微服务模块。—个小系统都能轻松弄出20个左右的部署包。
如果采用localhost加端口的方式直接访问,如果这些服务—并都要提供给外部用户访问那该怎么办呢?可以让前端程序员加班加点在各个页面给各种不同请求配置URL和端口号,人不是问题,项目完成就行。也可以我们配一个URL,通过F5或者Nginx可以做路由,话是没错,可是这样就要让运维团队手工维护路由规则表,当我们新增删除节点或者因为更换机房导致IP变化的时候就很麻烦。因此我们需要引入—套机制来降低路由表的维护成本。
还有一个问题就是安全性,我们在提供外部服务的时候往往会加入一些访问控制,比如说下单接口不允许未登录用户的访问,有的服务还会通过一些JWT签名等防止客户端篡改数据。如果让每个服务提供者都实现同样的访问验证逻辑未免有些太繁琐,这样纯属是增加研发人员的怒气值,况且如果有一天我们需要更换权限认证方案,比如更换为OAuth2.0,难不成还要每个服务提供者都做变更?
我们如何对外提供服务,既能管好路由规则,还能做好访问控制呢?在这个背景下,API网关应运而生,接待所有来访请求。
(2)网关层
微服务引入一层专事专办的中间层。
两件事:
1.访问控制,看你是否有权限访问,拒绝未授权的来访者
2.引导指路 问清楚你要办的事,指一条明路。找到对应处理这些事的人

网关层引入后,微服务的架构变成:
在这里插入图片描述
网关层作为唯一的对外服务,外部请求不直接访问服务层。由网关层承接所有HTTP
请求,在实际应用中。我们会将Gateway与Nginx一同使用。
(3)访问控制和路由规则
访问控制:
主要包含两个方面的任务,具体的实现并不是由网关层提供的,但是网关作为一个载体承载了两个任务:
拦截请求:有的接口需要登录用户才能访问,对于这类接口的访问,网关层可以检查访问请求中是否携带令牌等身份信息,比兔HTTP Header中的Authorization或者token属性。如果没有携带令牌,说明没登录,可以直接返回403 Forbidden
鉴权:对于携带令牌的服务,我们需要验证令牌的真假,否则用户可以
通过伪造的令牌进行通信,对令牌校验失败的请求,或者令牌已经过期
的请求执行拒绝服务!

路由规则
路由规则包含两个方面,分别是URL映射和服务寻址
URL映射:在大多数情况下,客户端访问的HTTP URL往往不是我们在Controller里配置的真实路径,比如客户端可以发起请求"/password/update"来修改密码,但后台并没有这个服务,这时候就需要网关层做一个路由规则,将来访URL映射成真正的服务路径,比如将刚才的密码修改请求的路径映射到"/user/settings/security/password"请求
服务寻址URL映射:好了之后,网关层就需要找到可以提供服务的服务器地址,对于服务集群的话,还需要实现负载均衡策略。(在Spring Cloud中,Gateway是借助Eureka的服务发现机制来实现服务寻址的,负载均衡则依靠Ribbon)

2.第二代网关组件Gateway介绍

Gateway业务场景:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.Gateway体系架构解析

(打开Gateway的自动装配工厂,gatewayAutoConfiguration看,第一个就是Netty)

Netty是什么?在网络传输领域Netty就是身份的象征,它是非阻塞、高性能、高可靠的异步输入输出框架,用一个字概括就是"快"。这里我们不对Netty做深入探讨,但是需要了解下Netty在Gateway中主要应用在以下几个地方:
发起服务调用:由NettyRoutingFilter过滤器实现,底层采用基于Netty的HttpClient发起外部服务的调用
Response传输:由NettyResponseFilter过滤器实现,网络请求结束后要将Response回传给调用者

  • Socket连接:具体由ReactortNettyWebSocketClient类承接,通过Netty的Httpclient发起连接请求
    在Gateway中发起Request和回传Response之类的步骤都是通过一系列过滤翮完成的,有关过滤器的内容将在稍后介绍。

(正常HTTP调用与netty的http调用的区别:
核心区别:javax.servlet.http.HttpServletRequest主要用于服务器端的HTTP请求处理,而Netty的HttpClient用于客户端发起HTTP请求)

在这里插入图片描述
Client发起请求到服务网关之后,由NettyRoutingFilter底层的HttpClient(Netty组件)向
服务发起调用,调用结束后,Response有NettyResponseFilter再回传给客户端。有了netty加持,网络请求效率大幅提升!Netty贯穿从Request发起到Response结束的过程,承担了所有网络调用相关的任务

(1)Gateway自动装配:
在这里插入图片描述
AutoConfig: 作为核心自动装配主类,GatewayAutoConfiguration负责初始化所有的Route路由规则、Predicate断言工厂和Filter(包括Global Filter和Route Filter),这三样是Gateway吃饭的家伙,用来完成路由功能。AutoConfig也会同时加载Netty配置
LoadBalancerGlient: 这部分在AutoConfig完成之后由GatewayLoadBalancerClientAutoConfiguration负责加载,用来加载Ribbon和一系列负载均衡配置
ClassPathWarning:同样也是在AutoConfig完成之后触发(具体加载类为GatewayClassPathWarningAutoConfiguration),由于Gateway底层依赖Spring WebFlux的实现,所以它会检查项目是否加载了正确配置
Redis:在Gateway中Redis主要负责限流的功能。
除了上面几个核心装配工厂以外,还有两个打酱油的路人,它们并不直接参与Gateway的核心功能,但是会提供—些重要的支持功能:
GatewayMetricsAutoConfiguration:负责做一些统计工作,比如对所谓的“short task"运行时长和调用次数做统计
GatewayDiscoveryClientAutoConfiguration:服务发现客户端自动装配类

爬坑指南
Gateway项目启动出错,但是查来查去,发现也没有什么配置问题。这时候就要看一下是不是引入了错误的依赖,Gateway比较坑的一个地方是它基于WebFlux实现,因此它需要的依赖是spring-boot-starter-webflux,假如我们不小心引入了spring-boot-starter-web将导致启动问题,
由于我们大部分的Spring Cloud项目都依赖spring-boot-starter-web,所以很容易就误将其依赖导入到了Gateway项目中,碰到这种问题只要打印出依赖树,排查下错误依赖的来源,然后将它在pom中排除出去就好了。
路由流程
这里就涉及到了Gateway最核心的路由功能,路由主要由断言和过滤器配合来实现,我们把这部分内容拆分为3个小节,分别介绍路由的整体功能、断言的使用、过滤器原理和生命周期。

4.路由功能详解

Gateway网关的路由功能可不简简单单的转发请求,在请求到达网关再流转到指定服务之间发生了很多事!它不光可以拒绝请求,甚至可以篡改请求的参数!

一个route包含完整转发规则的路由,主要由一下三部分组成:

在这里插入图片描述
断言集合:断言是路由处理的第一个环节,它是路由的匹配规则,它决定了一个网络请求是否可以匹配给当前路由来处理。之所以它是一个集合的原因是我们可以给一个路由添加多个断言,当每个断言都匹配成功以后才算过了路由的第一关。有关断言的详细内容将在下一小节进行介绍
过滤器集合:如果请求通过了前面的断言匹配,那就表示它被当前路由正式接手了,接下来这个请求就要经过一系列的过滤器集合。过滤器的功能就是八仙过海各显神通了,可以对当前请求做一系列的操作,比如说权限验证,或者将其他非业务性校验的规则提到网关过滤器这一层。在过滤器这一层依然可以通过修改Response里的status Code达到中断效果,比如对鉴权失败的访问请求设置Status Code为403之后中断操作。有关过滤器的详细内容将在后面的小节介绍
URI:如果请求顺利通过过滤器的处理,接下来就到了最后一步,那就是转发请求。URI是统一资源标识符,它可以是一个具体的网址,也可以是IP+端口的组合,或者是Eureka中注册的服务名称

关于负载均衡:
对最后一步寻址来说,如果采用基于Eureka的服务发现机制,那么在Gateway的转发过程中可以采用服务注册名的方式来调用,后台会借助Ribbon实现负载均衡(可以为某个服务指定具体的负载均衡策略),其配置方式如:1b://FEIGN-SERVICE-PROVIDER/。前面的lb就是指代Ribbon作为LoadBalancer,

路由的规则流程:
在这里插入图片描述
Predicate Handler:具体承接类是RoutePredicateHandlerMapping。首先它获取所有的路由(配置的routes全集),然后依次循环每个Route,把应用请求与Route中配置的所有断言进行匹配,如果当前Route所有断言都验证通过,Predict Handler就选定当前的路由。这个模式是典型的职责链。
Filter Handler:在前一步选中路由后,由FilteringWebHandler将请求交给过滤器,在具体处理过程中,不仅当前Route中定义的过滤器会生效,我们在项目中添加的全局过滤器(Global Filter)也会一同参与。同学们看到图中有Pre Filter和Post Filter,这是指过滤器的作用阶段,我们在稍后的章节中再深入了解
寻址:这一步将把请求转发到URI指定的地址,在发送请求之前,所有Pre类型过滤器都将被执行,而Post过滤器会在调用请求返回之后起作用。

5.断言功能详解(Predict)

Predicate机制:
Predicate是Java 8中引入的一个新功能,就和我们平时在项目中写单元测试时用到的Assertion差不多,Predicate接收一个判断条件,返回一个ture或false的布尔值结果,告知调用方判断结果。你也可以通过and (与),or(或)和negative (非)三个操作符将多个Predicate串联在一块共同判断。

如果Gateway是挡在微服务前面的中介,那这个Predicate就是和中介的接头暗号。比如中介可以要求你的Request中必须带有某个指定的参数叫name,对应的值必须是一个指定的信息,如果你的Request中没有包含指定信息,或者指定信息错误,那就是断言失败。只有当你的请求完全和接头暗号匹配的时候,中介才能给你放行。

说白了predicate就是一种路由规则,通过gateway中丰富内置断言的组合,
我们就能让一个请求找到对应的route来处理。

断言的作用阶段:
在一个请求抵达网关层后,首先就要进行断言匹配,在满足所有断言之后
才会进入Filter阶段!

常用断言介绍:gateway提供了十多种内置断言:
路径匹配:path断言是最常用一个断言。
.route(r -> r.path(“/gateway/**”)
.uri(“lb://FEIGN-SERVICE-PROVIDER/”)
)
.route(r -> r.path(“/baidu”)
.uri(“http://baidu.com:80/”)
)
Path断言的使用非常简单,就像我们在Controller中配置@RequestPath的方式一样,在Path断言中填上—段URL匹配规则,当实际请求的URL和断言中的规则相匹配的时候,就下发到该路由中URI指定的地址,这个地址可以是一个具体的HTTP地址,也可以是Eureka中注册的服务名称。在上面的例子中,如果我们访问"Igateway/test”,这个路径将匹配到第一个路由。

Method断言:
这个断言是专门验证HTTP Method的,在下面的例子中,我们把Method断言和Path断言通过一个and连接符合并起来,共同作用于路由判断,当我们访问"lgateway/sample"并且HTTP Method是GET的时候,将适配下面的路由
.route(r -> r.path(“/gateway/“)
.and().method(HttpMethod.GET)
.uri(“lb://FEIGN-SERVICE-PROVIDER/”)
)
RequestParam匹配:请求断言也是业务中经常使用的,它会从ServerHttpRequest中
的Parameters列表中查询指定的属性,如下有两种不同的使用方式:

.route(r -> r.path(”/gateway/
”)
.and().method(HttpMethod.GET)
.and().query(“name”, “test”)
.and().query(“age”)
.uri(“lb://FEIGN-SERVICE-PROVIDER/”)
)
属性名验证:如query(“age”),此时断言只会验证QueryPrameters列表中是否包含了一个叫age的属性,并不会验证它的值
属性值验证:如query ( "“name” ,“test”),它不仅会验证name属性是否存在,还会验证它的值是不是和断言相匹配,比如当前的断言会验证请求参数中的name属性值是不是test,第二个参数实际上是一个用作模式匹配的正则表达式

**Header断言:这个断言会检查Header中是否包含了响应的属性,通常可以用来
验证请求是否携带了令牌:
.route(r -> r.path("/gateway/
")
.and().header(“Authorization”)
.uri(“lb://FEIGN-SERVICE-PROVIDER/”)
)
上面的断言指定Header中必须包含一个Authorization属性,Header断言和Query断言
一样,也可以通过传入两个参数形式对属性值进行检查

Cookie断言:
顾名思义,Cookie验证的是Cookie中保存的信息,Cookie断言和上面介绍的两种断言使用方式大同小异,唯一的不同是它必须连同属性值一同验证,不能单独只验证属性是否存在,示例如下:

.route(r -> r.path(“/gateway/**”)
.and().cookie(“name”, “test”)
.uri(“lb://FEIGN-SERVICE-PROVIDER/”)
)

时间片匹配:
时间匹配有三种模式,分别是Before、After和Between,这些断言指定了在什么时间范围内路由才会生效
.route(r -> r.path(“/gateway/**”)
.and().before(ZonedDateTime.now().plusMinutes(1))
.uri(“lb://FEIGN-SERVICE-PROVIDER/”)
)

自定义断言:
Gateway也提供了一个扩展方法,用来将自定义的断言应用到路由上。老师给出两点提示,希望同学们顺着这个方向来参考Gateway的源码,实现一个自定义断言,完成一个小功能:将所有请求参数大于5个的访问请求拦截掉,即RequestParam个数小于5个的请求才能被放行。
提示1:所有断言类都可以继承自AbstractRoutePredicateFactory
提示2:在路由配置时可以通过predicate或者asyncPredicate传入一个自定义断言

6.过滤器原理和生命周期

过滤器的工作模式:
Gateway的过滤器是一样的模型,他们经过优先级排序,所有网关调用请求从最高优先级的过滤器开始,一路走到头,直到被最后一个过滤器处理。

过滤器的实现方式:
在Gateway实现一个过滤器非常简单,只要实现GatewayFilter接口的默认方法就好!

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    // 随意发挥
    return chain.filter(exchange);    
}

这里面有两个关键信息:
ServerWebExchange:这是Spring封装的HTTP request-response交互协议,从中我们可以获取request和response中的各种请求参数,也可以向其中添加内容
GatewayFilterChain:它是过滤器的调用链,在方法结束的时候我们需要将exchange对象传入调用链中的下一个对象

过滤器的执行阶段:
不同于springcloud中上一代网关组件Zuul里对过滤器的Pre和Post的定义,
Gateway是通过Filter中的代码来实现类似Pre和Post的效果!
Pre和Post是指当代过滤器执行阶段,Pre是在下一个过滤器之前被执行,Post
是在过滤器执行过后再执行。我们在GatewayFileter也可以同时定义Pre和Post执行逻辑!!

Pre类型:
AddResponseHeaderGatewayFilterFactory,它可以向Response中添加Header信息:

@Override
public GatewayFilter apply(NameValueConfig config) {
	return (exchange, chain) -> {
        exchange.getResponse().getHeaders().add(config.getName(), config.getValue());
        return chain.filter(exchange);
    };
}

Post类型:
SetStatusGatewayFilterFactory,它在过滤器执行完毕之后,将制定的HTTP status返回给调用方!!

return chain.filter(exchange).then(Mono.fromRunnable(() -> {
		// 这里是业务逻辑
		}));   

这个过滤器的主要逻辑在then方法中,then是一个回调函数,在下级调用链路都完成以后再执行,因此这类过滤器可以看做是Post Filter

过滤器排座次:
在Gateway中我们可以通过实现org.springframework.core.Ordered接口,来给过滤器指定执行顺序,比如下面的代码实现了Ordered接口方法,将过滤器执行顺序设置为0:

@Override
public int getOrder() {
	return 0;
}                                                          
Pre

类型的过滤器来说,数字越大表示优先级越高,也就越早被执行。但对于Post类型的过滤器,则是数字越小越先被执行。

过滤器示例:
Header过滤器
这个系列有很多组过滤器,AddRequestHeader和AddResponseHeader,分别向Request和Response里加入指定Header。相应的RemoveRequestHeader和RemoveResponseHeader分别做移除操作,用法也很简单:

.filters(f -> f.addResponseHeader("who", "gateway-header"))
                                             

上面的例子会向header中添加一个who的属性,对应的值是gateway-heade

StringPrefix过滤器:
这是个比较常用的过滤器,它的作用是去掉部分URL路径。比如我们的过滤器配置如下:

.route(r -> r.path("/gateway-test/**")
             .filters(f -> f.stripPrefix(1))
             .uri("lb://FEIGN-SERVICE-PROVIDER/")
)        

假如HTTP请求访问的是/gateway-test/sample/update,如果没有StripPrefix过滤器,那么转发到FEIGN-SERVICE-PROVIDER服务的访问路径也是一样的。当我们添加了这个过滤器之后,Gateway就会根据“stripPrefix(1)”中的值截取URL中的路径,比如这里我们设置的是1,那么就去掉一个前缀,最终发送给后台服务的路径变成了“/sample/update”

PrefixPath过滤器:
它和StripPrefix的作用是完全相反的,会在请求路径的前面加入前缀

.route(r -> r.path("/gateway-test/**")
             .filters(f -> f.prefixPath("go"))
             .uri("lb://FEIGN-SERVICE-PROVIDER/")
)      

比如说我们访问“/gateway-test/sample”的时候,上面例子中配置的过滤器就会把请求发送到“/go/gateway-test/sample”。

RedirectTo过滤器:
可以把收到特定的状态码的请求重定向到一个特定的网址:
.filters(f -> f.redirect(302, “https://www.xxx.com/”))
上面的例子接收HTTP status code和URL两个参数,如果请求结果是404,则重定向到第二个参数指定的页面,这个功能也可以做统一异常处理,将Unauthorized或Forbidden请求重定向到登录页面。

标签:网关,断言,SpringCloud,过滤器,Gateway,路由,请求
From: https://blog.csdn.net/TOMORROW6COME/article/details/140091980

相关文章

  • SpringCloud Alibaba Nacos 配置动态更新源码学习总结
    众所周知,nacos两大核心功能,服务注册发现与动态配置支持服务注册发现的有:Eureka、Consul、Zookeeper、Nacos支持动态配置的有:SpringCloudConfig、Nacos、Apollo、Consul像支持分布式的框架,必须得借用第三方服务,比如定时任务调度xxl-job,分布式事务seata,都分为server端与client......
  • Modbus转Profibus主站网关连接温控表通讯技术
    Modbus转ProfibusDP主站网关(XD-MDPBM20)是实现不同通讯协议设备之间联系的重要组件。在工业自动化领域中,温控表是常见且必要的设备,它可以用于监控和调节温度,保障生产过程的稳定性。本文将详细探讨如何利用Modbus转ProfibusDP主站网关(XD-MDPBM20)连接温控表,实现设备间的高效通讯。......
  • Profibus DP主站转Modbus网关连接伺服与电机通讯
    ProfibusDP主站转Modbus网关连接伺服与电机通讯在工业自动化领域,将ProfibusDP主站转Modbus网关(XD-MDPBM20)用于连接伺服与电机通讯是一种常见且重要的应用方式。当使用ProfibusDP主站转Modbus网关(XD-MDPBM20)连接伺服与电机进行通讯时,可以参考以下步骤进行配置和操作:1.什么是Pro......
  • Spring Cloud Gateway整合Knife4j 4.4.0实现微服务聚合文档(报错解决详细版)
    以前做过的都是单服务应用的文档,就算换到了微服务里做的实际上也是单服务(每个服务模块一个单独的文档,然后手动访问不同的端口去查找不同的模块文档,例如用户是3000端口,订单是3100端口,商品是3200端口)。这样的实现实际上挺蠢的,对前端伙伴很不友好,对自己测试也不友好,因此今天要说的......
  • Profibus DP主站转Modbus网关连接智能化电表通讯
    ProfibusDP主站转Modbus网关(XD-MDPBM20),是实现不同工业通信协议之间互联互通的设备,主要将ProfibusDP协议转换为Modbus协议,实现数据的双向传输。通过ProfibusDP主站转Modbus网关(XD-MDPBM20),可以有效实现现场设备和控制系统之间的无缝连接,提高生产效率。ProfibusDP主站转Modbus网......
  • 云原生周刊:Argo Rollouts 支持 Kubernetes Gateway API 1.0 | 2024.7.1
    开源项目KubetoolsRecommenderSystemKubetoolsRecommenderSystem(Krs)是一个基于GenAI的工具,用于帮助管理和优化Kubernetes集群。buoybuoy是Kubernetes的声明式TUI仪表板。你可以在JSON文件中定义仪表板,它将从Kubernetes集群中获取信息并构建仪表板,以便在......
  • springcloud-gateway 网关组件中文文档
      SpringCloud网关GreenwichSR5该项目提供了一个基于Spring生态系统的API网关,其中包括:Spring5,SpringBoot2和项目Reactor。SpringCloud网关的目的是提供一种简单而有效的方法来路由到API,并向它们提供跨领域的关注,例如:安全性,监视/度量和弹性。  如......
  • SpringCloud和Dubbo有什么区别
    SpringCloud与Dubbo的区别两者都是现在主流的微服务框架,但却存在不少差异:初始定位不同: SpringCloud定位为微服务架构下的一站式解决方案;Dubbo是SOA时代的产物,它的关注点主要在于服务的调用和治理生态环境不同: SpringCloud依托于Spring平台,具备更加完善的生态体系;而Du......
  • 工业除尘自动化中的Profibus转Modbus网关应用解析
    一、背景在工业生产过程中,除尘系统扮演着至关重要的角色,它不仅可以保护生产设备,延长设备寿命,还可以减少环境污染,提高生产效率。而Profibus转Modbus网关(XD-MDPB100)作为自动化领域中的关键技术,在除尘自动化控制系统中发挥着重要作用。二、Profibus转Modbus网关优势Profibus转Mod......
  • Gateway 路由(详解)
    Gateway网关的路由功能可不是简简单单的“转发”请求,在请求到达网关再流转到指定服务之间发生了很多事儿,它不光可以拒绝请求,甚至可以“篡改”请求的参数,我们接下来就去看看路由这里面的门道。路由三重门Gateway中可以定义很多个Route,一个Route就是一套包含完整转发规则的路由......