欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

【SpringCloud组件学习笔记系列】(4)Gateway组件

时间:2023-06-08
SpringCloud逐渐你学习笔记系列

【SpringCloud组件学习笔记系列】(1)Eureka组件
【SpringCloud组件学习笔记系列】(2)Hystrix组件
【SpringCloud组件学习笔记系列】(3)OpenFeign组件
【SpringCloud组件学习笔记系列】(4)Gateway组件
【SpringCloud组件学习笔记系列】(5)Config组件

4、SpringCloud Netflix Gateway的使用以及讲解 4.1、简介

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

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

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

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

Spring Cloud Gateway组件的核心是一系列的过滤器,通过这些过滤器可以将客户端发送的请求转发到对应的微服务。

Spring Cloud Gateway是加载整个微服务最前沿的防火墙和代理器,隐藏微服务节点Ip端口信息,从而加强安全防护。

Spring Cloud Gateway本身也是一个微服务,所以需要将其组测到Eureka服务注册中心去。

SpringCloud Gateway的核心功能是:过滤和路由(转发请求)

不管是来自客户端的请求还是服务内部调用,一切对服务的请求,都可以经过网关,然后网关来实现鉴权、动态路由等操作。Gateway是服务的统一入口。

4.2、核心概念

**路由(route)**路由信息的组成:由一个ID、一个目的URL、一组断言工厂、一组Filter组成。如果路由断言为真,说明请求URL和配置路由匹配。

**断言(Predicate)**Spring Cloud Gateway中的断言函数输入类型是Spring 5.0框架中的 ServerWebExchange。Spring Cloud Gateway的断言函数允许开发者去定义匹配来自于HTTP Request中的任何信息比如请求头和参数。 比如:必须携带某个参数才能访问服务A。

**过滤器(Filter)**一个标准的Spring WebFilter。 Spring Cloud Gateway中的Filter分为两种类型的Filter,分别是Gateway Filter(局部)和Global Filter(全局)。过滤器Filter将会对请求和响应进行修改处理。比如:对所有的请求添加请求头,对所有的请求路径做截取操作。

4.3、Gateway的使用 4.3.1、创建工程模块

引入依赖

org.springframework.cloud spring-cloud-starter-gateway org.springframework.cloud spring-cloud-starter-netflix-eureka-client

4.3.2、编写启动类

@SpringBootApplication@EnableDiscoveryClient // 开启eureka的服务发现public class SpringCloud_Gateway_10010_Application { public static void main(String[] args) { SpringApplication.run(SpringCloud_Gateway_10010_Application.class, args); }}

4.3.3、编写配置文件

server: port: 10010 # 设置端口spring: application: name: api-gateway # 设置服务名称 cloud: gateway: routes: - id: user-service-gateway # 路由服务的id,随便起 uri: http://127.0.0.1:8001 # 将符合断言的请求,转发到这个地址 predicates: # 指定断言的规则,即配置映射路径 - Path=/user/** # 断言的规则, # 如果请求地址为 http://127.0.0.1:10010/user/queryById/1 ,则符合断言规则,这个请求就会被转发到uri指定的服务地址 http://127.0.0.1:8001/user/queryById/1eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka #指定eureka地址 instance: prefer-ip-address: true # 优先使用ip地址

4.3.4、启动服务 4.3.5、测试

输入http://127.0.0.1:10010/user/queryById/1,输出结果

查看控制台打印情况,看Gateway组件是否将请求转发到了8001服务

8001控制台显示,方法被执行了

4.3.6、面向服务的路由

在上面的案例中,路由规则对应的服务地址是写死的,即URI的值,如果在集群模式下,这样的做法显然是不合理的。

应该是根据服务名称,去Eureka注册中心查找该名称对应的所有实例列表,然后进行动态路由。

所以Gateway的处理是,将URI的值,指定为服务名称,并在服务名称前用lb代替http

spring: application: name: api-gateway cloud: gateway: routes: - id: user-service-gateway # uri: http://127.0.0.1:8001 # 将符合断言的请求,转发到这个地址 uri: lb://User-Service # 将上面的地址,更换成服务名称 predicates: - Path=/user/**

替代http的lb表示LoadBalancerClient的意思,gateway会将后面的User-Service通过Eureka解析为实际的主机和端口,并进行Ribbon负载均衡处理。

配置好之后,重启服务,再次测试刚才的请求地址,http://127.0.0.1:10010/user/queryById/1,控制台输出信息

