栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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/1
eureka:
  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、编写自定义过滤器类

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

@Component
public 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管理即可。

@Component
public 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 #服务降级超时时间,默认1S
ribbon:
  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将用户服务接口暴露出来,给订单服务使用。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/737316.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号