• Spring Could Gateway使用


    代码示例

    项目地址,见gateway-example

    Spring Cloud Gateway官方文档

    网关作用

    网关可以为整个系统提供一个统一的入口,这样可以方便做一些统一的事情,比如统计流量、身份验证等。也方便客户端调用时不用记忆众多微服务的地址。

    简单使用

    引入依赖

    <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-gateway</artifactId>
    </dependency>
    

    网关最常用的作用就是把一个请求地址路由到真实的请求地址,Spring Cloud Gateway提供一个简单的配置,以服务名为前缀进行拦截路由。配置如下

    server:
      port: 8084
    
    spring:
      application:
        name: gateway-example
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
    
        gateway:
          discovery:
            locator:
              enabled: true
    

    其中 spring.cloud.gateway.discovery.locator.enabled=true将会开启以服务名为前缀的路由拦截。

    目前网关的访问地址: http://127.0.0.1:8084

    假设有另一个服务:nacos-producer,真实地址:127.0.0.1:8080

    那么访问http://127.0.0.1:8084/nacos-producer/api/users/1这个路径,将被路由到下面这个地址

    http://nacos-producer/api/users/1,而nacos-producer是一个服务名,在请求时将被ribbon替换成真实的ip和port,于是便变成了http://127.0.0.1:8080/api/users/1

    路由断言工厂

    Spring Cloud Gateway内置了很多路由断言工厂,具体可见官方文档,本节挑选PathRoutePredicateFactory作为使用例子,看名字就知道它是一个以路径做为匹配规则的路由断言工厂。

    路由断言工厂的顶层接口为RoutePredicateFactory,抽象实现为AbstractRoutePredicateFactory。具体的实现一般都会继承AbstractRoutePredicateFactory类。

    Spring Cloud Gateway的配置属性类为GatewayProperties,为路由断言工厂提供了两种配置方式,一种是传统的属性名字,另外一种是快捷方式(一行配置),下面就以PathRoutePredicateFactory为例。

    实现的目标:

    当请求路径以/api/users/开头,就路由到nacos-producer服务中

    先看属性名方式:

    @ConfigurationProperties(GatewayProperties.PREFIX)
    @Validated
    public class GatewayProperties {
    
    	public static final String PREFIX = "spring.cloud.gateway";
    
        /**
         * 配置路由列表
         */
    	private List<RouteDefinition> routes = new ArrayList<>();
        
        // ...略
    }
    
    @Validated
    public class RouteDefinition {
    
    	private String id;
    
        /**
         * 配置路由断言工厂
         */
    	@NotEmpty
    	@Valid
    	private List<PredicateDefinition> predicates = new ArrayList<>();
    
    	@Valid
    	private List<FilterDefinition> filters = new ArrayList<>();
    
    	@NotNull
    	private URI uri;
    
    	private Map<String, Object> metadata = new HashMap<>();
    
    	private int order = 0;
        
        // ...略
    }
    
    public class PredicateDefinition {
        
    	/**
    	 * 路由断言工厂名称前缀
    	 * PathRoutePredicateFactory: name=Path
    	 * HeaderRoutePredicateFactory: name=Header
    	 */
    	@NotNull
    	private String name;
    
        /**
         * 参数信息
         * map的key具体取决于路由断言工厂类中的Config类中的属性名
         * 如HeaderRoutePredicateFactory: key1=header  key2=regexp
         * 如PathRoutePredicateFactory: key1=patterns  key2=matchOptionalTrailingSeparator
         */
    	private Map<String, String> args = new LinkedHashMap<>();
    }
    
    spring:
      application:
        name: gateway-example
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
    
        gateway:
          routes:
              # 路由ID
            - id: nacos-producer-route
              # 路由到哪个地址,存在注册中心时可以使用服务名, lb://是使用负载均衡(LoadBalance)
              uri: lb://nacos-producer
              predicates:
                  # 断言工厂:Path即使用PathRoutePredicateFactory工厂
                - name: Path
                  args:
                  	# List<String>:可使用[val1,val2]形式
                    patterns: /api/users/**
    

    再看快捷方式:

    spring:
      application:
        name: gateway-example
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
    
        gateway:
          routes:
          	  # 路由ID
            - id: nacos-producer-route
              # 路由到哪个地址,存在注册中心时可以使用服务名, lb://是使用负载均衡(LoadBalance)
              uri: lb://nacos-producer
              # 断言工厂:Path即使用PathRoutePredicateFactory工厂
              # =符号后面的是参数值,多个值用逗号分割
              predicates:
                - Path=/api/users/**
    

    快捷方式绑定值规则逻辑

    首先有一个前提,所有的路由断言工厂实现类名称全部以RoutePredicateFactory结尾,而名称的前缀部分则将作为使用哪个断言工厂的依据,这一点在上面的配置中已经体现,PathRoutePredicateFactory则为Path

    然后看参数部分的绑定逻辑,每一个断言工厂都有一个Config类,用于保存参数信息。这里有一个很重要的接口,接口名为ShortcutConfigurable,主要解读这个类的作用就能明白绑定逻辑了。

    我们的路由断言工厂顶层接口RoutePredicateFactory是继承了ShortcutConfigurable接口的

    /**
     * 这个接口定义了两个重要的方法,如下所示,要想实现快捷方式配置,具体的路由断言工厂实现类
     * 都要重写这两个方法
     */
    public interface ShortcutConfigurable {
    
        /**
         * 用于描述如何封装参数信息
         * 有3个值可选
         * DEFAULT、GATHER_LIST、GATHER_LIST_TAIL_FLAG
         */
    	default ShortcutType shortcutType() {
    		return ShortcutType.DEFAULT;
    	}
    
        /**
         * 用于描述参数名字信息,有顺序的 
         */
    	default List<String> shortcutFieldOrder() {
    		return Collections.emptyList();
    	}
        
        enum ShortcutType {
    		
            /**
             * 默认行为, 将Map中的参数逐一映射
             */
    		DEFAULT,
    
            /**
             * 将Map中的参数聚合成一个参数, 变成一个List, 赋值给shortcutFieldOrder方法
             * 第0一个元素指定的参数名
             */
    		GATHER_LIST,
    
    		/**
    		 * 如果最后一个参数是boolean值,则将这个参数赋值给shortcutFieldOrder方法第1个
    		 * 元素指定的参数名, 其它值聚合成一个List赋值给shortcutFieldOrder方法
             * 第0一个元素指定的参数名
    		 */
    		GATHER_LIST_TAIL_FLAG;
        
    }
    

    下面以具体例子来解释它们的区别

    假设yaml中如下配置

    XXX=name1,name2,true

    例1

    ShortcutType shortcutType() {
        return ShortcutType.DEFAULT;
    }
    
    default List<String> shortcutFieldOrder() {
        return [arg1, arg2, arg3]
    }
    
    class Config {
        // name1
        private String arg1;
        // name2
        private String arg2;
        // true
        private boolean arg3;
    }
    

    例2

    ShortcutType shortcutType() {
        return ShortcutType.GATHER_LIST;
    }
    
    default List<String> shortcutFieldOrder() {
        return [args]
    }
    
    class Config {
        // [name1,name2,true]
        private List<String> args;
    }
    

    例3

    ShortcutType shortcutType() {
        return ShortcutType.GATHER_LIST_TAIL_FLAG;
    }
    
    default List<String> shortcutFieldOrder() {
        return [args, flag]
    }
    
    class Config {
        // [name1,name2]
        private List<String> args;
        // true
        private boolean flag;
    }
    

    自定义路由断言工厂

    本节将自定义一个PathPrefixRoutePredicateFactory路由断言工厂,即请求包含某个路径前缀则进行拦截。

    注意事项

    • 名称必须以RoutePredicateFactory结尾
    • 继承AbstractRoutePredicateFactory
    • 实现shortcutTypeshortcutFieldOrder方法以便支持快捷方式配置
    • 实现apply方法来完成自定义逻辑的编写(即包含某一个前缀)
    • 注册到Spring容器中
    package com.wangtao.gatewayexample.predicate;
    
    import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.server.ServerWebExchange;
    
    import java.net.URI;
    import java.util.Collections;
    import java.util.List;
    import java.util.function.Predicate;
    
    public class PathPrefixRoutePredicateFactory extends AbstractRoutePredicateFactory<PathPrefixRoutePredicateFactory.Config> {
    
        private static final String ARG_KEY = "prefix";
    
        public PathPrefixRoutePredicateFactory() {
            super(Config.class);
        }
    
        @Override
        public List<String> shortcutFieldOrder() {
            return Collections.singletonList(ARG_KEY);
        }
    
        @Override
        public Predicate<ServerWebExchange> apply(Config config) {
            return new GatewayPredicate() {
                @Override
                public boolean test(ServerWebExchange exchange) {
                    URI uri = exchange.getRequest().getURI();
                    return uri.getPath().startsWith(config.getPrefix());
                }
            };
        }
    
        @Validated
        public static class Config {
    
            private String prefix;
    
            public String getPrefix() {
                return prefix;
            }
    
            public void setPrefix(String prefix) {
                this.prefix = prefix;
            }
        }
    }
    
    

    配置使用

    spring:
      application:
        name: gateway-example
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
    
        gateway:
          routes:
           - id: nacos-producer-custom-route
             uri: lb://nacos-producer
             predicates:
               - PathPrefix=/api
    

    或者

    spring:
      application:
        name: gateway-example
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
    
        gateway:
          routes:
           - id: nacos-producer-custom-route
             uri: lb://nacos-producer
             predicates:
               - name: PathPrefix
               	 args:
               	   prefix: /api
                 
    

    过滤器工厂

    Spring Cloud Gateway中内置了许多的过滤器工厂(官网中例举了30个),当路由断言通过后,会执行该路由配置的一系列过滤器,这样我们可以修正或添加一些请求信息,比如增加参数,请求头等操作。其中配置方式与路由断言工厂一样,这里不再赘述。

    过滤器工厂顶层接口为GatewayFilterFactory

    本节将在请求头信息增加一个token,用于验证,将会用到AddRequestHeaderGatewayFilterFactory

    spring:
      application:
        name: gateway-example
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
    
        gateway:
          routes:
           - id: nacos-producer-custom-route
             uri: lb://nacos-producer
             predicates:
               - PathPrefix=/api
             filters:
               - AddRequestHeader=customToken,waston
    

    访问日志配置

    通过JVM启动参数打开,不能通过application.yaml文件配置

    -Dreactor.netty.http.server.accessLogEnabled=true

    跨域配置

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.reactive.CorsWebFilter;
    import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
    
    /**
     * 全局跨域配置
     */
    @Configuration
    public class CorsConfig {
    
        @Bean
        public CorsWebFilter corsWebFilter() {
            CorsConfiguration corsConfiguration = new CorsConfiguration();
            // 允许的请求头
            corsConfiguration.addAllowedHeader("*");
            // 允许的请求源 (如:http://localhost:8080)
            corsConfiguration.addAllowedOrigin("*");
            // 允许的请求方法 ==> GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
            corsConfiguration.addAllowedMethod("*");
            // URL 映射 (如: /admin/**)
            UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
            urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
            return new CorsWebFilter(urlBasedCorsConfigurationSource);
        }
    }
    
  • 相关阅读:
    【python+selenium】selenium grid(分布式)
    【python】导入自定义模块
    Maven的配置以及IDEA导入本地Maven
    java历史概述
    JVM 内存调优 与 实际案例
    ConcurrentHashMap实现线程安全的原理
    Request.UrlReferrer详解
    等比例缩放生成缩略图
    JavaEE的ajax入门
    javaee三层架构案例--简单学生管理系统
  • 原文地址:https://www.cnblogs.com/wt20/p/16461511.html
Copyright © 2020-2023  润新知