Gateway服务的打印信息中显示采取了Ribbon,表示配置成功了。

4.4、路由前缀

客户端的请求地址与微服务的服务地址如果不一致的时候,可以通过配置路径过滤器实现路径前缀的添加和去除。

添加前缀举例:

http://127.0.0.1:10010/8 - > http://127.0.0.1:8001/user/queryById/8 添加路径前缀为/user/queryById

去除前缀举例:

http://127.0.0.1:10010/api/user/queryById/8 - > http://127.0.0.1:8001/user/queryById/8 去除的路径前缀为/api

这样的处理,也可以有效提升请求的安全性。

4.4.1、添加前缀的配置

改造原来的配置

spring: application: name: api-Gateway cloud: gateway: routes: - id: user-service-gateway uri: lb://User-Service predicates: - Path=/** # 将这里的user去掉,方便测试 filters: - PrefixPath=/user # 添加请求路径的前缀

4.4.2、测试添加前缀的效果

请求中并没有加user这个路径,仍然可以正常访问。因为Gateway在后台按照配置的前缀自动拼接了上去,并调用真实的请求地址获取结果。

4.4.3、实现去除前缀的配置

spring: application: name: api-gateway cloud: gateway: routes: - id: user-service-gateway uri: lb://User-Service predicates: - Path=/** # 此处指定拦截路径 filters: - StripPrefix=1 # 此处指定去除前缀的段数

StripPrefix这个取值为1 表示请求地址为http://127.0.0.1:10010/user/queryById/1时,该请求路径去掉/user这一段,如果为2,则去掉/user/queryById这两段,以此类推

4.4.4、测试去除前缀的效果

可以看到,正常情况下,请求的真实地址应该是:http://127.0.0.1:10010/user/queryById/1,但输入的是http://127.0.0.1:10010/test/user/queryById/1依然可以正确访问到,就是因为去除前缀生效的缘故。

4.5、过滤器 4.5.1、过滤器的介绍

Gateway作为网关组件其中一个重要功能就是实现请求的鉴权,这个功能是需要通过Gateway的过滤器来实现,过滤器也可以实现路由前缀,是Gateway的核心功能。

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

AddRequestHeader : 对匹配上的请求加上HeaderAddRequestParameters :对匹配上的请求路由添加参数AddResponseHeader :对从网关返回的响应添加HeaderStripPrefifix:对匹配上的请求路径去除前缀

进入GatewayFilterFactory接口,可以看到其它暂未介绍的过滤器

这些过滤器在配置文件中的使用是,将去掉后面的GatewayFilterFactory之后的类名称作为参数名称。

具体可以参考PrefixPath以及StripPrefix这两个参数在配置文件以及全类名上的区别。

4.5.2、过滤器类型

过滤器分为局部过滤器以及全局过滤器

局部过滤器:

通过spring.cloud.gateway.routes.filters配置在具体路由下,只作用在当前路由上

通过spring.cloud.gateway.default-filters配置,会对所有路由生效,既是局部过滤器,也是全局过滤器。但这些过滤器的实现都是要实现GatewayFilterFactory接口。

全局过滤器:

不需要在配置文件中配置,作用在所有的路由上,实现GlobalFilter接口即可。后面会详细讲解实现方式。

4.5.3、过滤器的生命周期

Spring Cloud Gateway 的 Filter 的生命周期也类似Spring MVC的拦截器有两个:“pre” 和 “post”。“pre”和 “post” 分别会在请求被执行前调用和被执行后调用

这里的 pre 和 post 可以通过过滤器的 GatewayFilterChain 执行fifilter方法前后来实现

常见的使用场景:

请求鉴权:一般 GatewayFilterChain 执行fifilter方法前,如果发现没有访问权限,直接就返回空。

异常处理:一般 GatewayFilterChain执行fifilter方法后,记录异常并返回。

服务调用时长统计: GatewayFilterChain 执行fifilter方法前后根据时间统计。

4.5.4、默认过滤器

默认过滤器也是一种局部过滤器,只不过所有路由都会起作用。

配置默认过滤器

