• Gateway 简介


    概述

    微服务可能分布在不同的主机上,这样有许多缺点:前端需要硬编码调用不同地址的微服务很麻烦;存在跨域访问的问题;微服务地址直接暴露是不安全的。还有所以需要为前端提供一个统一的访问入口。Gateway 就是用于解决以上问题的框架。Gateway 本身是一个微服务,要注册到 Eureka 中。

    入门案例

    1. maven 依赖:
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
    
    1. 启动类添加 @EnableDiscoveryClient 注解
    2. 配置文件:基本的 Eureka 配置 + 路由配置
    server:
      port: 10010
    spring:
      application:
        name: api-gateway
      cloud:
        gateway:
          routes:
            - id: user-service-route # 路由的标识 ID,任意名称都可以
              uri: http://127.0.0.1:9092 # 路由的转发地址
              predicates:
                - Path=/user/** # 路由的映射范围
    eureka:
      client:
        service-url:
          defaultZone: http://127.0.0.1:10086/eureka
      instance:
        prefer-ip-address: true
    
    1. 是否配置成功?

    启动实例后能在 Eureka 注册中心看到注册,http://localhost:10010/user/** 的请求会被转发到 http://localhost:9092/user/**

    动态路由

    入门案例中使用的是静态IP 地址,下面是用服务名称代替具体 IP,这样避免直接使用 IP,并可以添加负载均衡。

    只需要将配置中的 uri 更换为「loadbalance 协议」

    spring:
      application:
        name: api-gateway
      cloud:
        gateway:
          routes:
            - id: user-service-route # 路由的标识 ID,任意名称都可以
              uri: lb://USER-SERVICE # 路由的转发地址
              predicates:
                - Path=/user/** # 路由的映射范围
    

    路由规则

    • 添加前缀:http://localhost:10010/**-->lb://USER-SERVICE/user/**
    spring:
      application:
        name: api-gateway
      cloud:
        gateway:
          routes:
            - id: user-service-route # 路由的标识 ID,任意名称都可以
              uri: lb://USER-SERVICE # 路由的转发地址
              predicates:
                - Path=/** # 路由的映射范围
              filters:
                - PrefixPath=/user
    
    • 去除前缀:http://localhost:10010/api/api/user/**-->lb://USER-SERVICE/user/**
    spring:
      application:
        name: api-gateway
      cloud:
        gateway:
          routes:
            - id: user-service-route # 路由的标识 ID,任意名称都可以
              uri: lb://USER-SERVICE # 路由的转发地址
              predicates:
                - Path=/api/api/user/** # 路由的映射范围
              filters:
                - StripPrefix=2 # 去除两层前缀
    

    过滤器

    前面修改请求其实都是通过 Gateway 的过滤器实现的,上面用到的过滤器为 StripPrefixGatewayFilterFactory 产生的过滤器。Gateway 提供了几十种过滤器,过滤器可以过滤指定规则的请求,也可以过滤所有请求。

    spring:
      cloud:
        gateway:
          default-filters:
            - AddResponseHeader=this-is-a-header, Have-Fun
    

    这个过滤器可以为所有请求添加一个响应头,this-is-a-header: Have-Fun

    路由器分为局部路由和全局路由,局部路由分为两种(上面展示的两种):

    1. spring.cloud.gateway.routes.- id.filters
    2. spring.cloud.gateway.default-filters (功能等价于全局过滤器)

    全局过滤器:不在配置文件中配置,需要实现 GlobalFilter 接口。

    生命周期

    过滤器类似于拦截器,但是只在微服务处理前后运作,请求转发不会触发过滤器。

    自定义局部过滤器

    自定义过滤器需要实现 AbstractGatewayFilterFactory 抽象类。该类使用内部类接受配置文件中的参数,需要给这个内部类的提供 Getter/Setter 方法。

    package com.example.gateway.filter;
    
    import java.util.Arrays;
    import java.util.List;
    
    import org.springframework.cloud.gateway.filter.GatewayFilter;
    import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.stereotype.Component;
    
    @Component
    public class MyMyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyMyGatewayFilterFactory.Config> {
        public MyMyGatewayFilterFactory() {
            super(Config.class);
        }
    
        // 这个方法指定配置文件注入 Config 的哪一个字段
        public List<String> shortcutFieldOrder() {
            return Arrays.asList("param");
        }
    
        // 由于接受配置文件参数
        public static class Config {
            private String param;
    
            public String getParam() {
                return param;
            }
    
            public void setParam(String param) {
                this.param = param;
            }
    
        }
    
        // 此为过滤器逻辑,返回值是一个过滤方法,可以使用 Lambda 表达式。
        // exchange 参数可以获取请求内容。chain 用于执行请求。
        @Override
        public GatewayFilter apply(Config config) {
            return (exchange, chain) -> {
                // 前置处理
                ServerHttpRequest request = exchange.getRequest();
                if (request.getQueryParams().containsKey(config.param)) {
                    request.getQueryParams().get(config.param)
                            .forEach(o -> System.out.printf("局部过滤器:%s = %s \n", config.param, o));
                }
                return chain.filter(exchange); // 执行请求
            };
        }
    }
    

    这里我们使用配置文件将 "name" 注入到 Config.param 字段上。注意配置中使用该过滤器类的前缀来注入。比如 MyMyGatewayFilterFactory 的前缀为 MyMy。过滤器类的命名格式是固定的,为 XxxxGatewayFilterFactory,需要使用 @Component 注入到 Spring 容器中,泛型使用 Config 内部类。

    spring:
      cloud:
        gateway:
          routes:
            - id: user-service-route # 路由的标识 ID,任意名称都可以
              uri: lb://USER-SERVICE # 路由的转发地址
              predicates:
                - Path=/api/api/user/** # 路由的映射范围
              filters:
                - StripPrefix=2 # 去除两层前缀
                - MyMy=name
          default-filters:
            - AddResponseHeader=this-is-a-header, Have-Fun
    

    自定义全局过滤器

    实现 GlobalFilter, Ordered 接口并注入到 Spring 容器中。

    package com.example.gateway.filter;
    
    import org.apache.commons.lang.StringUtils;
    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.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    
    import reactor.core.publisher.Mono;
    
    @Component
    public class MyGlobalFilter implements GlobalFilter, Ordered {
    
        // 设置执行的优先级,数字越小优先级越高
        @Override
        public int getOrder() {
            return 1;
        }
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            System.out.println("自定义全局过滤器 MyGlobalFilter 执行");
            String token = exchange.getRequest().getHeaders().getFirst("token");
            // 如果没有 token 将响应状态设置为「未验证 401」,并直接返回,不执行后面的操作。
            if (StringUtils.isBlank(token)) {
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
            return chain.filter(exchange);
        }
    }
    

    负载均衡和熔断配置

    Gateway 默认集成了 Ribbon 和 Hystrix。

    hystrix:
      command:
        default:
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 2000 #服务降级超时时间
          circuitBreaker:
            errorThresholdPercentage: 50 # 触发熔断错误比例阈值,默认值50%
            sleepWindowInMilliseconds: 10000 # 熔断后休眠时长,默认值5秒
            requestVolumeThreshold: 10 # 熔断触发最小请求次数,默认值是20
    ribbon:
      ConnectTimeout: 1000 # 连接超时时长
      ReadTimeout: 2000 # 数据通信超时时长
      MaxAutoRetries: 0 # 当前服务器的重试次数
      MaxAutoRetriesNextServer: 0 # 重试多少次服务
      OkToRetryOnAllOperations: false # 是否对所有的请求方式都重试
    

    跨域访问配置

    微服务会面临跨域访问(比如端口不同)的问题,默认情况下服务不允许别的域访问,我们需要在配置中设置「白名单」告知从哪些域来的请求是被允许的。

    spring:
      cloud:
        gateway:
          globalcors:
            corsConfigurations:
              '[/**]': # 哪些请求可以跨域
                allowedOrigins: # 跨域白名单
                  - "http://docs.spring.io"
                allowedMethods: # 允许的请求方式
                  - GET 
    

    一个常见的情况是前后端分离,前端后端部署在不同端口。比如上面配置中 http://docs.spring.io 为前端的域,完成这个配置之后,前端就可以正常使用 Ajax 访问其他域的所有微服务。

    Gateway 高可用

    启动多个 Gateway 实例,实现微服务内部访问的高可用。但是 Gateway 主要适用于直接暴露给客户端,仅内部高可用并不是想要的,这时候需要使用 Nginx 代理 Gateway。

    Gateway 和 Feign 的区别

    Gateway 是作为整体微服务暴露给客户端的访问入口,通常用作权限鉴定和流量控制。Feign 是微服务内部之间互相调用的入口。一句话说就是 Gateway、Feign 一外一内。



    尊重原创,转载请标明出处。

  • 相关阅读:
    项目管理必备的22个公式
    Dreamweaver网页设计与制作100例——HTML5期末考核大作业——票务网站整套网页
    双指针算法(超详细)
    ChatGPT 1.0.0安卓分析,仅限国内分享
    JVM启动参数简介
    突破编程_C++_C++11新特性(function与bind绑定器)
    《动手学深度学习 Pytorch版》 8.5 循环神经网络的从零开始实现
    CSS保持宽高比(aspect-ratio)VS(padding-top)
    HELM 如何使用模板变量部署多个应用
    艾美捷Bio-Helix CCH321 超敏ECL化学发光试剂盒(皮克级)特点
  • 原文地址:https://www.cnblogs.com/dai-blog/p/15929501.html