Introduction to the

Taking into account the previous analysis of the plug-in processing process, this time we will write a custom plug-in: count how long a request lasts in the plug-in chain

Write to

First, let’s explore how a Plugin is loaded into the plugins analyzed in the previous article.

When we look at the plugins, we find that global is also in the plugins, which means that all plugins are in the plugins

public class SoulConfiguration {

    @Bean("webHandler")
    public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) {
        List<SoulPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);
        // The global plugin is also included
        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 newSoulWebHandler(soulPlugins); }}Copy the code

The call stack on the top has been broken and nothing useful has been found. For a change of thought, we can break the globalPlugin constructor and restart it

When the call stack is updated, we can look at the call stack and see that the SoulConfiguration call was made before globalPlugin, so there is nothing useful

We see the globalPlugin constructor calls trigger in the previous section, found to be the following: GlobalPluginConfiguration

@Configuration
@ConditionalOnClass(GlobalPlugin.class)
public class GlobalPluginConfiguration {

    @Bean
    public SoulPlugin globalPlugin(final SoulContextBuilder soulContextBuilder) {
        return newGlobalPlugin(soulContextBuilder); }}Copy the code

Let’s take a closer look at this class. It’s Spring Configuration, which generates beans and then injects them, and then Spring does its own assembly and so on

We notice that this bean returns a SoulPlugin, and remember that all plugins we analyzed in the previous article inherit from this, so the List is automatically assembled to all plugins. I don’t quite understand this detail, Spring is still not familiar enough, so I need to fix it later

But this gives us a general idea:

  • 1. Write a custom plug-in
  • 2. Write a custom plug-in’s Spring configuration and inject it

Custom plug-in writing

First of all, the Plugin should follow the Soul gateway specification, or should be written in the soul-plugin module, but we are just experimenting, so we can write it directly in the soul-bootstrap module

PS: Time is a little tight, research specification writing also hurt time, next time

Engineering structure

Two files need to be written this time:

  • Custom plug-in: TimeRecordPlugin
  • Custom plug-in configuration: TimeRecordConfiguration

The directory structure looks like this: directly under the source-soul-bootstrap module

├ ─ SRC │ ├ ─ the main │ │ ├ ─ Java │ │ │ └ ─ org │ │ │ └ ─ dromara │ │ │ └ ─ soul │ │ │ └ ─ the bootstrap │ │ │ ├ ─ the configuration: │ │ ├─ ├─ └ Resources │ ├─ ├─ custom pluginCopy the code

Custom plug-in writing

First, inherit SoulPlugin so that it can be properly injected into the Datalist

We then write the corresponding handler, in which we put the current system time in Exchange when the request first comes into the plug-in

Imitate WebClientResponsePlugin. After the plugin chain execution returns, we take out the previous system time and subtract the current system time to get the duration of the request in the Plugin chain

Note that the globalPlugin has an order of 0, which is not a small operation. Our plugin needs to be in front of it, so our order should be -1

So, a custom plug-in is written, roughly as follows:

import lombok.extern.slf4j.Slf4j;
import org.dromara.soul.plugin.api.SoulPlugin;
import org.dromara.soul.plugin.api.SoulPluginChain;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Slf4j
public class TimeRecordPlugin implements SoulPlugin {

    private final String TIME_RECORD = "time_record";

    @Override
    public Mono<Void> execute(ServerWebExchange exchange, SoulPluginChain chain) {
        exchange.getAttributes().put(TIME_RECORD, System.currentTimeMillis());

        return chain.execute(exchange).then(Mono.defer(() -> {
            Long startTime = exchange.getAttribute(TIME_RECORD);

            if (startTime == null) {
                log.info("Get start time error");
                return Mono.empty();
            }

            long timeRecord = System.currentTimeMillis() - startTime;
            log.info("Plugin time record: " + timeRecord + " ms");
            return Mono.empty();
        }));
    }

    @Override
    public int getOrder(a) {
        return -1; }}Copy the code

Custom plug-in configuration

It’s simple, we don’t need anything, so no arguments passed in, just a new return, code as follows:

import org.dromara.soul.bootstrap.plugin.TimeRecordPlugin;
import org.dromara.soul.plugin.api.SoulPlugin;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass(TimeRecordPlugin.class)
public class TimeRecordConfiguration {

    @Bean
    public SoulPlugin timeRecordPlugin(a) {
        return newTimeRecordPlugin(); }}Copy the code

Run the test

Let’s launch soul-admin, soul-bootstrap, and soul-example Http

Visit: http://127.0.0.1:9195/http/order/findById? id=1111

View the log, see the obvious custom plugin log print, NICE!

O.D.S.P lugin. Httpclient. WebClientPlugin: The request urlPath is http://192.168.101.104:8188/order/findById? id=1111, retryTimes is 0 o.d.s.bootstrap.plugin.TimeRecordPlugin : Plugin time record: 9 ms o.d.soul.plugin.base.AbstractSoulPlugin : resilience4j selector success match , selector name :http_limiter o.d.soul.plugin.base.AbstractSoulPlugin : resilience4j rule success match , rule name :http_limiter o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http o.d.soul.plugin.base.AbstractSoulPlugin : divide rule success match , rule name :/http/order/findById o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://192.168.101.104:8188/order/findById? id=1111, retryTimes is 0 o.d.s.bootstrap.plugin.TimeRecordPlugin : Plugin time record: 10 msCopy the code

conclusion

The main processing analysis of the Soul Gateway is almost over, and the next chapter concludes with another important module: data synchronization

This article is easy to write custom plugins. There are no selectors or rules involved, but if you want to create plugins like Divide, you can write URI rules directly

Because all the requested data can be obtained, do not have to go to the background just the rules cannot change dynamically

Interested elder brother can try to write, or very interesting, ha ha

Soul Gateway source code analysis article list

Github

  • Soul source reading (a) overview

  • Soul source code reading (two) the initial run of the code

  • HTTP request processing overview

  • Dubbo Request Overview

  • Soul Gateway source code reading (five) request type exploration

  • Soul Gateway source code reading (6) Sofa request processing overview

  • Soul gateway source code reading (seven) a stream limit plug-in exploration

  • Soul gateway source code reading (eight) route matching exploration

  • Soul Gateway source code reading (nine) plug-in configuration load preliminary discussion

  • HTTP parameter request error

The Denver nuggets

  • Soul Gateway source code read (a) overview

  • Soul Gateway source code reading (two) the initial operation of the code

  • Soul Gateway source code reading (3) Request processing overview

  • Dubbo Request Overview

  • Soul Gateway source code reading (five) request type exploration

  • Soul Gateway source code reading (6) Sofa request processing overview

  • Soul gateway source code reading (seven) a stream limit plug-in exploration

  • Soul gateway source code reading (eight) route matching exploration

  • Soul Gateway source code reading (nine) plug-in configuration load preliminary discussion

  • HTTP parameter request error