本文基于 spring cloud gateway 2.0.1

1、简介

Spring Cloud Gateway 创建 Route 对象时, 使用 RoutePredicateFactory 创建 Predicate 对象,Predicate 对象可以赋值给 Route。 Spring Cloud Gateway 包含许多内置的Route Predicate Factories。所有这些谓词都匹配HTTP请求的不同属性。多种谓词工厂可以组合,并通过逻辑and。

路由选择是通过Predicate函数式接口进行判断当前路由是否满足给定条件。

路由谓词工厂 RoutePredicateFactory 包含的主要实现类如图所示,可以看到该接口有多个实现类。抽象类 AbstractRoutePredicateFactory 实现了路由谓词工厂,但是没有实际的方法,具体的实现类都是继承自抽象类 AbstractRoutePredicateFactory 包括 Datetime、 请求的远端地址、 路由权重、 请求头、 Host 地址、 请求方法、 请求路径 和 请求参数等类型的路由断言。

2、路由谓词工厂 RoutePredicateFactory

按照功能对路由谓词工厂进行划分,可以划分为以下几种,如图所示:

@FunctionalInterface
public interface RoutePredicateFactory<C> extends ShortcutConfigurable, Configurable<C> {
String PATTERN_KEY = "pattern"; // useful for javadsl
default Predicate<ServerWebExchange> apply(Consumer<C> consumer) {
C config = newConfig();
consumer.accept(config);
beforeApply(config);
return apply(config);
} default AsyncPredicate<ServerWebExchange> applyAsync(Consumer<C> consumer) {
C config = newConfig();
consumer.accept(config);
beforeApply(config);
return applyAsync(config);
} default Class<C> getConfigClass() {
throw new UnsupportedOperationException("getConfigClass() not implemented");
} @Override
default C newConfig() {
throw new UnsupportedOperationException("newConfig() not implemented");
} default void beforeApply(C config) {} Predicate<ServerWebExchange> apply(C config); default AsyncPredicate<ServerWebExchange> applyAsync(C config) {
return toAsyncPredicate(apply(config));
} default String name() {
return NameUtils.normalizeRoutePredicateName(getClass());
} }

RoutePredicateFactory 接口继承自 ShortcutConfigurable 接口,ShortcutConfigurable 接口在多个实现类中都有出现,根据传入的具体 RouteDefinitionLocator 获取路由定义对象时,就用到了该接口中的默认方法。路由谓词的种类很多,不同的谓词需要的配置参数不一样,所以每种 路由谓词(断言)和过滤器的实现都会实现 ShortcutConfigurable 接口,来指定自身参数的个数和顺序。

2.1、ShortcutConfigurable

ShortcutConfigurable 接口提供的默认方法,主要用于对过滤器和断言参数进行标准化处理,将表达式和生成的键进行转换。

public interface ShortcutConfigurable {

	enum ShortcutType {
DEFAULT {
@Override
public Map<String, Object> normalize(Map<String, String> args, ShortcutConfigurable shortcutConf, SpelExpressionParser parser, BeanFactory beanFactory) {
Map<String, Object> map = new HashMap<>();
int entryIdx = 0;
for (Map.Entry<String, String> entry : args.entrySet()) {
String key = normalizeKey(entry.getKey(), entryIdx, shortcutConf, args);
Object value = getValue(parser, beanFactory, entry.getValue()); map.put(key, value);
entryIdx++;
}
return map;
}
}, GATHER_LIST {
@Override
public Map<String, Object> normalize(Map<String, String> args, ShortcutConfigurable shortcutConf, SpelExpressionParser parser, BeanFactory beanFactory) {
Map<String, Object> map = new HashMap<>();
// field order should be of size 1
List<String> fieldOrder = shortcutConf.shortcutFieldOrder();
Assert.isTrue(fieldOrder != null
&& fieldOrder.size() == 1,
"Shortcut Configuration Type GATHER_LIST must have shortcutFieldOrder of size 1");
String fieldName = fieldOrder.get(0);
map.put(fieldName, args.values().stream()
.map(value -> getValue(parser, beanFactory, value))
.collect(Collectors.toList()));
return map;
}
}; public abstract Map<String, Object> normalize(Map<String, String> args, ShortcutConfigurable shortcutConf,
SpelExpressionParser parser, BeanFactory beanFactory);
} static String normalizeKey(String key, int entryIdx, ShortcutConfigurable argHints, Map<String, String> args) {
/*************************(1)*********************************/
// RoutePredicateFactory has name hints and this has a fake key name
// replace with the matching key hint
if (key.startsWith(NameUtils.GENERATED_NAME_PREFIX) && !argHints.shortcutFieldOrder().isEmpty()
&& entryIdx < args.size() && entryIdx < argHints.shortcutFieldOrder().size()) {
key = argHints.shortcutFieldOrder().get(entryIdx);
}
return key;
} static Object getValue(SpelExpressionParser parser, BeanFactory beanFactory, String entryValue) {
/*************************(2)*********************************/ Object value;
String rawValue = entryValue;
if (rawValue != null) {
rawValue = rawValue.trim();
}
if (rawValue != null && rawValue.startsWith("#{") && entryValue.endsWith("}")) {
/*************************(3)*********************************/ // assume it's spel
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new BeanFactoryResolver(beanFactory));
Expression expression = parser.parseExpression(entryValue, new TemplateParserContext());
value = expression.getValue(context);
} else {
value = entryValue;
}
return value;
} default ShortcutType shortcutType() {
return ShortcutType.DEFAULT;
} /**
* Returns hints about the number of args and the order for shortcut parsing.
* @return
*/
default List<String> shortcutFieldOrder() {
/*************************(4)*********************************/ return Collections.emptyList();
} default String shortcutFieldPrefix() {
return "";
} }

