SpringCloud集成微服务API网关Gateway(详解)

07-14 915阅读

引言:

SpringCloud集成微服务API网关Gateway(详解)
(图片来源网络,侵删)

SpringCloud Alibaba 系列目录

一、搭建SpringCloud工程,请前往:保姆级教程构建SpringCloud工程(图文结合)

二、集成服务注册配置管理中心Nacos,请前往:SpringCloud集成服务注册配置管理中心Nacos

三、集成HTTP客户端工具OpenFeign,请前往:SpringCloud集成HTTP客户端工具OpenFeign

四、集成统一网关SpringCloud Gateway,请前往:SpringCloud集成微服务API网关Gateway(详解)

集成统一网关Gateway

1.为什么需要网关和其作用?

  • 路由转发: 网关可以作为所有客户端请求的入口,根据请求的URL路径将请求路由到相应的微服务实例。通过网关,可以将客户端请求分发到不同的微服务中,实现了微服务之间的解耦。
  • 负载均衡: 网关可以对请求进行负载均衡,将请求分发到多个相同服务的实例中。这样可以提高系统的性能和可用性,并且有效地利用了系统资源。
  • 安全认证: 网关可以作为安全认证的入口,对请求进行身份验证、授权和安全检查。通过网关,可以集中管理系统的安全策略,保护微服务免受未经授权的访问。
  • 流量控制: 网关可以实现流量控制进行限流,限制对微服务的访问速率,防止突发流量对系统造成影响。可以基于请求频率、并发连接数等指标对流量进行控制和限制。
  • 监控和日志: 网关可以记录所有的请求和响应信息,并且可以进行统一的监控和日志记录。这样可以方便开发人员进行故障排查、性能优化和系统分析。

    2.技术实现

    在SpringCloud中网关的实现有两种:

    • Gateway 实现
    • Zuul 实现

      Zuul是基于Servlet的实现,属于阻塞式编程。

      SpringCloud Gateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。

      3. 搭建网关服务

      1. 创建新的 cloud-gateway Module,引入SpringCloud Gateway的依赖及Nacos的服务注册与发现依赖

         
                
                
                    org.springframework.cloud
                    spring-cloud-starter-gateway
                
                
                
                    com.alibaba.cloud
                    spring-cloud-starter-alibaba-nacos-discovery
                
            
        
      2. 在Gateway 的 resources 下新建 bootstrap.yml,配置路由和Nacos 服务注册与发现中心

        这里如果创建的是bootstrap.yml的话,一定要引入 bootstrap的依赖,不然是不会进行识别和加载的,也可以不引入 bootstrap依赖,直接创建application.yml也是可以的

                
                
                    org.springframework.cloud
                    spring-cloud-starter-bootstrap
                
        

        网关路由配置的内容包括:

        • 路由Id:唯一的路由标识
        • uri:目标路由地址,支持http和lb两种
        • predicates:路由断言,判断请求是否符合规则,符合路由规则在进行转发到目标路由地址
        • filters:路由过滤器,处理请求或者响应
          # 服务端口
          server:
            port: 10717
          spring:
            application:
              # 服务名称
              name: gateway
            profiles:
              active: dev
            main:
              #   Description:
              #   Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway.
              #   Action:
              #   Please set spring.main.web-application-type=reactive or remove spring-boot-starter-web dependency.
              #上述错误表明 SpringBoot 与 SpringCloud Gateway 不兼容, 设置属性为reactive 告诉Spring Boot使用响应式Web应用程序类型,与Spring Cloud Gateway兼容。
              web-application-type: reactive  #
            cloud:
              nacos:
                # Nacos服务地址
                server-addr: localhost:8848
                # Nacos的服务注册与发现
                discovery:
                  # Nacos服务地址
                  #        server-addr: localhost:8848
                  # 配置集群名称,也就是机房位置,例如:HZ 杭州
                  cluster-name: HZ
                  # Nacos可视化命名空间ID
                  namespace: cdd2a801-3280-4476-8362-d46318aac1a3
              gateway:
                routes: # 网关路由配置
                  - id: orderservice # 路由ID,自定义填写,唯一即可
                    # uri 有两种配置方法,一种 http ,一种 lb:服务名称
                    #          uri: http://127.0.0.1:18088/  # 路由目标地址
                    uri: lb://orderservice # 路由的目标地址 lb 就是负载均衡,后边跟服务名称
                    predicates: #路由断言 ,白话就是判断请求符不符合路由规则条件
                      - Path=/orderservice/** # 这个是按照路径匹配,只要以 /orderservice/ 开头就符合条件
                  - id: paymentservice
                    uri: lb://paymentservice
                    predicates:
                      - Path=/paymentservice/**
          
        • 新建GatewayApplication

             import org.springframework.boot.SpringApplication;
          import org.springframework.boot.autoconfigure.SpringBootApplication;
          /**
           * @author waves
           * @date 2024/6/4 15:53
           */
          @SpringBootApplication
          public class GatewayApplication {
              public static void main(String[] args) {
                  SpringApplication.run(GatewayApplication.class,args);
                  System.out.println("Gateway网关服务启动成功 ヾ( ̄ー ̄)X(^▽^)ゞ");
              }
          }
          

          启动网关服务,查看是否正常启动。

      4.路由断言工厂

      路由断言工厂 Route Predicate Factory

      • 在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断条件
      • 在SpringCloud Gateway 断言工厂有十几个,Spring 提供了十一种基本的Predicate工厂
      • 例如 - Path=/paymentservice/** 是按照路径匹配,这个规则是lorg.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来处理的

        5.Spring 提供了十一种基本的Predicate工厂

        名称说明示例
        After是某个时间点后的请求- After=2024-01-20T19:41:47.789-07:00[America/Denver]
        Before是某个时间点之前的请求- Before=2024-04-13T19:14:47.433+08:00[Asia/Shanghai]
        Between是某两个时间点之前的请求- Between=2023-01-20T17:42:47.789-07:00[America/Denver], 2024-10-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权重处理

        6.GatewayFilter 路由过滤器

        GatewayFilter是SpringCloud Gateway 网关中提供的一种过滤器,对进入网关的请求和服务响应进行处理。

        1. 过滤器的作用是什么?
          1. 对服务的路由请求或响应进行处理,例如添加请求头,添加前缀路径等
          2. 配置在某个服务路由下的过滤器只对当前路由的请求生效
        2. default-filters 默认过滤器的作用?
          1. 对配置在网关的所有路由都生效的过滤器

        1.Spring 提供了31种不同的路由过滤器工厂。

        如下:

        名称说明
        AddRequestHeaderGatewayFilterFactory用于添加请求头。
        AddResponseHeaderGatewayFilterFactory用于添加响应头。
        AddRequestParameterGatewayFilterFactory用于添加请求参数。
        AddRequestParameterGatewayFilterFactory用于添加响应参数。
        AddResponseHeaderGatewayFilterFactory用于添加请求参数。
        AddRequestParameterGatewayFilterFactory用于添加响应参数。
        PrefixPathGatewayFilterFactory用于添加前缀路径。
        PreserveHostHeaderGatewayFilterFactory用于保留主机头。
        RemoveNonProxyHeadersGatewayFilterFactory用于删除非代理头。
        RemoveRequestHeaderGatewayFilterFactory用于删除请求头。
        RemoveResponseHeaderGatewayFilterFactory用于删除响应头。
        RequestRateLimiterGatewayFilterFactory用于请求速率限制。
        RetryGatewayFilterFactory用于请求重试。
        SecureHeadersGatewayFilterFactory用于添加安全头。
        SetPathGatewayFilterFactory用于设置路径。
        SetPathGatewayFilterFactory用于设置路径。
        SetRequestHeaderGatewayFilterFactory用于设置请求头。
        SetResponseHeaderGatewayFilterFactory用于设置响应头。
        SetStatusGatewayFilterFactory用于设置状态码。
        RedirectToGatewayFilterFactory用于重定向。
        RewritePathGatewayFilterFactory用于重写路径。
        RewriteLocationResponseHeaderGatewayFilterFactory用于重写响应头中的位置。
        SaveSessionGatewayFilterFactory用于保存会话。
        MapRequestHeaderGatewayFilterFactory用于映射请求头。
        MapResponseHeaderGatewayFilterFactory用于映射响应头。
        MapRequestParameterGatewayFilterFactory用于映射请求参数。
        MapResponseHeaderGatewayFilterFactory用于映射响应参数。
        StripPrefixGatewayFilterFactory用于删除前缀。
        AddResponseHeaderGatewayFilterFactory用于删除前缀。
        AddRequestParameterGatewayFilterFactory用于删除前缀。
        AddRequestParameterGatewayFilterFactory用于删除前缀。

        2.测试AddRequestHeader

        以AddRequestHeaderGatewayFilterFactory 为例:给进入orderservice 的请求添加一个请求头 Authorization = token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA

        在Gateway的 bootstrap.yml 文件中,在orderservice 的配置下添加路由过滤器

         spring:
           cloud:
            gateway:
              routes: # 网关路由配置
                - id: orderservice # 路由ID,自定义填写,唯一即可
                  # uri 有两种配置方法,一种 http ,一种 lb:服务名称
                  #          uri: http://127.0.0.1:18088/  # 路由目标地址
                  uri: lb://orderservice # 路由的目标地址 lb 就是负载均衡,后边跟服务名称
                  predicates: #路由断言 ,白话就是判断请求符不符合路由规则条件
                    - Path=/orderservice/** # 这个是按照路径匹配,只要以 /orderservice/ 开头就符合条件
                  # 路由过滤器
                  filters:
                    # 对进入 orderservice 的请求添加 请求头
                    - AddRequestHeader=Authorization,token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA
        

        在代码中测试是否能获取到请求头

            /**
             * 测试获取gateway中添加的 Authorization
             * @param request
             * @return
             */
            @GetMapping("/getToken")
            public ResponseResult getToken(HttpServletRequest request){
                return ResponseResult.success("操作成功",request.getHeader("Authorization"));
            }
            
        返回结果,成功获取到token
        http://localhost:10717/orderservice/system/order/getToken
        {
          "code": 100,
          "data": "token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA",
          "message": "操作成功"
        }
        

        3.默认过滤器

        如果要对所有的路由都添加过滤器,可以把过滤器工厂配置在default下。如下配置:

        spring:
          application:
            # 服务名称
            name: gateway
          profiles:
            active: dev
          cloud:
            gateway:
              routes: # 网关路由配置
                - id: orderservice # 路由ID,自定义填写,唯一即可
                  # uri 有两种配置方法,一种 http ,一种 lb:服务名称
                  #          uri: http://127.0.0.1:18088/  # 路由目标地址
                  uri: lb://orderservice # 路由的目标地址 lb 就是负载均衡,后边跟服务名称
                  predicates: #路由断言 ,白话就是判断请求符不符合路由规则条件
                    - Path=/orderservice/** # 这个是按照路径匹配,只要以 /orderservice/ 开头就符合条件
                  # 路由过滤器
                  filters:
                    # 对进入 orderservice 的请求添加 请求头
                    - AddRequestHeader=Authorization,token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA
                - id: paymentservice
                  uri: lb://paymentservice
                  predicates:
                    - Path=/paymentservice/**
                # 默认过滤器,会对所有的路由请求都生效
              default-filters:
                  - AddRequestHeader=Authorization,token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA
        

        代码测试,paymentservice服务未单独配置添加请求头,进行测试

            /**
             * 测试获取gateway中添加的 Authorization
             * @param request
             * @return
             */
            @GetMapping("/getToken")
            public ResponseResult getToken(HttpServletRequest request){
                return ResponseResult.success("操作成功",request.getHeader("Authorization"));
            }
            
        返回结果,成功获取到token
        http://localhost:10717/paymentservice/payment/getToken
        {
          "code": 100,
          "data": "token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA",
          "message": "操作成功"
        }
        

        4.全局过滤器

        全局过滤器 GlobalFilter 的作用 与 Gateway Filter 的作用一样。处理一切进入网关的请求和服务的响应。

        两个的区别在于GatewayFilter是通过配置定义,处理逻辑是不可变的。而GlobalFilter的逻辑需要开发人员自己写代码进行实现。方式是实现GlobalFilter接口

        public interface GlobalFilter {
        	/**
        	 * Process the Web request and (optionally) delegate to the next {@code WebFilter}
        	 * through the given {@link GatewayFilterChain}.
        	 * @param exchange the current server exchange
        	 * @param chain provides a way to delegate to the next filter
        	 * @return {@code Mono} to indicate when request processing is complete
        	 * 
             *  处理Web请求并(可选地)通过给定的GatewayFilterChain委托给下一个WebFilter。
             *  Params: exchange-请求上下文-提供了一种委托给下一个过滤器的方法	
             *  返回:Mono指示请求处理何时完成
        	 *
        	 */
        	Mono filter(ServerWebExchange exchange, GatewayFilterChain chain);
        }
        

        自定义过滤器实现代码:

        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(-1) // 优先级 配置方式有两种,一种直接加 @Order(-1) 注解,另一种实现Order接口
         * @author waves
         * @date 2024/6/5 14:36
         */
        @Component
        public class AuthorizeFilter implements GlobalFilter, Ordered {
            private static final String STATIC_TOKEN = "token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA";
            @Override
            public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                //1.获取请求中的参数
                ServerHttpRequest request = exchange.getRequest();
                MultiValueMap params = request.getQueryParams();
                //2.获取请求中的Authorization 参数值
                String authToken = params.getFirst("Authorization");
                //3.判断token是否合法
                if (STATIC_TOKEN.equals(authToken)){
                    //4.合法,进行放行
                    return chain.filter(exchange);
                }
                //5.不合法,直接拦截,并返回状态码
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                //拦截请求
                return exchange.getResponse().setComplete();
            }
            @Override
            public int getOrder() {
                return -1;
            }
        }
        

        测试:

        错误请求:
        http://localhost:10717/paymentservice/payment/getToken?Authorization=1234
        返回:
        This page isn’t workingIf the problem continues, contact the site owner.
        HTTP ERROR 401
        正确请求:
        http://localhost:10717/paymentservice/payment/getToken?Authorization=token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA
        返回:
        {"code":100,"data":"token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA","message":"操作成功"}
        

        5.过滤器的执行顺序

        当请求进入到网关会经过三类过滤器:针对配置的路由过滤器(filters:)、默认过滤器(default-filters:)、全局过滤器(GlobalFilter)

        请求进入网关路由后,会将当前路由过滤器和默认过滤器,GlobalFilter合并到一个过滤器链(集合)中,进行排序后依次执行

        当请求进入时

        请求 --> 路由 --> 默认过滤器 --> 服务路由过滤器 --> 全局过滤器 --> 微服务

        响应 --> 全局过滤器 --> 服务路由过滤器 --> 默认过滤器 --> 路由

        1.自定义GlobalFilter 通过添加@Order注解指定order值,或者实现 Ordered接口 返回order的值。

        2.每一个过滤器都必须指定一个int类型的order值,值越小,优先级就越高,执行顺序就越靠前。

        3.路由过滤器和默认过滤器的order由spring指定,默认是按照声明顺序从1递增。

        4.当过滤器的order值都一样的时候,会按照 默认过滤器 > 路由过滤器 > 全局过滤器 的顺序执行。

        具体可以参考如下几个类的源码进行查看:

        org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()方法是先加载defaultFilters,然后再加载某个route的filters,然后合并。
        org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()方法会加载全局过滤器,与前面的过滤器合并后根据order排序,组织过滤器链
        

        7.跨域问题处理

        什么是跨域:

        当一个网页的源(origin)与所请求资源的源不一致时产生的问题。

        跨域包括:

        1. 域名不同: 例如 www.baidu.com 和 fanyi.baidu.com
        2. 域名相同但端口不同:localhost:8088 和 localhost:18088

        原因:

        跨域时浏览器会执行同源策略(Same-Origin Policy),限制页面中的JavaScript代码只能与其同源的资源进行交互,否则的话请求就会被拦截

        解决方案:

        1. 使用CORS(跨域资源共享):服务端设置合适的 CORS 头部,允许特定的源访问资源。通过设置 Access-Control-Allow-Origin 头部,服务器可以允许指定源的请求访问资源。
        2. 代理:在同源的服务器上设置代理,将跨域请求转发到目标服务器,然后将响应返回给客户端。这种方法可以绕过同源策略限制,但需要在服务器端进行额外的配置。
        3. 反向代理:在服务器端配置反向代理,将客户端请求发送到目标服务器,并将响应返回给客户端。这种方法可以隐藏目标服务器的真实地址,同时解决跨域问题。
        4. JSONP(JSON with Padding):JSONP 是通过动态添加 标签实现跨域请求的一种方法。由于 标签的跨域特性,可以通过在请求 URL 中添加回调函数名来获取数据,并在响应中返回 JSON 数据。
        5. iframe:通过在页面中嵌入 iframe,将目标页面作为 iframe 的 src,通过 window.postMessage() 方法进行通信,可以实现跨域通信。
        6. WebSocket:WebSocket 提供了一种在不同源之间进行双向通信的方式,可以用于跨域通信。

        这里网关使用CORS解决 跨域问题,并且在Gateway中只需要简单配置即可实现:

        spring:
          cloud:
            gateway:
              # 全局跨域处理
              globalcors:
                # 解决options(预检请求)请求被拦截问题,默认是拦截
                add-to-simple-url-handler-mapping: true
                cors-configurations:
                  '[/**]':
                    allowedOrigins: # 允许哪些网站的跨域请求
                      - "http://localhost:10717"
                      - "http://localhost:8088"
                      - "https://localhost:8099"
                    allowedMethods: #允许跨越的Ajax请求
                      - "GET"
                      - "POST"
                      - "DELETE"
                      - "PUT"
                      - "OPTIONS"
                    allowedHeaders: "*" # 允许在请求头中携带头信息
                    allowCredentials: true # 是否允许携带Cookie
                    maxAge: 360000 # 跨域检测的有效时间
        
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]