Soul API网关源码解析之MatchStrategy详解程序员

前言

前面一篇文章讲到了SoulPlugin和插件选择器,主要是讲了SoulPlugin的定义与实现,以及选择器的匹配,但是并没有把选择器的匹配给写完。那么本篇文章就去这里继续进行解析。和大家进一步进行深入的分析。先看下代码:

public class MatchStrategyUtils {
    
    /** 
     * Match boolean. 
     * 
     * @param strategy          the strategy 
     * @param conditionDataList the condition data list 
     * @param exchange          the exchange 
     * @return the boolean 
     */ 
    public static boolean match(final Integer strategy, final List<ConditionData> conditionDataList, final ServerWebExchange exchange) {
    
        String matchMode = MatchModeEnum.getMatchModeByCode(strategy); 
        MatchStrategy matchStrategy = ExtensionLoader.getExtensionLoader(MatchStrategy.class).getJoin(matchMode); 
        return matchStrategy.match(conditionDataList, exchange); 
    } 
} 

选择器匹配

  • 获取匹配模式

从上面的代码中,我们可以看到首先是根据策略来获取匹配模式,那说到这里就要看看代码了,如下:

@RequiredArgsConstructor 
@Getter 
public enum MatchModeEnum {
    
    /** 
     * And match mode enum. 
     */ 
    AND(0, "and"), 
    /** 
     * Or match mode enum. 
     */ 
    OR(1, "or"); 
    private final int code; 
    private final String name; 
    /** 
     * get match mode name by code. 
     * 
     * @param code match mode code. 
     * @return match mode name. 
     */ 
    public static String getMatchModeByCode(final int code) {
    
        return Arrays.stream(MatchModeEnum.values()) 
                .filter(e -> e.code == code).findFirst() 
                .orElse(MatchModeEnum.AND) 
                .getName(); 
    } 
} 

其实这就是一个枚举类,这里的匹配模式是根据or、and的策略来进行匹配。所以这块代码还是很简单的。那我们匹配到了自己的模式后,那就进入到下一步了。

  • 获取ExtensionLoader

从最上面的代码中可以看到,先根据MatchStrategy.class来ExtensionLoader,然后根据获得到的ExtensionLoader来获取具体的匹配策略。说这么多,直接上代码:

ExtensionLoader.java

public static <T> ExtensionLoader<T> getExtensionLoader(final Class<T> clazz) {
    
    if (clazz == null) {
    
        throw new NullPointerException("extension clazz is null"); 
    } 
    if (!clazz.isInterface()) {
    
        throw new IllegalArgumentException("extension clazz (" + clazz + ") is not interface!"); 
    } 
    if (!clazz.isAnnotationPresent(SPI.class)) {
    
        throw new IllegalArgumentException("extension clazz (" + clazz + ") without @" + SPI.class + " Annotation"); 
    } 
    ExtensionLoader<T> extensionLoader = (ExtensionLoader<T>) LOADERS.get(clazz); 
    if (extensionLoader != null) {
    
        return extensionLoader; 
    } 
    LOADERS.putIfAbsent(clazz, new ExtensionLoader<>(clazz)); 
    return (ExtensionLoader<T>) LOADERS.get(clazz); 
} 

这段代码很简单,根据传入的class来到缓存中获取ExtensionLoader,如果能获取到则直接返回,没有就直接new 一个,然后返回去。

  • 正式匹配
@Join 
public class AndMatchStrategy extends AbstractMatchStrategy implements MatchStrategy {
    
@Override 
public Boolean match(final List<ConditionData> conditionDataList, final ServerWebExchange exchange) {
    
return conditionDataList 
.stream() 
.allMatch(condition -> OperatorJudgeFactory.judge(condition, buildRealData(condition, exchange))); 
} 
} 
@Join 
public class OrMatchStrategy extends AbstractMatchStrategy implements MatchStrategy {
    
    @Override 
    public Boolean match(final List<ConditionData> conditionDataList, final ServerWebExchange exchange) {
    
        return conditionDataList 
                .stream() 
                .anyMatch(condition -> OperatorJudgeFactory.judge(condition, buildRealData(condition, exchange))); 
    } 
} 

上面的代码就是两种策略的实现类,但是我们发现这两个都是继承了AndMatchStrategy,并且实现了MatchStrategy,我们还是要来看看MatchStrategy代码:

@SPI 
public interface MatchStrategy {
    
    /** 
     * this is condition match. 
     * 
     * @param conditionDataList condition list. 
     * @param exchange          [email protected] ServerWebExchange} 
     * @return true is match , false is not match. 
     */ 
    Boolean match(List<ConditionData> conditionDataList, ServerWebExchange exchange); 
} 

MatchStrategy接口类中只是定义了一个方法,[email protected] 注解,这个先放一放,后面再关注。接着继续看看AbstractMatchStrategy抽象类的代码实现:

