当前位置: 代码网 > it编程>编程语言>Java > Spring Cloud系列—Spring Cloud Gateway服务网关的部署与使用指南

Spring Cloud系列—Spring Cloud Gateway服务网关的部署与使用指南

2024年08月02日 Java 我要评论
网关,简而言之,是一个系统的单一入口点,它接收所有外部请求,并根据请求的特性(如路径、HTTP方法等)将其转发给内部的各个微服务。比如说,你想要向一个小区内的一个朋友送信,必须要经过看门大爷的同意,大爷如果认为好人,就会把信带给你要送的人;如果大爷认为你是坏人,就会拦截你。过滤器是用于修改进入网关的请求和发出网关的响应的组件。过滤器的作用范围可以是特定的路由,也可以是全局的。过滤器可以用来添加或修改请求头和响应头,修改请求体,进行权限校验,限流,熔断等。编写过滤器工厂类自定义不是直接实现,而是实现。

gateway网关

1. 网关基本简介

1.1 什么是网关
1.2 为什么需要网关?
  1. 路由:微服务架构通常由多个服务组成,每个服务可能部署在不同的地址和端口上。网关可以根据请求的url或其他条件,将外部请求路由到正确的服务实例上,从而隐藏了服务实例的具体位置。
  2. 负载均衡:当服务有多个实例时,网关可以根据不同的负载均衡策略(如轮询、随机、最少连接等)将请求分发到不同的实例,确保系统资源的有效利用和服务的稳定性。
  3. 统一鉴权和认证:在微服务架构中,每个服务都可能有自己的安全要求。网关可以作为统一的鉴权和认证点,处理所有服务的安全验证,简化了服务之间的安全通信。
  4. 跨域处理:由于浏览器的同源策略,前端应用在访问不同域名或端口的后端服务时可能会遇到跨域问题。网关可以配置cors(跨源资源共享)策略,允许前端应用安全地访问后端服务。
  5. 熔断和限流:网关可以集成熔断和限流机制,当后端服务出现问题时,网关可以快速失败,防止级联故障。同时,限流可以控制对后端服务的请求速率,防止服务过载。
  6. 监控和日志:网关是所有外部请求的入口点,可以在这一层收集和记录重要的监控数据和日志信息,帮助开发者和运维人员快速定位问题和性能瓶颈。

2. 快速搭建gateway网关

基本步骤:

  1. 创建新模块
  2. 引入网关依赖
  3. 编写启动类
  4. 配置路由规则
2.1 创建新模块

创建一个新模块gatewawy

image-20240514150510201

2.2 引入依赖
        <!--网关-->
        <dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-gateway</artifactid>
        </dependency>
        <!--nacos 服务发现-->
        <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-loadbalancer</artifactid>
        </dependency>
2.3 编写启动类
@springbootapplication
public class gatewayapplication {
    public static void main(string[] args) {
        springapplication.run(gatewayapplication.class, args);
    }
}

2.4 配置路由规则

gateway模块:

server:
  port: 8080 
