专栏首页码匠的流水账聊聊spring cloud的DiscoveryClientRouteDefinitionLocator

聊聊spring cloud的DiscoveryClientRouteDefinitionLocator

本文主要研究一下spring cloud的DiscoveryClient Route Definition Locator

GatewayAutoConfiguration

spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java

@Configuration
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore(HttpHandlerAutoConfiguration.class)
@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class})
@ConditionalOnClass(DispatcherHandler.class)
public class GatewayAutoConfiguration {
    //......
    @Bean
    @ConditionalOnMissingBean
    public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
        return new PropertiesRouteDefinitionLocator(properties);
    }

    @Bean
    @ConditionalOnMissingBean(RouteDefinitionRepository.class)
    public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
        return new InMemoryRouteDefinitionRepository();
    }
    @Bean
    @Primary
    public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
        return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
    }
    //......
}

这里注册了CompositeRouteDefinitionLocator

GatewayDiscoveryClientAutoConfiguration

spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/discovery/GatewayDiscoveryClientAutoConfiguration.java

@Configuration
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@AutoConfigureBefore(GatewayAutoConfiguration.class)
@ConditionalOnClass({DispatcherHandler.class, DiscoveryClient.class})
@EnableConfigurationProperties
public class GatewayDiscoveryClientAutoConfiguration {

    @Bean
    @ConditionalOnBean(DiscoveryClient.class)
    @ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled")
    public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(
            DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
        return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
    }

    @Bean
    public DiscoveryLocatorProperties discoveryLocatorProperties() {
        DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
        properties.setPredicates(initPredicates());
        properties.setFilters(initFilters());
        return properties;
    }

    public static List<PredicateDefinition> initPredicates() {
        ArrayList<PredicateDefinition> definitions = new ArrayList<>();
        // TODO: add a predicate that matches the url at /serviceId?

        // add a predicate that matches the url at /serviceId/**
        PredicateDefinition predicate = new PredicateDefinition();
        predicate.setName(normalizeRoutePredicateName(PathRoutePredicateFactory.class));
        predicate.addArg(PATTERN_KEY, "'/'+serviceId+'/**'");
        definitions.add(predicate);
        return definitions;
    }

    public static List<FilterDefinition> initFilters() {
        ArrayList<FilterDefinition> definitions = new ArrayList<>();

        // add a filter that removes /serviceId by default
        FilterDefinition filter = new FilterDefinition();
        filter.setName(normalizeFilterFactoryName(RewritePathGatewayFilterFactory.class));
        String regex = "'/' + serviceId + '/(?<remaining>.*)'";
        String replacement = "'/${remaining}'";
        filter.addArg(REGEXP_KEY, regex);
        filter.addArg(REPLACEMENT_KEY, replacement);
        definitions.add(filter);

        return definitions;
    }

}

默认配置了一个根据serviceId进行rewrite的filter

DiscoveryLocatorProperties

spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/discovery/DiscoveryLocatorProperties.java

@ConfigurationProperties("spring.cloud.gateway.discovery.locator")
public class DiscoveryLocatorProperties {

    /** Flag that enables DiscoveryClient gateway integration */
    private boolean enabled = false;

    /**
     * The prefix for the routeId, defaults to discoveryClient.getClass().getSimpleName() + "_".
     * Service Id will be appended to create the routeId.
     */
    private String routeIdPrefix;

    /**
     * SpEL expression that will evaluate whether to include a service in gateway integration or not,
     * defaults to: true
     */
    private String includeExpression = "true";

    /** SpEL expression that create the uri for each route, defaults to: 'lb://'+serviceId */
    private String urlExpression = "'lb://'+serviceId";

    /**
     * Option to lower case serviceId in predicates and filters, defaults to false.
     * Useful with eureka when it automatically uppercases serviceId.
     * so MYSERIVCE, would match /myservice/**
     */
    private boolean lowerCaseServiceId = false;

    private List<PredicateDefinition> predicates = new ArrayList<>();

    private List<FilterDefinition> filters = new ArrayList<>();

    //......

    @Override
    public String toString() {
        return new ToStringCreator(this)
                .append("enabled", enabled)
                .append("routeIdPrefix", routeIdPrefix)
                .append("includeExpression", includeExpression)
                .append("urlExpression", urlExpression)
                .append("lowerCaseServiceId", lowerCaseServiceId)
                .append("predicates", predicates)
                .append("filters", filters)
                .toString();
    }
}
  • 默认lowerCaseServiceId为false,因为eureka默认把serviceId大写
  • urlExpression默认为’lb://‘+serviceId

DiscoveryClientRouteDefinitionLocator

spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java