abstract class AbstractMatchStrategy {
 
/** 
* Build real data string. 
* 
* @param condition the condition 
* @param exchange  the exchange 
* @return the string 
*/ 
String buildRealData(final ConditionData condition, final ServerWebExchange exchange) {
 
String realData = ""; 
ParamTypeEnum paramTypeEnum = ParamTypeEnum.getParamTypeEnumByName(condition.getParamType()); 
switch (paramTypeEnum) {
 
case HEADER: 
final HttpHeaders headers = exchange.getRequest().getHeaders(); 
final List<String> list = headers.get(condition.getParamName()); 
if (CollectionUtils.isEmpty(list)) {
 
return realData; 
} 
realData = Objects.requireNonNull(headers.get(condition.getParamName())).stream().findFirst().orElse(""); 
break; 
case URI: 
realData = exchange.getRequest().getURI().getPath(); 
break; 
case QUERY: 
final MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams(); 
realData = queryParams.getFirst(condition.getParamName()); 
break; 
case HOST: 
realData = HostAddressUtils.acquireHost(exchange); 
break; 
case IP: 
realData = HostAddressUtils.acquireIp(exchange); 
break; 
case POST: 
final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT); 
realData = (String) ReflectUtils.getFieldValue(soulContext, condition.getParamName()); 
break; 
default: 
break; 
} 
return realData; 
} 
} 

在这个抽象类中,它首先会根据参数类型获取paramTypeEnum,然后根据paramTypeEnum来判断调用那个流程。这里有HEADER、URI等等。这里代码先不细说,我们回到两个策略类的juge方法,这个方法是OperatorJudgeFactory类中的,代码如下:

public class OperatorJudgeFactory {
 
private static final Map<String, OperatorJudge> OPERATOR_JUDGE_MAP = Maps.newHashMapWithExpectedSize(4); 
static {
 
OPERATOR_JUDGE_MAP.put(OperatorEnum.EQ.getAlias(), new EqOperatorJudge()); 
OPERATOR_JUDGE_MAP.put(OperatorEnum.MATCH.getAlias(), new MatchOperatorJudge()); 
OPERATOR_JUDGE_MAP.put(OperatorEnum.LIKE.getAlias(), new LikeOperatorJudge()); 
OPERATOR_JUDGE_MAP.put(OperatorEnum.REGEX.getAlias(), new RegExOperatorJudge()); 
} 
/** 
* judge request realData has by pass. 
* @param conditionData condition data 
* @param realData       realData 
* @return is true pass   is false not pass 
*/ 
public static Boolean judge(final ConditionData conditionData, final String realData) {
 
if (Objects.isNull(conditionData) || StringUtils.isBlank(realData)) {
 
return false; 
} 
return OPERATOR_JUDGE_MAP.get(conditionData.getOperator()).judge(conditionData, realData); 
} 
} 

从代码中可以看出类中在一个缓存,这个缓存存着几种操作类型,然后你如果去看OPERATOR_JUDGE_MAP.get(conditionData.getOperator()).judge(conditionData, realData)这行代码,会发现,这个juge方法也有匹配的几个类。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xFnMzir3-1611235899159)(https://uploader.shimo.im/f/Hq3s6RQKjeYs1IKr.png!thumbnail?fileGuid=hgKJCgVPG68kYqVq)]

在这里,我们以MatchOperatorJudge类为例:

public class MatchOperatorJudge implements OperatorJudge {
 
@Override 
public Boolean judge(final ConditionData conditionData, final String realData) {
 
if (Objects.equals(ParamTypeEnum.URI.getName(), conditionData.getParamType())) {
 
return PathMatchUtils.match(conditionData.getParamValue().trim(), realData); 
} 
return realData.contains(conditionData.getParamValue().trim()); 
} 
} 

在这个类的juge方法中,有一这么一行代码:PathMatchUtils.match(conditionData.getParamValue().trim(), realData),这里是通过PathMatchUtils工具类的match来进行匹配的。代码如下:

public class PathMatchUtils {
 
private static final AntPathMatcher MATCHER = new AntPathMatcher(); 
/** 
* Match boolean. 
* 
* @param matchUrls the ignore urls 
* @param path      the path 
* @return the boolean 
*/ 
public static boolean match(final String matchUrls, final String path) {
 
return Splitter.on(",").omitEmptyStrings().trimResults().splitToList(matchUrls).stream().anyMatch(url -> reg(url, path)); 
} 
private static boolean reg(final String pattern, final String path) {
 
return MATCHER.match(pattern, path); 
} 
} 

其实到这里,匹配的代码就差不多,那今天就到此结束了,后面准备从数据同步的角度来看看功能是如何实现的。

总结

这篇文章只是给上篇文章扫了个尾,主要从:

  • 获取匹配模式
  • 获取ExtensionLoader
  • 正式匹配

这几个几个方面来进行解析的。后面将要从其他角度来继续进行代码分析。

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/1290.html

(0)
上一篇 2021年7月15日
下一篇 2021年7月15日

相关推荐

发表回复

登录后才能评论