Introduction to the

This time, we will start a dubbo service sample to explore the Soul gateway source code dubbo request processing process

An overview of

Request processing overview

Let’s take a look at how proxy forwarding of Dubbo requests differs from HTTP; Went through those classes; Did those operations

The sample run

Environment configuration

After the Soul source code is cloned, there is a soul-example directory, which is the example project, and there are many examples to run

Maybe the initial folder is not recognized by IDEA as a Java project. We need to click the pom. XML file in the change project directory, right click it, and choose Add as Maven Project from the menu

We chose to run: soul-examples –> soul-examples-apache-dubbo-service

This example requires mysql and ZooKeeper, so let’s use Docker to start the database. Remember to change the soul-admin database configuration

docker run -dit --name zk -p 2181:2181 zookeepe
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest
Copy the code

A test run

Then run soul-admin, soul-bootstrap, you can refer to: Soul gateway source code parsing (ii) preliminary run code

Run the soul – examples – apache – dubbo – service

Log in to the soul-admin management page at http://localhost:9095/ with the user name and password admin 123456

Enter the interface: System Management > Plug-in Management

In plugin: Dubbo, click Edit and change the status to On (a switch icon)

After this function is successfully enabled, you can see the related logs in soul-Bootstrap, which are similar to the following:

o.d.s.p.a.d.c.ApplicationConfigCache     : init aliaba dubbo reference success there meteData is :MetaData
o.d.s.p.a.d.c.ApplicationConfigCache     : init aliaba dubbo reference success there meteData is :MetaData
o.d.s.p.a.d.c.ApplicationConfigCache     : init aliaba dubbo reference success there meteData is :MetaData
Copy the code

Go to the soul-admin admin interface: Plug-in Management –> dubbo, we can see many configurations, these are the soul-examples-apache-dubbo-service configuration

Grab a query interface to: http://localhost:9195/dubbo/findAll

Using a browser, the results are as follows:

{
    "code": 200."message": "Access to success!"."data": {
        "name": "hello world Soul Apache, findAll"."id": "1206394682"}}Copy the code

Good, you have successfully run the sample, next, enter the source code to debug

Source code analysis

Based on the previous article, we have seen the initial process of processing an HTTP request: Soul Source Read 3- Request Processing Overview

Based on the familiarity of the previous article, let’s take a look at how the Dubbo process differs from Http and what each Plugin does

The core chained processing class of Plugins is SoulWebHandler, which is where we start to break through and step into each Plugin

        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

GlobalPlugin

The code is not very understand, guess there are the following functions:

  • Get the upgrade of the request header, null at this time, and enter the logic
  • The other branch of MultiValueMap is a bit like file upload and so on
  • The SoulContext function is modified

That’s not the goal of this analysis, so I’ll skip it here

SignPlugin

This should be a certification related plug-in, but in the access to plug-in information, did not open, do not enter the logic, directly skip, did not enter the specific execution

Here we find that some of the functions need to go to: execute, others go directly to: doExecute. AbstractSoulPlugin needs to go directly into doExecute, but AbstractSoulPlugin needs to go directly into execute. This is not the point, so I will not continue to study this, but have a general understanding

WafPlugin, RateLimiterPlugin, HystrixPlugin, Resilience4JPlugin

The above several are to obtain plug-in information, not open, do not enter the logic, directly skip

DividePlugin

So here it’s true, it’s a little bit different than what I thought, so if I look at the admin screen and I see divide turned on, dig a little bit

Skip = plugin.skip(exchange); skip(exchange);

    public Boolean skip(final ServerWebExchange exchange) {
        final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
        return! Objects.equals(Objects.requireNonNull(soulContext).getRpcType(), RpcTypeEnum.HTTP.getName()); }Copy the code

It seems that it is the type that returns the plugins to whether or not to skip other plugins. How do these things come from? I won’t analyze them today

WebClientPulugin, WebsocketPlugin

Skip is true and directly skips. The judgment of skip is basically the same as that of DividePlugin and can be judged by type

BodyParamPlugin

SoulContext has some interaction with GlobalPlugin, MediaType and other keywords, it feels like file upload or something, but the details are not clear, this is not the purpose of this analysis, do not get into the details, this time let it go, next time when you have time to study more carefully

AlibabaDubblePlugin