spring: application: name: api-gateway # 设置服务名称 cloud: gateway: routes: - id: user-service-gateway # 路由服务的id,随便起 # uri: http://127.0.0.1:8001 # 将符合断言的请求,转发到这个地址 uri: lb://User-Service # 将符合断言的请求,转发到这个地址 predicates: # 指定断言的规则,即配置映射路径 - Path=/** # 断言的规则, # 如果请求地址为 http://127.0.0.1:10010/user/queryById/1 ,则符合断言规则,这个请求就会被转发到uri指定的服务地址 http://127.0.0.1:8001/user/queryById/1 filters: - StripPrefix=1 # 这个取值为1 表示请求地址为http://127.0.0.1:10010/user/queryById/1是,去掉/user这一段,如果为2,则去掉/user/queryById这两段,以此类推 default-filters: # 默认过滤器,对所有路由都有效 - AddResponseHeader=zidingyijian,zidingyizhi # 配置添加响应头内容,逗号前是键,逗号后是值

default-filters 用于指定过滤器,AddResponseHeader是添加响应头信息。

测试效果

去除了前缀,添加了响应头。

4.5.5、自定义局部过滤器

实现一个自定义局部过滤器,根据案例学习

需求:

在过滤器(MyParamGatewayFilterFactory) 中将 http://127.0.0.1:10010/api/user/queryById/1?name=zhangsan&name=lisi中的参数name的值,获取到并输出到控制台;

**注意:**参数名是可变的,也就是不一定每次都是name,这需要通过配置过滤器的时候配置参数名。

在application.yml中对某个路由配置过滤器,该过滤器可以在控制台输出配置文件中指定名称的请求参数的值。

4.5.5.1、修改配置文件

spring: application: name: api-gateway # 设置服务名称 cloud: gateway: routes: - id: user-service-gateway # 路由服务的id,随便起 # uri: http://127.0.0.1:8001 # 将符合断言的请求,转发到这个地址 uri: lb://User-Service # 将符合断言的请求,转发到这个地址 predicates: # 指定断言的规则,即配置映射路径 - Path=/** # 断言的规则, filters: - MyParam=name # MyParam是自定义的局部过滤器,全称是MyParamGatewayFilterFactory,它的作用是获取请求中指定参数名的值,并输出到控制台,这个指定参数名就是后面的name - StripPrefix=1 # 这个取值为1 表示请求地址为http://127.0.0.1:10010/user/queryById/1是,去掉/user这一段,如果为2,则去掉/user/queryById这两段,以此类推

filters下添加了MyParam=name, MyParam是MyParamGatewayFilterFactory类的名字。

4.5.5.2、编写自定义过滤器类

这部分内容多,可以着重看注释。

