• Gateway网关


    1、微服务网关的基本介绍

    不同的微服务一般会有不同的网络地址,客户端在访问这些微服务时必须记住几十甚至几百个地址,这对于客户端方来说太复杂也难以维护,如果让客户端直接与各个微服务通讯,可能会有很多问题:

    1. 客户端会请求多个不同的服务,需要维护不同的请求地址,增加开发难度
    2. 在某些场景下存在跨域请求的问题
    3. 加大身份认证的难度,每个微服务需要独立认证

    因此,我们需要一个微服务网关,介于客户端与服务器之间的中间层。所有的外部请求都会先经过微服务网关,客户端只需要与网关交互,只知道一个网关地址即可。

    网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过网关这一层。也就是说,API 的实现方更多地只需要考虑业务逻辑,而安全、性能、监控可以交由网关来完成,这样既提高业务灵活性又不缺安全性。

    微服务网关是一个服务器,是系统对外的唯一入口。网关封装了系统内部架构,为外部客户端提供一个定制的 API。API 网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP 的访问 API。服务端通过 API-GW 注册和管理服务。

    1.1、使用微服务网关的优点

    网关具有的职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。当然,最主要的职责还是与“外界联系”。

    使用网关的优点:

    1. 安全 。只有网关系统对外进行暴露,微服务可以隐藏在内网,通过防火墙保护。
    2. 易于监控。可以在网关收集监控数据并将其推送到外部系统进行分析。
    3. 易于认证。可以在网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
    4. 减少了客户端与各个微服务之间的交互次数。
    5. 易于统一授权。

    微服务网关就是一个系统,通过暴露该微服务网关系统,方便我们进行相关的鉴权,安全控制,日志统一处理,易于监控的相关功能。

    1.2、常见的网关实现方式

    常见的实现微服务网关的技术有以下:

    1. Nginx。 使用Nginx的反向代理和负载均衡可实现对api服务器的负载均衡及高可用。
    2. zuul。Zuul 是 Netflix 出品的一个基于 JVM 路由和服务端的负载均衡器。Netflix开源,功能丰富,使用JAVA开发,易于二次开发;需要运行在web容器中,如Tomcat。但是缺乏管控,无法动态配置;依赖组件较多;处理Http请求依赖的是Web容器,性能不如Nginx;
    3. spring cloud gateway。gateway 是spring 出品的 基于spring 的网关项目,集成断路器,路径重写,性能比Zuul好。

    2、搭建gateway网关服务

    创建一个以 springboot 作为模板的 gateway 模块,引入nacos服务发现和gateway依赖,如下:

    <!--nacos服务注册发现依赖-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
    <!--网关gateway依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    然后在配置文件 application.yml 中配置服务基本信息、nacos地址、路由等即可。

    路由配置包括:

    1. 路由id:路由的唯一标示
    2. 路由目标(uri):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡
    3. 路由断言(predicates):判断路由的规则
    4. 路由过滤器(filters):对请求或响应做处理

    如下:

    server:
      port: 10010
    spring:
      application:
        name: gateway
      cloud:
        nacos:
          server-addr: localhost:8848 # Nacos地址
        gateway:
          routes:
            - id: user-service   # 路由标示,必须唯一
              # uri: http://127.0.0.1:8081 # 路由的目标地址。也可以通过http的写成固定地址,但不推荐使用
              uri: lb://userservice # 路由的目标地址。lb就是负载均衡的意思,后面跟服务名称
              predicates: # 路由断言,判断请求是否符合规则
                - Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合
            - id: order-service
              uri: lb://orderservice
              predicates:
                - Path=/order/**

    启动该服务,可以在 nacos 注册中心中查看到该服务,如下:

    然后外部服务即可直接通过访问网关来访问微服务,而无需直接访问微服务的地址,如下通过直接访问 http://localhost:10010/order/101 即可访问到 order-service 的服务,网关会自动将匹配到的路由转发至对应的服务中,并且会自动实现负载均衡。

    2.1、路由断言工厂(Route Predicate Factory)

    我们在配置文件中写的断言规则只是字符串,这些字符串会被 Predicate Factory(断言工厂) 读取并处理,转变为路由判断的条件。例如 Path=/user/** 是按照路径匹配,这个规则是由rg.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory 类来处理的,像这样的断言工厂在SpringCloudGateway还有十几个。

    Spring提供了11种基本的Predicate工厂,如下:

    示例:

    spring:
      application:
        name: gateway
      cloud:
        nacos:
          server-addr: localhost:8848 # Nacos地址
        gateway:
          routes:
            - id: order-service
              uri: lb://orderservice
              predicates:
                - Path=/order/**
                - After=2022-04-20T15:35:08.721398+08:00[Asia/Shanghai]

    上面的路由断言意思是必须匹配 /order/ 路径,并且只有在 2022-04-20 日期后才能匹配成功正常访问,必须同时满足条件才能正常访问到服务,否则无法访问,接口会报 404。

    可参考官网:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

    2.2、路由过滤器工厂(GatewayFilter)

    GatewayFilter 是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理。

    Spring提供了31种不同的路由过滤器工厂。例如:

    示例,比如我们通过路由过滤器来配置所有进入某个服务,比如 userservice 服务的请求都自动添加一个请求头:X-Request-red=blue。只需在配置 gateway 服务的配置文件中添加以下配置即可,如下:

    spring:
      application:
        name: gateway
      cloud:
        nacos:
          server-addr: localhost:8848 # Nacos地址
        gateway:
          routes:
            - id: user-service 
              uri: lb://userservice 
              predicates: 
                - Path=/user/** 
              filters: # 过滤器
                - AddRequestHeader=X-Request-red, blue # 添加请求头

    由此,所有通过 gateway 网关发往 userservice 的请求都会自动添加 X-Request-red=blue 请求头,在 userservice 服务中可以通过参数获取到该请求头数据。

    上面的过滤器配置是针对了某个具体的服务,如果要对所有的路由都生效,则可以将过滤器工厂写到 default 下。格式如下:

    spring:
      application:
        name: gateway
      cloud:
        nacos:
          server-addr: localhost:8848 # Nacos地址
        gateway:
          routes:
            - id: user-service 
              uri: lb://userservice 
              predicates: 
                - Path=/user/** default-filters: # 默认过滤器,会对所有的路由请求都生效
            - AddRequestHeader=X-Request-red, blue # 添加请求头

    可参考官网:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

    2.3、全局过滤器(GlobalFilter)

    全局过滤器(GlobalFilter)与 GatewayFilter 的 default-filters 的作用类似,也是处理一切进入网关的请求和微服务响应,对所有路由都生效。区别在于 GatewayFilter 通过配置定义,只能处理一些比较简单的逻辑,而 GlobalFilter 的逻辑通过代码实现,可以自定义复杂逻辑。

    要想实现全局过滤器需要实现 GlobalFilter 接口:

    public interface GlobalFilter {
       /**
        *  处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
        *
        * @param exchange 请求上下文,里面可以获取Request、Response等信息 
        * @param chain 用来把请求委托给下一个过滤器 
        * @return {@code Mono<Void>} 返回标示当前过滤器业务结束   
       */
       Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
    }

    示例,我们定义一个全局过滤器,拦截所有的请求,判断请求的参数中是否有 authorization,并且参数值是否为admin,如果是则放行,否则拦截该请求。

    我们在 gateway 服务中创建一个类,通过该类实现 GlobalFilter 接口即可,如下:

    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.stereotype.Component;
    import org.springframework.util.MultiValueMap;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    @Order(0)
    @Component
    public class AuthorizeFilter implements GlobalFilter{
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            // 1.获取请求参数
            ServerHttpRequest request = exchange.getRequest();
            MultiValueMap<String, String> params = request.getQueryParams();
            // 2.获取参数中的 authorization 参数
            String auth = params.getFirst("authorization");
            // 3.判断参数值是否等于 admin
            if ("admin".equals(auth)) {
                // 4.是,放行
                return chain.filter(exchange);
            }
            // 5.否,拦截
            // 5.1.设置状态码
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);   //这里设置了HttpStatus.UNAUTHORIZED状态码,即401,如果请求的参数不满足时将会返回401
            // 5.2.拦截请求
            return exchange.getResponse().setComplete();
        }
    }

    2.4、过滤器的执行顺序(defaultFilter、路由过滤器、GlobalFilter)

    请求在进入网关后会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter。在请求匹配到路由后,会将 当前路由过滤器、DefaultFilter、GlobalFilter,合并到同一个过滤器链(集合)中,通过比较它们之间的 order 值大小进行排序,值越小越先执行,以此来依次执行每个过滤器。

    • 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
    • GlobalFilter 通过实现 Ordered 接口,或者添加 @Order 注解来指定 order 值;但是路由过滤器和 defaultFilter 的 order 则由 Spring 自动指定,默认是按照声明顺序从1递增,比如 defaultFilter 下有多个过滤器,则从上至下默认从1开始递增,如果 filters 下定义了多个过滤器,同样从上至下默认从1开始递增
    • 当不同类型之间的过滤器的 order 值大小一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter 的顺序执行。

    3、服务网关解决跨域问题

    跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题。网关处理跨域采用的是 CORS 方案,只需要简单配置即可实现,如下:

    spring:
      application:
        name: gateway
      cloud:
        nacos:
          server-addr: localhost:8848 # Nacos地址
        gateway:
          # gateway的全局跨域请求配置
          globalcors:
            add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
            corsConfigurations:
              '[/**]':  # 匹配的路由
                allowedHeaders: "*"  # 允许在请求中携带的头信息
                allowedOrigins: "*"  # 允许哪些网站的跨域请求。也可以通过数组的方式来只指定一些特定的网站
                allowCredentials: true  # 是否允许携带cookie
                allowedMethods: "*"  # 允许的跨域ajax的请求方式。也可以通过数组的方式来只指定一些特定的请求方式
                maxAge: 360000 # 跨域检测的有效期
  • 相关阅读:
    谁说AI看不懂视频?
    为什么说容器的崛起预示着云原生时代到来?
    小熊派开发实践丨漫谈LiteOS之传感器移植
    华为云如何赋能无人车飞驰?从这群AI热血少年谈起
    趣味科普丨一文读懂云服务器的那些事儿
    【API进阶之路】研发需求突增3倍,测试团队集体闹离职
    这个应用魔方厉害了,让软件开发者效率提升10倍
    数据安全无小事:揭秘华为云GaussDB(openGauss)全密态数据库
    数据湖探索DLI新功能:基于openLooKeng的交互式分析
    基本数据类型与表达式5 零基础入门学习Delphi06
  • 原文地址:https://www.cnblogs.com/wenxuehai/p/16262799.html
Copyright © 2020-2023  润新知