This plugin is the heart of the game and does three things

  • After entering and matching the rules, it is found that the plugin is a general plugin for Apache Dubbo and can also handle Apache Dubbo, i.e. they are identical or reused
  • Enter the doExecute function: get the body, soulContext, and soulContext. Check the soulContext to find the method, path, and other information
  • Get the result of the request, and in exchange: the Object result. = alibabaDubboProxyService genericInvoker (body, metaData)

The code looks like this:

    protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
        String body = exchange.getAttribute(Constants.DUBBO_PARAMS);
        SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
        assertsoulContext ! =null;
        // Dubbo requests related data
        MetaData metaData = exchange.getAttribute(Constants.META_DATA);
        if(! checkMetaData(metaData)) {assertmetaData ! =null;
            log.error(" path is :{}, meta data have error.... {}", soulContext.getPath(), metaData.toString());
            exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            Object error = SoulResultWrap.error(SoulResultEnum.META_DATA_ERROR.getCode(), SoulResultEnum.META_DATA_ERROR.getMsg(), null);
            return WebFluxResultUtils.result(exchange, error);
        }
        if (StringUtils.isNoneBlank(metaData.getParameterTypes()) && StringUtils.isBlank(body)) {
            exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            Object error = SoulResultWrap.error(SoulResultEnum.DUBBO_HAVE_BODY_PARAM.getCode(), SoulResultEnum.DUBBO_HAVE_BODY_PARAM.getMsg(), null);
            return WebFluxResultUtils.result(exchange, error);
        }
        // Get the request result
        Object result = alibabaDubboProxyService.genericInvoker(body, metaData);
        if (Objects.nonNull(result)) {
            // Put the result in exchange
            exchange.getAttributes().put(Constants.DUBBO_RPC_RESULT, result);
        } else {
            exchange.getAttributes().put(Constants.DUBBO_RPC_RESULT, Constants.DUBBO_RPC_RESULT_EMPTY);
        }
        exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.SUCCESS.getName());
        return chain.execute(exchange);
    }
Copy the code

Tracing back to the function that fetched result, it is clear from the following code that this is indeed an RPC call and the result

    public Object genericInvoker(final String body, final MetaData metaData) throws SoulException {
        Metadata.getpath ()==/dubbo/findAll
        / / ApplicationConfigCache. GetInstance (). Get the logic of feeling is more complex, don't look at the first, general feeling is initialized using the path as the key
        ReferenceConfig<GenericService> reference = ApplicationConfigCache.getInstance().get(metaData.getPath());
        if (Objects.isNull(reference) || StringUtils.isEmpty(reference.getInterface())) {
            ApplicationConfigCache.getInstance().invalidate(metaData.getPath());
            reference = ApplicationConfigCache.getInstance().initRef(metaData);
        }
        // This looks a lot like the object generated by retrieving bytecode in RPC Demo
        GenericService genericService = reference.get();
        try {
            Pair<String[], Object[]> pair;
            if (ParamCheckUtils.dubboBodyIsEmpty(body)) {
                pair = new ImmutablePair<>(new String[]{}, new Object[]{});
            } else {
                pair = dubboParamResolveService.buildParameter(body, metaData.getParameterTypes());
            }
            // Pass the method name, parameter name, parameter? Dubbo is not yet proficient, so it does not affect the overall situation of the analysis
            return genericService.$invoke(metaData.getMethodName(), pair.getLeft(), pair.getRight());
        } catch (GenericException e) {
            log.error("dubbo invoker have exception", e);
            throw newSoulException(e.getExceptionMessage()); }}Copy the code

For reference.get(), just to be curious, take a look at ReferenceConfig

    public synchronized T get(a) {
        // Check whether it can be used
        if (this.destroyed) {
            throw new IllegalStateException("Already destroyed!");
        } else {
            // Has the effect of lazy loading
            if (this.ref == null) {
                this.init();
            }

            return this.ref; }}Copy the code

It has a lazy loading effect. It seems that you can change the client proxy generation of your OWN RPC Demo by following this idea

MonitorPlugin

Get plug-in information, do not open, do not enter the logic, directly skip

WebClientResponsePlugin

Skip = true

DubboResponsePlugin