以上代码主要做了以下工作:

1)对键进行标准化处理,因为键有可能是自动生成,当键以_ genkey_ 开头时, 表明是自动生成的。

2)获取真实值,需要传入 Spring EL 解析器、Bean工厂等工具类。

3)对传入的 entryValue 是一个表达式的情况进行处理,这里默认是 Spring EL 表达式。

4)返回有关参数数量和解析顺序的提示。

3、Datetime 类型的路由谓词工厂

Datetime 类型的路由谓词工厂有三种,分别为:

  • AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日志

  • BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日志

  • BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内

以 AfterRoutePredicateFactory 为例,介绍 Datetime 类型的断言工厂的应用:

spring:
cloud:
gateway:
routes:
- id: after_route_id
uri: http://www.baidu.com
predicates:
- After= 2018-12-30T23:59:59.789+08:00[Asia/Shanghai]

上面的配置文件指定了路由的断言,谓词关键字是 After ,表示请求时间必须晚于上海时间 2018年12月30日 23:59:59 才可用。

4、基于远程地址(RemoteAttr)的路由谓词工厂

RemoteAddrRoutePredicateFactory 属于根据请求IP进行路由类型,接收 CIDR表示 法(IPv4或IPv6)的字符串列表(列表最小长度为1)作为参数, 例如 192.168.0.1/ 16, 其中 192. 168. 0. 1 是 IP 地址, 16 是 子 网 掩 码。

spring:
cloud:
gateway:
routes:
-id: remoteaddr_ route_id
uri: http://www.baidu.com
predicates:
-RemoteAddr=192.168.1.1/24

以上配置表示如果请求的远程地址是 192.168.1.10,将会匹配该路由。

// RemoteAddrRoutePredicateFactory.java

@Override
public Predicate<ServerWebExchange> apply(Config config) {
List<IpSubnetFilterRule> sources = convert(config.sources); return exchange -> {
//获取请求中的远程地址
InetSocketAddress remoteAddress = config.remoteAddressResolver.resolve(exchange);
if (remoteAddress != null) {
String hostAddress = remoteAddress.getAddress().getHostAddress();
String host = exchange.getRequest().getURI().getHost(); if (log.isDebugEnabled() && !hostAddress.equals(host)) {
log.debug("Remote addresses didn't match " + hostAddress + " != " + host);
}
//遍历配置好的 RemoteAddr 列表, 如果远程地址在列表中则匹配成功
for (IpSubnetFilterRule source : sources) {
if (source.matches(remoteAddress)) {
return true;
}
}
} return false;
};
} private void addSource(List<IpSubnetFilterRule> sources, String source) {
if (!source.contains("/")) {
//当 RemoteAddr 没有子网掩码时,默认为/32
source = source + "/32";
} String[] ipAddressCidrPrefix = source.split("/",2);
String ipAddress = ipAddressCidrPrefix[0];
int cidrPrefix = Integer.parseInt(ipAddressCidrPrefix[1]);
//根据 ip 和 子网,确定 RemoteAddr 的范围,并加入到 sources 中
sources.add(new IpSubnetFilterRule(ipAddress, cidrPrefix, IpFilterRuleType.ACCEPT));
}

基于远程地址匹配的路由断言实现,首先获取配置文件中的 RemoteAttr 列表,然后将配置的 RemoteAttr 列表转换成 sources 列表,主要是根据IP地址和子网掩码确定地址范围,最后判断请求的远程地址是否在设置的 IP 列表中。

