PredicateJudge -- analyze the design based on SPI
Apache Shenyu has been identified as a gateway application which supports a variety of protocols and microservice frameworks such as Dubbo, gRPC, Spring-Cloud, etc. To do this, the product has accomplished an elegant SPI (Service Provider Interface) as its foundation, and make the Rule data parsing and predicting program very simple , resiliency and security. As to rule data parsing processing, the SPI design increases the product's scalability. When appending new plugin, in most cases, the existing module is enough for rule data parsing , otherwise it can be rapidly carry out with tiny effort.
Top level design of SPI#
In Apache Shenyu, the SPI archtecure is defined in shenyu-spi module and composed of three parts: SPI interface, factory design pattern, and configuration file. There is two interface defined as annotation: @SPI and @Join. When class file with @Join annotation, it means that it will join as an SPI extension class, in other words, it is an application or registration. The @SPI denotes that the class is an SPI extension class.
Fig 1 classes in the shenyu-spi
The SPI configuration directory is META-INF/shenyu/. that is specified:
SHENYU_DIRECTORY = "META-INF/shenyu/";When starting the gateway system , the ExtensionLoader will scan the profiles under SHENYU_DIRECTORY, in turn, load and validate and then initialize each configed class. The configuration file uses "Key = class-file" format. During operation of the system, the corresponding SPI implementation class will be invoked through the factory mechanism.
Implementation of shenyu-plugin SPI#
In shenyu-plugin module, various plugins for HTTP routing are implemented according to the plugin mechanism, including request, redirect, response and rewrite, etc. Plugins for microservice frameworks such as Dubbo, gRPC , Spring-Cloud and Tars have been developed in the gateway product. And plugins are still increasing. If no such dependent module fo parsing and judge routing parameters and data, each plugin is necessary to implement the parsing functions, and has to frequently modify to support their matching rules, such as wildcard, regular expression, SpEL expression, etc. Therefore, they made a high level abstraction for routing parameter data following the SPI framework in shenyu-plugin module. The rule analysis consists of three parts:
ParameterData- parameter dataPredicatJudge- predicate whether the actural data match the ruleMatchStrategy- combine multiple conditions, the final used strategy
These implementation classes are defined in shenyu-plugin-base module. In each plugin, resolution and predication of the routing parameter can be realized through AbstractShenyuPlugin using the above SPIs. That is dedicated and easy to extend, in line with SOLID principle.
This section analyzes the PredictJudge in detail. You can find the dependency to shenyu-spi in the pom.xml of this module.
<dependency> <groupId>org.apache.shenyu</groupId> <artifactId>shenyu-spi</artifactId> <version>${project.version}</version></dependency>Design of PredicateJudge SPI#
PredicateJudge SPI is used to analyze and judge various routing rules configed in Apache Shenyu gateway. The name and functions of this SPI are similar to Predicate in Java, but the acceptance behavior is further abstracted applying for routing aspect. This SPI is implemented through the Factory pattern. Let's look at the Predictejudge SPI interface:
@SPI@FunctionalInterfacepublic interface PredicateJudge {
/** * judge conditionData and realData is match. * * @param conditionData {@linkplain ConditionData} * @param realData realData * @return true is pass false is not pass. */ Boolean judge(ConditionData conditionData, String realData);}The class diagram is as follows:
Fig 2-Predicate class diagram

The important methods of PredicateJudgeFactory are shown as follows:
Whenever need to parsing and matching routing data, you can use
public static PredicateJudge newInstance(final String operator) { return ExtensionLoader.getExtensionLoader(PredicateJudge.class).getJoin(processSpecialOperator(operator)); } public static Boolean judge(final ConditionData conditionData, final String realData) { if (Objects.isNull(conditionData) || StringUtils.isBlank(realData)) { return false; } return newInstance(conditionData.getOperator()).judge(conditionData, realData); }ConditionData contains of four attributes of String type: paramType, operator,paramName,paramValue
ParamTypeEnum#
Where paramType must be the enumeration type ParamTypeEnum. The default supported paramType are:
post, uri,query, host, ip,header, cookie,req_methodOperatorEnum#
operator must be the enumeration type OperatorEnum, currently supported operators are:
match, =,regex, >,<, contains, SpEL, Groovy, TimeBefore,TimeAfterBase on the above defination , the plugin module provides the following eight PredicateJudge implemetation classes to realize the logic of these operators respectively.
| Implementation class | Logic description | corespondece operator |
|---|---|---|
ContainsPredicateJudge | "contain" relation, the actual data needs contain the specified string | contains |
EqualsPredicateJudge | equals "=" | = |
MatchPredicateJudge | used for URI context path matching | match |
TimerAfterPredicateJudge | Whether the local time is after the specified time | TimeAfter |
TimerBeforePredicateJudge | Whether the local time is before the specified time | TimeBefore |
GroovyPredicateJudge | used Groovy syntax toe set ParamName and value data | Groovy |
RegexPredicateJudge | used Regex to match | regex |
How to use PredicateJudge#
When you want to parse parameters, you only need to call PredicateJudgeFactory as follows.
PredicateJudgeFactory.judge(final ConditionData conditionData, final String realData);SPI profile#
The implementation class is configed in the file under directory SHENYU_DIRECTORY . It will be loaded and cached at startup.
equals=org.apache.shenyu.plugin.base.condition.judge.EqualsPredicateJudge
contains=org.apache.shenyu.plugin.base.condition.judge.ContainsPredicateJudgeGroovy=org.apache.shenyu.plugin.base.condition.judge.GroovyPredicateJudgematch=org.apache.shenyu.plugin.base.condition.judge.MatchPredicateJudgeregex=org.apache.shenyu.plugin.base.condition.judge.RegexPredicateJudgeSpEL=org.apache.shenyu.plugin.base.condition.judge.SpELPredicateJudgeTimeAfter=org.apache.shenyu.plugin.base.condition.judge.TimerAfterPredicateJudgeTimeBefore=org.apache.shenyu.plugin.base.condition.judge.TimerBeforePredicateJudgeThe usage of PredicateJudge SPI in Shenyu gateway#
Most plugins in Apache Shenyu are inherited from AbstractShenyuPlugin. In this abstract class, the filter functions (selection and matching) are achieved through MatchStrategy SPI, and PredicateJudge will be invoked from MatchStrategy to predicate each condition data.
Fig 3- class diagram of plugins with PredicateJudge and MatchStrategy SPI

The process from client request calling the routing parsing moodule is showed as following chart.
Fig 4- flow chart for Shenyu gateway filter with parameter processing

- When startup, the system will load
SPIclasses from profile and cache them. - When the client sends a new request to the Shenyu gateway, will call the corresponding plugin within the gateway.
- When analyzing real data with routing rules, the
PredicateJudgeimplementation class will be invoked according to the contained operator.
Others#
Examples of PredicateJudge judgement#
ContainsPredicateJudge- " contains“ rule#
For example, giving a ConditionData with: paramType="uri", paramValue 是 "/http/**", when using the "contains" relation: ContainsPredicateJudge, the matching result is as follows.
ConditionData (operator="contains") | real data | judge result |
|---|---|---|
| paramType="uri", "/http/**" | "/http/**/test" | true |
| "/test/http/**/other" | true | |
| "/http1/**" | false |
About other PredicateJudge implemetantion classes, you can refer to the code and test classes.