@Componentpublic class MyParamGatewayFilterFactory extends AbstractGatewayFilterFactory { public static final String PARAM_NAME = "name"; //2、提取字符串,方便阅读管理 public MyParamGatewayFilterFactory(){ super(Config.class); // 3、调用父类构造方法,将内部类作为值传递,用于封装根据配置文件中配置的参数名获取到的参数值 } // 4、对请求中获取的值的处理,多个值的情况 public List shortcutFieldOrder(){ return Arrays.asList(PARAM_NAME); // 4.1、对应Config类中变量名,用于封装参数值,因为需求是单个值,就使用单个字符串就行,如果是键值对, } @Override public GatewayFilter apply(MyParamGatewayFilterFactory.Config config) { return ((exchange, chain) -> { // 6、此处可以编写前置过滤器逻辑,此处可以去获取请求中name的值 //6.1、获取请求对象 ServerHttpRequest request = exchange.getRequest(); //6.2、判断请求对象的参数key中,是否包含name if(request.getQueryParams().containsKey(config.name)){ //6.3、如果包含,就获取这个参数名对应的值的集合,注意:这里类型为集合主要是因为可能有多个参数名同名 List names = request.getQueryParams().get(config.name); //6.4、遍历这个值的集合 for (String name : names) { System.out.println("局部过滤器获得参数:"+config.name+"="+name); //6.5、打印出键值对,键为字符串name,值为请求中携带的 } } return chain.filter(exchange); //6.6、执行请求,将请求放行 //6.7、此处可以编写后置过滤器逻辑,当前demo未做其它处理 }); } // 7、需要定义config类,用来为过滤器获取到请求中的参数时做进一步封装 public static class Config{ private String name; // 7.1、用来对应配置文件中MyParam过滤器指定的参数名 public String getName() { return name; } public void setName(String name) { this.name = name; } }}

4.5.5.3、重启服务测试

前端输入地址:

后端打印结果:

4.5.6、自定义全局过滤器

实现一个自定义全局过滤器,根据案例学习

需求:

编写全局过滤器,在过滤器中检查请求中是否携带Token请求头,如果Token请求头存在则放行,如果不存在或为空则设置返回状态码:未授权,不再执行下去

全局过滤器需要实现GlobalFilter

Ordered接口指明了过滤器的执行顺序(优先级),返回的值越小越优先执行。

4.5.6.1、编写自定义全局过滤器

全局过滤器是不需要在配置文件中配置的,只要交给spring管理即可。

@Componentpublic class MyGlobalFilter implements GlobalFilter, Ordered { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 获取第一个名为token的请求头的值 String token = exchange.getRequest().getHeaders().getFirst("token"); // 使用工具类判断是否为空 if(StringUtils.isBlank(token)){ // 为空,设置响应码为UNAUTHORIZED,这是个枚举类,定义了http的各种响应码供使用,此处的UNAUTHORIZED的值是401 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); //为空,也就是请求头没有token,就结束并返回响应码 } // 不为空,就继续执行 return chain.filter(exchange); } @Override public int getOrder() { return 1; }}

4.5.6.2、使用浏览器测试

因为需求中要判断的是请求头的内容,普通的浏览器无法自行添加请求头,所以需要借助工具ApiPost或PostMan去测试。

4.5.6.3、使用ApiPost工具测试:

无token的情况测试:

有token的情况测试:

4.6、负载均衡和熔断

Gatway默认整合了Hystrix以及Ribbon,但是所有的超时策略都是用的默认值。因此,在使用Gateway的负载均衡以及熔断机制时,有需要的地方要自行配置。

不过一般也不使用gateway做负载均衡以及熔断,了解即可。

hystrix: command: default: execution: isolation: timeoutInMilliseconds: 6000 #服务降级超时时间,默认1Sribbon: ConnectTimeout: 1000 # 连接超时时长 ReadTimeout: 2000 # 数据通信超时时长 MaxAutoRetries: 0 # 当前服务器的重试次数 MaxAutoRetriesNextServer: 0 # 重试多少次服务

4.7、跨域配置

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

跨域:在js请求访问中,如果访问的地址与当前服务器的域名、ip或者端口号不一致则称为跨域请求。若不解决则不能获取到对应地址的返回结果。

如:从在http://localhost:9090中的js访问 http://localhost:9000的数据,因为端口不同,所以也是跨域请求。

在访问Spring Cloud Gateway网关服务器的时候,出现跨域问题的话;可以在网关服务器中通过配置解决,允许哪 些服务是可以跨域请求的;具体配置如下:

server: port: 10010 spring: application: name: api-gateway cloud: gateway: routes: # 路由id,可以随意写 - id: user-service-route # 代理的服务地址 uri: lb://user-service # 路由断言,可以配置映射路径 predicates: - Path=/api/user/** filters: # 表示过滤1个路径,2表示两个路径,以此类推 - StripPrefix=1 # 自定义过滤器 - MyParam=name # 默认过滤器,对所有路由都生效 default-filters: - AddResponseHeader=X-Response-Foo, Bar - AddResponseHeader=abc-myname,lxs globalcors: corsConfigurations: '[/**]': allowedOrigins: # 指定允许跨域请求的请求地址,可以单个可以多个,*表示全部 - "http://docs.spring.io" allowedMethods: # 允许跨域请求的请求类型- GET

上述配置表示:可以允许来自 http://docs.spring.io 的get请求方式获取服务数据。

allowedOrigins 指定允许访问的服务器地址,如:http://localhost:10000 也是可以的。 ‘[/**]’ 表示对所有访问到网关服务器的请求地址。

**官网具体说明:**https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/multi/multi__cors_confifiguration.html

4.8、Gateway的高可用

启动多个Gateway服务,自动注册到Eureka,形成集群。如果是服务内部访问,访问Gateway,自动负载均衡,没问题。

但是,Gateway更多是外部访问,PC端、移动端等。它们无法通过Eureka进行负载均衡,那么该怎么办?

此时,可以使用其它的服务网关,来对Gateway进行代理。比如:Nginx

4.9、Gateway与feign的区别

Gateway 作为整个应用的流量入口,接收所有的请求,如PC、移动端等,并且将不同的请求转- 发至不同的处理微服务模块,其作用可视为nginx;

大部分情况下用作权限鉴定、服务端流量控制

Feign 则是将当前微服务的部分服务接口暴露出来,并且主要用于各个微服务之间的服务调用

例如:订单服务访问用户服务,这种内部访问,可以使用feign将用户服务接口暴露出来,给订单服务使用。

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。