Spring Cloud Gateway(六):路由谓词工厂 RoutePredicateFactory的更多相关文章

  1. Spring Cloud Alibaba学习笔记(17) - Spring Cloud Gateway 自定义路由谓词工厂

    在前文中,我们介绍了Spring Cloud Gateway内置了一系列的路由谓词工厂,但是如果这些内置的路由谓词工厂不能满足业务需求的话,我们可以自定义路由谓词工厂来实现特定的需求. 例如有某个服务 ...

  2. 通过Nacos动态刷新Spring Cloud Gateway的路由

    通过Nacos动态刷新Spring Cloud Gateway的路由 一.背景 二.解决方案 三.实现功能 四.实现步骤 1.网关服务的实现 1.pom文件 2.bootstrap.yml配置文件 3 ...

  3. springcloud3(五) spring cloud gateway动态路由的四类实现方式

    写这篇博客主要是为了汇总下动态路由的多种实现方式,没有好坏之分,任何的方案都是依赖业务场景需求的,现在网上实现方式主要有: 基于Nacos, 基于数据库(PosgreSQL/Redis), 基于Mem ...

  4. Spring Cloud gateway 六 Sentinel nacos存储动态刷新

    微服务当前这么火爆的程度,如果不能学会一种微服务框架技术.怎么能升职加薪,增加简历的筹码?spring cloud 和 Dubbo 需要单独学习.说没有时间?没有精力?要学俩个框架?而Spring C ...

  5. spring cloud gateway网关路由分配

    1, 基于父工程,新建一个模块 2,pom文件添加依赖 <dependencies> <dependency> <groupId>org.springframewo ...

  6. Spring Cloud Alibaba学习笔记(16) - Spring Cloud Gateway 内置的路由谓词工厂

    Spring Cloud Gateway路由配置的两种形式 Spring Cloud Gateway的路由配置有两种形式,分别是路由到指定的URL以及路由到指定的微服务,在上文博客的示例中我们就已经使 ...

  7. Spring Cloud Alibaba学习笔记(19) - Spring Cloud Gateway 自定义过滤器工厂

    在前文中,我们介绍了Spring Cloud Gateway内置了一系列的内置过滤器工厂,若Spring Cloud Gateway内置的过滤器工厂无法满足我们的业务需求,那么此时就需要自定义自己的过 ...

  8. Spring cloud gateway 如何在路由时进行负载均衡

    本文为博主原创,转载请注明出处: 1.spring cloud gateway 配置路由 在网关模块的配置文件中配置路由: spring: cloud: gateway: routes: - id: ...

  9. Spring Cloud Gateway(一):认识Spring Cloud Gateway

    1.Spring Cloud Gateway 简介 Spring Cloud Gateway 系列目录 Spring Cloud Gateway(一):认识Spring Cloud Gateway S ...

随机推荐

  1. core直接获取报异常数据

    报异常直接跳转到/Home/Error [ResponseCache(Duration = , Location = ResponseCacheLocation.None, NoStore = tru ...

  2. 转 无损转换Image为Icon

    不可取 var handle = bmp.GetHicon();    //得到图标句柄return Icon.FromHandle(handle); //通过句柄得到图标 可取 /// <su ...

  3. Axios使用拦截器全局处理请求重试

    Axios拦截器 Axios提供了拦截器的接口,让我们能够全局处理请求和响应.Axios拦截器会在Promise的then和catch调用前拦截到. 请求拦截示例 axios.interceptors ...

  4. CSS3或CSS+JS实现改变滚动条样式(兼容所有浏览器)

    /*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/ ::-webkit-scrollbar { width: 16px; /*滚动条宽度*/ height: 16px; /*滚动条高度*/ } ...

  5. Lwip与底层的接口

    Lwip有三套api,分别是: raw api:使用方法为使用回调函数,即先注册一个函数,当接受到数据之后调用这个函数.缺点是对于数据连续处理不好. Lwip api:把接收与处理放在一个线程里面.因 ...

  6. Win10开启蓝屏信息记录及文件查看位置的方法

    蓝屏,是电脑最常见的故障,一般出现蓝屏时都会显示详细的蓝屏错误信息,方便用户排查故障.但是如果系统未开启蓝屏记录,下文介绍蓝屏日志开启及蓝屏日志文件存放位置.我用的是win10系统 蓝屏日志开启方法步 ...

  7. 【问题】man命令打开的手册上链接怎么展开?

    参考:How to follow links in linux man pages? 前言 在使用man查看命令帮助的时候,有些文字下面会有下划线.给人的感觉是一个链接,但是又打不开.那么到底是不是链 ...

  8. vue项目 npm install 安装依赖 特别慢 解决办法

    使用NPM(Node.js包管理工具)安装依赖时速度特别慢,为了安装Express,执行命令后两个多小时都没安装成功,最后只能取消安装,笔者20M带宽,应该不是我网络的原因,后来在网上找了好久才找到一 ...

  9. 记一次oracle 11g数据导入

    1.ORACLE数据库数据导入到测试库环境 172.15.1.51 root  kic@test 172.15.1.52 root 一般先将数据导入52的环境(配置比较低),再将数据导入51的环境(本 ...

  10. Java关键字—final

    final—不可变的,用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变. 修饰范围: 1.修饰类,被final关键字修饰的类无法被继承: 2.修饰方法,被f ...