spring:
  application:
    name: gateway-service # 服务名称
  cloud:
    nacos:
      server-addr: 192.168.56.101:8848 # nacos服务注册中心的地址和端口
    gateway:
      routes:
        - id: user # 路由的唯一标识符,自定义,需要保证唯一性
          uri: lb://user-service # 路由的目标服务地址,lb表示使用负载均衡,user-service是注册中心中的服务名
          predicates: # 路由断言,用来判断请求是否符合路由规则
            - path=/users/**,/addresses/** # 路由断言的路径模式,匹配以/users/或/addresses/开头的请求路径

解析:
routes部分,定义了一条路由规则,该规则将所有符合指定路径模式的请求路由到user-service服务。这条规则使用了路径断言path,匹配所有以/users//addresses/开头的请求。通过使用负载均衡lb://前缀,网关能够根据注册中心中的服务实例信息进行负载均衡。

user-service模块:

server:
  port: 8084
spring:
  application:
    name: user-service
  cloud:
    nacos:
      server-addr: 192.168.56.101:8848
2.5 测试

测试接口:

@apioperation("网关测试接口")
@getmapping("/gateway-test")
public string testgateway(){
    return "网关测试成功";
}

nacos:

image-20240514160742494

测试:

image-20240514160300872

3. 路由过滤

在前面的gatewayroutes部分有四个属性可以配置:

  • id:路由的唯一标示
  • predicates:路由断言,其实就是匹配条件
  • filters:路由过滤条件
  • uri:路由目标地址,lb://代表负载均衡,从注册中心获取目标微服务的实例列表,并且负载均衡选择一个访问。

我们在配置文件中写的断言规则只是字符串,这些字符串会被 predicate factory(断言工厂) 读取并处理,转变为路由判断的条件。例如 path=/user/** 是按照路径匹配,这个规则是由org.springframework.cloud.gateway.handler.predicate.pathroutepredicatefactory 类来处理的,这里重点关注路由断言predicates

名称说明示例
after是某个时间点后的请求- after=2037-01-20t17:42:47.789-07:00[america/denver]
before是某个时间点之前的请求- before=2031-04-13t15:14:47.433+08:00[asia/shanghai]
between是某两个时间点之前的请求- between=2037-01-20t17:42:47.789-07:00[america/denver], 2037-01-21t17:42:47.789-07:00[america/denver]
cookie请求必须包含某些cookie- cookie=chocolate, ch.p
header请求必须包含某些header- header=x-request-id, \d+
host请求必须是访问某个host(域名)- host=**.somehost.org,**.anotherhost.org
method请求方式必须是指定方式- method=get,post
path请求路径必须符合指定规则- path=/red/{segment},/blue/**
query请求参数必须包含指定参数- query=name, jack或者- query=name
remoteaddr请求者的ip必须是指定范围- remoteaddr=192.168.1.1/24
weight权重处理

4. 过滤器

4.1 简介
4.2 网关过滤器
image-20240514161937208

如图所示:

  1. 客户端请求进入网关后由handlermapping对请求做判断,找到与当前请求匹配的路由规则(route),然后将请求交给webhandler去处理。
  2. webhandler则会加载当前路由下需要执行的过滤器链(filter chain),然后按照顺序逐一执行过滤器(后面称为**filter**)。
  3. 图中filter被虚线分为左右两部分,是因为filter内部的逻辑分为prepost两部分,分别会在请求路由到微服务之前之后被执行。
  4. 只有所有filterpre逻辑都依次顺序执行通过后,请求才会被路由到微服务。
  5. 微服务返回结果后,再倒序执行filterpost逻辑。
  6. 最终把响应结果返回。

观察得知:如果我们能够定义一个过滤器,在其中实现登录校验逻辑,并且将过滤器执行顺序定义到nettyroutingfilter之前,就能解决前面的问题。

4.2.2 种类

网关过滤器链中的过滤器有两种:

  • gatewayfilter:路由过滤器,作用范围比较灵活,可以是任意指定的路由route.
  • globalfilter:全局过滤器,作用范围是所有路由,不可配置。

下面是一些常用的gateway filterglobal filter

  • addrequestheader:添加请求头。
  • addresponseheader:添加响应头。
  • rewritepath:重写请求路径。
  • prefixpath:为请求路径添加前缀。
  • setpath:设置请求路径。
  • retry:设置重试逻辑。
  • circuitbreaker:集成熔断器。
  • requestratelimiter:限流过滤器。
  • secureheaders:添加安全相关的响应头。
  • tokenrelay:用于将认证信息传递给下游服务。

例如addrequestheadergatewayfilterfacotry,顾明思议,就是添加请求头的过滤器,可以给请求添加一个请求头并传递到下游微服务。

使用的使用只需要在application.yaml中这样配置:

spring:
  cloud:
    gateway:
      routes:
      - id: user
        uri: lb://user-service
        predicates:
          -path=/users/**
        filters:
          - addrequestheader=key, value # 逗号之前是请求头的key,逗号之后是value

如果想要让过滤器作用于所有的路由,则可以这样配置:

spring:
  cloud:
    gateway:
      default-filters: # default-filters下的过滤器可以作用于所有路由
        - addrequestheader=key, value
      routes:
      - id: user
        uri: lb://user-service
        predicates:
          -path=/users/**
4.3 自定义过滤器
4.3.1 自定义gatewayfilter
  1. 编写过滤器工厂gatewayfilterfactory

自定义gatewayfilter不是直接实现gatewayfilter,而是实现abstractgatewayfilterfactory。最简单的方式是这样的:

@component
public class printanygatewayfilterfactory extends abstractgatewayfilterfactory<object> {
    @override
    public gatewayfilter apply(object config) {
        return new gatewayfilter() {
            @override
            public mono<void> filter(serverwebexchange exchange, gatewayfilterchain chain) {
                // 获取请求
                serverhttprequest request = exchange.getrequest();
                // 编写过滤器逻辑
                system.out.println("过滤器执行了");
                // 放行
                return chain.filter(exchange); 
            }
        };
    }
}

注意:该类的名称一定要以gatewayfilterfactory为后缀!!!

  1. 配置路由规则
spring:
  cloud:
    gateway:
      default-filters:
            - printany # 此处直接以自定义的gatewayfilterfactory类名称前缀类声明过滤器

另外,这种过滤器还可以支持动态配置参数,不过实现起来比较复杂,示例:

  1. 编写过滤器工厂gatewayfilterfactory

@component
public class printanygatewayfilterfactory // 父类泛型是内部类的config类型
                extends abstractgatewayfilterfactory<printanygatewayfilterfactory.config> {

    @override
    public gatewayfilter apply(config config) {
        // orderedgatewayfilter是gatewayfilter的子类,包含两个参数:
        // - gatewayfilter:过滤器
        // - int order值:值越小,过滤器执行优先级越高
        return new orderedgatewayfilter(new gatewayfilter() {
            @override
            public mono<void> filter(serverwebexchange exchange, gatewayfilterchain chain) {
                // 获取config值
                string a = config.geta();
                string b = config.getb();
                string c = config.getc();
                // 编写过滤器逻辑
                system.out.println("a = " + a);
                system.out.println("b = " + b);
                system.out.println("c = " + c);
                // 放行
                return chain.filter(exchange);
            }
        }, 100);
    }

    // 自定义配置属性,成员变量名称很重要,下面会用到
    @data
    static class config{
        private string a;
        private string b;
        private string c;
    }
    // 将变量名称依次返回,顺序很重要,将来读取参数时需要按顺序获取
    @override
    public list<string> shortcutfieldorder() {
        return list.of("a", "b", "c");
    }
        // 返回当前配置类的类型,也就是内部的config
    @override
    public class<config> getconfigclass() {
        return config.class;
    }

}
  1. 配置路由规则
spring:
  cloud:
    gateway:
      default-filters:
            - name: printany # 自定义过滤器的名称
              args: # 过滤器传递的参数,手动指定参数名,无需按照参数顺序
                a: 1
                b: 2
                c: 3
4.3.2 自定义globalfilter

自定义globalfilter则简单很多,直接实现globalfilter即可,而且也无法设置动态参数:

@component
public class printanyglobalfilter implements globalfilter, ordered {
    @override
    public mono<void> filter(serverwebexchange exchange, gatewayfilterchain chain) {
        // 编写过滤器逻辑
        system.out.println("未登录,无法访问");
        // 放行
        // return chain.filter(exchange);

        // 拦截
        serverhttpresponse response = exchange.getresponse();
        response.setrawstatuscode(401);
        return response.setcomplete();
    }

    @override
    public int getorder() {
        // 过滤器执行顺序,值越小,优先级越高
        return 0;
    }
}
4.4 跨域问题
4.4.1 什么是跨域问题

同源的定义是:如果两个页面的协议、域名(或ip地址)和端口都相同,则它们具有相同的源。例如,以下两个url具有相同的源:

  • http://example.com:80/index.html
  • http://example.com:80/page.html

而以下url与上面的url不同源:

  • https://example.com:80/index.html (协议不同)
  • http://www.example.com:80/index.html (域名不同)
  • http://example.com:81/index.html (端口不同)

当尝试从不同源的客户端发送请求到服务器时,浏览器会阻止这些请求,除非服务器明确允许跨源请求。这就是所谓的跨域问题。

4.4.2 解决跨域问题

全局cors配置:在网关的配置文件中,可以定义全局的cors配置,这些配置会应用到所有的路由上。

spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: 192.168.56.101:8848
    gateway:
      globalcors:
        add-to-simple-url-handler-mapping: true # 因为ajax发起者会通过options请求来向服务器询问是否允许跨域,所以需要设置为true来解决options请求被拦截问题
        corsconfigurations:
          '[/**]':
            allowedorigins: "http://www.xxx.com" # 允许那些网站跨域访问
            allowedmethods: # 允许的http方法
              - get
              - post
              - put
              - delete
            allowedheaders: "*" # 允许的请求头
            allowcredentials: true # 是否允许发送cookie
            maxage: 3600 # 预检请求的缓存时间(秒)

5. 总结

本文深入探讨了spring cloud gateway网关的核心概念及其在微服务架构中的快速部署方法,除此之外着重讲解了gateway过滤器(filters)的重要性以及相关使用,包括过滤器的种类、自定义过滤器的实现以及过滤器在解决跨域问题中的作用。

至于具体的应用要根据实际开发要求编写,这里就不过多展示了,希望对大家有所帮助。


参考资料:

gateway网关 - wenxuehai - 博客园 (cnblogs.com)

gateway strict-origin-when-cross-origin跨域问题解决 | 少将全栈 (whatled.com)

微服务02

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com