Soul Gateway learning (2-2)Http proxy for Divide plug-in source parsing
Author: JiPeng
How does Divide forward HTTP requests
Imagine for a moment that the gateway receives a request xxx.com/openapi/app…
Imagine the following steps:
- 1. Parse the url
- 2. View the configuration file to see which service line the URL corresponds to
- 3. Read the configuration file to obtain the list of all apis registered with the gateway
- 4. Check whether the USER’s API request is in the service API list
- 5. Perform related authentication operations (user AK/SK authentication, user Quota/QPS exceeds the limit)
- 6. If the gateway provides the load balancing function, obtain the load balancing policy configured for the API
- 7. The gateway sends a request to a specific service API
- 8. The gateway sends the response from the service API to the user
This note focuses on learning how the SUol gateway forwards HTTP requests.
Take a look at the official documentation for HTTP users and Divide plug-ins
If the gateway needs to support HTTP forwarding, it needs to have the following dependencies in the gateway POM:
<! --if you use http proxy start this--> <dependency> <groupId>org.dromara</groupId> <artifactId>soul-spring-boot-starter-plugin-divide</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.dromara</groupId> <artifactId>soul-spring-boot-starter-plugin-httpclient</artifactId> <version>${project.version}</version> </dependency> <! --if you use http proxy end this-->Copy the code
The proxy for HTTP requests is related to plugin-divide and plugin-httpClient.
Plug-in chain
The soul-plugin/soul-plugin-divide module provides a DividePlugin class, derived from AbstractPlugin. AbstractPlugin implements the SoulPlugin interface
As you can see that SoulPlugin is the parent of DividePlugin, you can guess that SoulPlugin is the parent of all plug-ins. A global search for SoulPlugin is sure enough, as it is the parent class of many plug-ins.
In global search SoulPlugin, found the soul – web/SRC/main/Java/org/dromara/soul/web/in a class handler SoulWebHandler there is a property List < SoulPlugin >, Guess SoulWebHandler can operate on multiple plug-ins.
If you look at the inheritance diagram for SoulWebHandler, it inherits WebHandler, which is an interface in the Spring framework.
Since I do not know WebFlux, I did a quick search on the Internet for WebHandler and learned that this is a very important thing in WebFlux. It provides a set of general HTTP request processing schemes.
Soul gateway source code, their own implementation of a WebHandler interface SoulWebHandler class, is no doubt hope framework use soul implementation of this thing to handle requests.
In the soul – web/SRC/main/Java/org/dromara/soul/web/SoulConfiguration class in the configuration, it on class declares the annotations @ configuration, indicates that it is a configuration. The SoulConfiguration class injects a bean named webHandler to the Spring container. The bean is of type SoulWebHandler. Application scans for @Configuration annotated classes at startup, so SoulWebHandler is injected into the Spring container with the following code.
@Bean("webHandler")
public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) {
List<SoulPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);
final List<SoulPlugin> soulPlugins = pluginList.stream()
.sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList());
soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName()));
return new SoulWebHandler(soulPlugins);
}
Copy the code
When SoulWebHandler is initialized, it passes the sorted plug-ins into its constructor. Each plug-in has an order attribute that can be used to prioritize plug-ins. In the case of the DividePlugin, see that its order attribute comes from an enumerated class.
@Override
public int getOrder() {
return PluginEnum.DIVIDE.getCode();
}
Copy the code
And the order of each plug-in specific value is in the soul – common/SRC/main/Java/org/dromara/soul/common/enums/PluginEnums the enumeration defined inside the class. The code of the PluginEnum is the order of each plug-in.
The order of the plug-ins is:Global -> sign -> rate-limiter -> Hystrix -> resilience4j -> Divide -> webClient -> …………
Each time there is a request, the Handle method of WebHandler (SoulWebHandler) will be called. The most important thing in this method is to initialize DefaultSoulPluginChain and execute the chain of plug-ins.
Take a look at the execute method of DefaultSoulPluginChain, which iterates through all plug-ins, calling the execute method of each plug-in in turn.
@Override
public Mono<Void> execute(final ServerWebExchange exchange) {
return Mono.defer(() -> {
if (this.index < plugins.size()) {
SoulPlugin plugin = plugins.get(this.index++);
Boolean skip = plugin.skip(exchange);
if (skip) {
return this.execute(exchange);
}
return plugin.execute(exchange, this);
}
return Mono.empty();
});
}
Copy the code
The DividePlugin does not have the Override parent’s execute method. So let’s go to our AbstractSoulPlugin parent and see what the execute method does. As you can see in the figure below, you get the Selector and rule to execute the doExecute method of Divide plug-in.
Selectors and rules
Let’s introduce the concepts of selectors and rules.
According to the official documentation, “The selector is the first filter for traffic, and the rule is the final filter.”
Looking at Divide TAB in the soul-Admin admin background, you can see that some of the APIS for the soul-example HTTP service launched in the previous note are mapped to selectors and rules.
Imagine that when you buy something online and fill in your shipping address, most of the interactions are provincial -> municipal, and very few are direct to an alphabetically indexed list of cities across the country.
Selector -> rules is similar to province -> city, the gateway uses selector to match HTTP traffic to the gateway, and then uses rules to match HTTP traffic. The benefit of this should be that when the gateway has hundreds of downstream businesses (tens of thousands/hundreds of thousands of apis), it can be relatively fast to match the address where the request should be forwarded.
Generally speaking, a Spring Boot can be considered as a service. The selector can initially match the name of the service, and the rule can match the specific API of the service. For example, if I have businessA and businessB both selling apis, then I can have two selectors: /businessA and /businessB.
See how Divide proxy forwards HTTP requests based on a selector or rule that matches the url.
As can be seen from the figure above, DividePlugin selects and constructs the real URL from the selector and rule according to the load balancing policy, and puts the real URL, timeout time and retry times into the attribute of ServerWebExchange.
As mentioned earlier in the plugin chain, Divide’s next plugin is webClient. Let’s go to the soul – the plugin – httpclient/SRC/main/Java/org/dromara/soul/plugins/httpclient/see WebClientPlugin inside the execute method.
As you can see from the WebClientPlugin’s execute method, HTTP_URL, HTTP_TIME_OUT, and HTTP_RETRY are retrieved from exchange and sent with the HTTP request. At this point, an external HTTP request is actually proxied to the line of business by the gateway.
That’s all for this article, and I’ll go on to learn about other mechanisms of the Soul Gateway, or the WebFlux framework if I have more time.