public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {

    private final DiscoveryClient discoveryClient;
    private final DiscoveryLocatorProperties properties;
    private final String routeIdPrefix;

    public DiscoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
        this.discoveryClient = discoveryClient;
        this.properties = properties;
        if (StringUtils.hasText(properties.getRouteIdPrefix())) {
            this.routeIdPrefix = properties.getRouteIdPrefix();
        } else {
            this.routeIdPrefix = this.discoveryClient.getClass().getSimpleName() + "_";
        }
    }

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        SimpleEvaluationContext evalCtxt = SimpleEvaluationContext
                .forReadOnlyDataBinding()
                .withInstanceMethods()
                .build();

        SpelExpressionParser parser = new SpelExpressionParser();
        Expression includeExpr = parser.parseExpression(properties.getIncludeExpression());
        Expression urlExpr = parser.parseExpression(properties.getUrlExpression());

        return Flux.fromIterable(discoveryClient.getServices())
                .map(discoveryClient::getInstances)
                .filter(instances -> !instances.isEmpty())
                .map(instances -> instances.get(0))
                .filter(instance -> {
                    Boolean include = includeExpr.getValue(evalCtxt, instance, Boolean.class);
                    if (include == null) {
                        return false;
                    }
                    return include;
                })
                .map(instance -> {
                    String serviceId = instance.getServiceId();

                    RouteDefinition routeDefinition = new RouteDefinition();
                    routeDefinition.setId(this.routeIdPrefix + serviceId);
                    String uri = urlExpr.getValue(evalCtxt, instance, String.class);
                    routeDefinition.setUri(URI.create(uri));

                    final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties);

                    for (PredicateDefinition original : this.properties.getPredicates()) {
                        PredicateDefinition predicate = new PredicateDefinition();
                        predicate.setName(original.getName());
                        for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
                            String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
                            predicate.addArg(entry.getKey(), value);
                        }
                        routeDefinition.getPredicates().add(predicate);
                    }

                    for (FilterDefinition original : this.properties.getFilters()) {
                        FilterDefinition filter = new FilterDefinition();
                        filter.setName(original.getName());
                        for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
                            String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
                            filter.addArg(entry.getKey(), value);
                        }
                        routeDefinition.getFilters().add(filter);
                    }

                    return routeDefinition;
                });
    }

    String getValueFromExpr(SimpleEvaluationContext evalCtxt, SpelExpressionParser parser, ServiceInstance instance, Map.Entry<String, String> entry) {
        Expression valueExpr = parser.parseExpression(entry.getValue());
        return valueExpr.getValue(evalCtxt, instance, String.class);
    }
    //......
}

可以看到这里从discoveryClient.getServices()获取注册信息转换为RouteDefinition

小结

RouteDefinitionLocator接口有不同的实现类:

  • InMemoryRouteDefinitionRepository
  • CompositeRouteDefinitionLocator
  • DiscoveryClientRouteDefinitionLocator
  • PropertiesRouteDefinitionLocator
  • CachingRouteDefinitionLocator 如果开启spring.cloud.gateway.discovery.locator.enabled=true,那么最后CompositeRouteDefinitionLocator则是组合了InMemoryRouteDefinitionRepository、PropertiesRouteDefinitionLocator、DiscoveryClientRouteDefinitionLocator三个RouteDefinitionLocator

doc

  • 114.2 DiscoveryClient Route Definition Locator

本文分享自微信公众号 - 码匠的流水账(geek_luandun),作者:go4it

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-06-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 聊聊spring cloud gateway的ForwardedHeadersFilter

    本文主要研究一下spring cloud gateway的ForwardedHeadersFilter

    codecraft
  • 使用prettyTime格式化时间

    社交网站经常会显示几分钟前,几个小时前之类的时间,java里头有个prettyTime的类库可以用来做这样的转换,这里介绍一下。

    codecraft
  • restTemplate访问https

    codecraft
  • 通过写Java代码来对MyEclipse进行注册

    import java.io.BufferedReader; import java.io.IOException; import java.io...

    williamwong
  • 简单的java实验,涉及到 类继承以及接口问题,方法体的重写(区别于重载)

    1 package test ; 2 abstract class Animal 3 { 4 abstract void cry(); ...

    Gxjun
  • Flutter 学习记2 - 首个应用

    void main() 是入口方法,=> 用于单行方法,就是函数签名和函数体间的连接符号,感觉作用和 Kotlin 的单行函数体用 = 类似。既然这样,把代码修...

    七适散人
  • commons-configuration2:properties文件写入中文(no escape)

    properties 是java标准支持的配置文件格式,默认编码ISO 8859-1,unicode字符会被转义(Unicode escapes) 参见 ht...

    用户1148648
  • 细说new与malloc的10点区别

    Tencent JCoder
  • 【手把手教你全文检索】Lucene索引的【增、删、改、查】

    前言   搞检索的,应该多少都会了解Lucene一些,它开源而且简单上手,官方API足够编写些小DEMO。并且根据倒排索引,实现快速检索。本文就简单的实现增...

    用户1154259
  • 有趣的卷积神经网络

    最近一直在研究深度学习,联想起之前所学,感叹数学是一门朴素而神奇的科学。F=G*m1*m2/r²万有引力描述了宇宙星河运转的规律,E=mc²描述了恒星发...

    天涯泪小武

扫码关注云+社区

领取腾讯云代金券