Is a core class, as the name indicates, and we need to break the code segment that executes after then.

    public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
        return chain.execute(exchange).then(Mono.defer(() -> {
            // Retrieve the result from exchange
            final Object result = exchange.getAttribute(Constants.DUBBO_RPC_RESULT);
            try {
                if (Objects.isNull(result)) {
                    Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null);
                    return WebFluxResultUtils.result(exchange, error);
                }
                Object success = SoulResultWrap.success(SoulResultEnum.SUCCESS.getCode(), SoulResultEnum.SUCCESS.getMsg(), JsonUtils.removeClass(result));
                // Return the response to the client using the familiar: exchange.getresponse ().writewith
                return WebFluxResultUtils.result(exchange, success);
            } catch (SoulException e) {
                returnMono.empty(); }})); }Copy the code

After executing the plugins chain, the breakpoint is reached: the result is roughly obtained, and success is processed

Then WebFluxResultUtils. Result (exchange, success), after entering is: exchange. The method getResponse () writeWith, returns a response to the client

At this point, the process of processing a Dubbo request is roughly presented in front of us. With the analysis foundation of the previous several articles, it is quite smooth

conclusion

This analysis verifies some of the assumptions made in the previous article, and also provides some new information. The rough flow chart for request processing is as follows:

  • HttpServerOperations: The obvious place where Netty’s requests are received, the request entry
  • ReactorHttpHandlerAdapter: to generate the response and request
  • HttpWebHandlerAdapter: Exchange generation
  • FilteringWebHandler: Indicates the filter operation
  • SoulWebHandler: Plugins call

An overview of HTTP request processing

In this paper, we analyze the relevant processing of Plugin for each Dubbo in detail. Some complicated ones are omitted, but we also get the results we want from this analysis. We have a certain understanding of how to process an RPC request and which components to design, based on which we can conduct further analysis

We learn that HTTP request invocation, RPC request invocation, and Websocket request invocation are mutually exclusive. We select one of them by judging the type of request, and the CORRESPONDING HTTP response and RPC response will be converted into HTTP response, and then returned to the client. Exchange is bound to a socket, and then call it directly.

The analysis raises new questions:

  • How does the Websocket response return? Is it a long connection? There is no Websocket response handler class
  • How are request types bound and generated in Exchange?
  • Do plugins such as current limiting also need to be matched? What is the execution logic?

Leave the above questions for later analysis, and take your time to achieve sustainable development

Soul Gateway source code parsing article list

The Denver nuggets

Understanding and preliminary operation

  • Soul Gateway source code parsing (a) overview
  • Soul gateway source code analysis (two) the initial operation of the code

Request processing flow parsing

  • Soul Gateway source code parsing (iii) request processing overview
  • Soul Gateway source code parsing (4) Dubbo request overview
  • Soul gateway source code analysis (five) request type exploration
  • Soul Gateway source code analysis (six) Sofa request processing overview
  • Soul gateway source code analysis (seven) limit the flow plug-in exploration
  • Soul gateway source code analysis (eight) route matching exploration
  • Soul gateway source code analysis (nine) plug-in configuration loading preliminary
  • Soul gateway source code analysis (ten) custom simple plug-in preparation
  • Soul gateway source code parsing (11) request processing summary

Data Synchronization parsing

  • Soul gateway source code analysis (12) data synchronization
  • Soul gateway source code parsing (thirteen) Websocket synchronization data -Bootstrap end
  • Soul Gateway source code parsing (fourteen) HTTP data synchronization -Bootstrap end
  • Zookeeper data synchronization -Bootstrap end
  • Soul gateway source code analysis (sixteen) Nacos data synchronization example run
  • Soul gateway source code parsing (seventeen) Nacos data synchronization parsing -Bootstrap end
  • Soul gateway source code parsing (18) Zookeeper data synchronization preliminary -Admin end
  • Soul Gateway source code parsing (19) Nacos data synchronization initialization fix -Admin end
  • Soul gateway source code parsing (20) Websocket data synchronization -Admin end
  • Soul Gateway source code parsing (21) HTTP long polling data synchronization -Admin end
  • Soul gateway source code analysis (22) data synchronization summary

Soul – the Client module

  • Soul gateway source code analysis (23) SoulSpringMvcClient annotations
  • Soul gateway source code analysis (24) SoulDubboClient notes
  • Soul gateway source code analysis (25) soul-client module summary

The overview

  • Soul gateway source code analysis (26) preliminary overview

One day

  • HTTP parameter request error