SOFA

Scalable Open Financial Architecture

It is a finance-level distributed middleware independently researched and developed by Ant Financial, which contains all components required for the construction of the finance-level cloud native architecture. It is the best practice forged in the financial scene.

This is the anatomy of | SOFARPC framework “seventh article, the author mautner lu tao, from E to sign the Po.

The anatomy of | SOFARPC framework “series produced by SOFA team and source enthusiasts, SOFARPC source code parsing series, the official directory has all run out.


preface

We know that in RPC calls, the client needs to load the interface definition class provided by the server. However, this is not always feasible, so the need for generalized calls is derived. A mature and fully functional RPC framework generally supports generalized calls. How does SOFARPC support generalized calls? And how is it done? How is it different from other RPC generalization calls? What are the advantages? We will answer them all in this article.

Introduction to generalization calls

Generalization calls are required when the client cannot get the service provider’s interface JAR package for some reason, or when the client is a generic system that does not want to rely on the facade interface provided by each service provider, but needs to be invoked.


Such as:

  1. When a distributed system is developed by multiple languages, let’s say NodeJs, and NodeJs needs to call RPC service of Java language, then we need to set up an adaptation layer between the two, and let the adaptation layer process NodeJs request and then forward it to Java RPC service.

  2. Some of the functions of intermediate systems, such as some internal gateways, require calls to other downstream systems in a unified manner (in the case of non-SPIs), and it is obviously not possible to rely on downstream packages one by one.

  3. Some traffic playback class online systems can intercept data collection and then call playback through generalization without relying on site-wide applications.


In this case, you must not include jar files for all interfaces, otherwise it would be too bloated. In fact, it is unrealistic to add a JAR dependency every time a server is added, and then the application is published and restarted.

At this time, you can use the generalization call to wrap the corresponding request into the generalization call, so as to achieve independent interface JAR package, multi-language RPC service invocation, avoiding repeated development.

SOFARPC generalization call use

The official documentation for SOFARPC is very detailed, as described in the official Wiki generalization call. At the same time, in the source code example module, there are ready-made demo can run, readers can clone source code to read, here we briefly explain the use, so that we have an intuitive understanding.

The interface definition

In total, the generalization call has two apis that contain five methods, two of which are obsolete, that is, there are three main methods. Respectively is:

/** * generalization call * @returnNormal type (not GenericObject type) */ Object$invoke(String methodName, String[] argTypes, Object[] args); /** * supports generalization calls * @ when the parameter type cannot be loaded by the class loaderreturnIn addition to built-in types such as JDK, other objects are GenericObject */ Object$genericInvoke(String methodName, String[] argTypes, Object[] args); /** * supports generalization calls * @ when the parameter type cannot be loaded by the class loaderreturnReturns the specified T type return object */ <T> T$genericInvoke(String methodName, String[] argTypes, Object[] args, Class<T> clazz);Copy the code
  1. $invoke This method can be used if the user knows the parameter type and return value type.

  2. $genericInvoke This method is an overloaded method. Overload 1 is used when your application does not know the parameter type and return value type of the interface. In this case, you need to use the GenericObject class to wrap the return value and parameter.

  3. The $genericInvoke overload 2 is used in situations where the application does not know the type of the interface parameter, but knows the type of the interface return value, then there is no need to use GenericObject as the return value.

Basically, it covers common centralized scenarios and is quite comprehensive.

Generalization using

Because of the limited space, I will not post the demo here. If you are interested, you can check the official demo or source code, including the use of SOFARPC API and the use of SOFABoot:

  1. Demo Wiki address:

    http://www.sofastack.tech/sofa-rpc/docs/Generic-Invoke

  2. Source code address:

    https://github.com/alipay/sofa-rpc/tree/master/example/src/test/java/com/alipay/sofa/rpc/invoke/generic

Design and implementation of SOFARPC generalization call

Let’s focus on how SOFARPC implements generalization calls.

Framework call Design

In a nutshell, the key to generalized calls is object representation and serialization. SOFARPC provides objects such as GenericObject to represent parameter objects or return value objects, and serializes GenericObject objects into target objects. Or deserializing the return value into a GenericObject is the key to SOFARPC’s generalization.


Let’s first look at the flow chart of the SOFARPC generalization call to help you understand the generalization implementation.

Let’s take a look at this diagram:

  1. When the generalization API is called, the generalization filter is loaded to do some parameter conversions and set the serialization factory type.

  2. SOFARPC creates and passes to SOFABolt a context before making a network call with SOFABolt. This context contains the serialization factory type information that determines which serializer to use, and this context will flow throughout the call.

  3. Before SOFABolt formally sends the data, it serializes the GenericObject object into a byte stream of ordinary objects, so that the service provider does not have to care about whether it is a generalization call. As you can see from the figure, The provider does not make any changes to the generalization call — this is what differentiates SOFARPC generalizations from other RPC generalizations.

  4. When the provider successfully receives the request, the data can be deserialized using the normal serializer, which is simply called and returned as normal.

  5. When the consumer’s SOFABolt receives the response data, it deserializes the return value based on the context’s serialization type, that is, deserializing the ordinary byte stream into a GenericObject object — because the client might not know the Class type of the return value.

  6. Finally, the generalized API returns a value of type GenericObject.


As you can see from the flow above, serializers play a significant role in generalization calls. SOFARPC has modified Hessian3 to support serialization for generalized calls. Changes to SOFA-Hessian can be found here.

Hessian generalization implementation

SOFA – Hessian joined in the Hessian bag com. Alipay. Hessian. Generic package, the package of the role is to handle calls for generalization, The key to the rewrite is to implement or inherit the SerializerFactory class and Serializer, Deserializer and other interfaces. Here, several classes are designed to describe the corresponding type information in, and realize the serialization and deserialization of these classes. The corresponding relationship is as follows



Take GenericObjectSerializer as an example. The serializer rewrites the writeObject method, which serializes a GenericObject object into a target object stream. That is, take the Type and Fields of the GenericObject and assemble the byte stream of the target object.


Such as:

There is an RPC object of type

public class TestObj {
    private String str;
    private int    num;
}Copy the code

In the generalization call client, you can construct a GenericObject directly

  GenericObject genericObject = new GenericObject(
                    "com.alipay.sofa.rpc.invoke.generic.TestObj");
                genericObject.putField("str"."xxxx");
                genericObject.putField("num", 222);Copy the code


In this case, GenericObjectSerializer can use this information to convert GenericObject into a byte stream of TestObj objects. The service provider can then retrieve the object through plain Hessian2 deserialization.

SOFARPC is much friendlier than other RPC frameworks that require generalization support on both ends. That is, if the application wants to support generalization, it simply upgrades the client (consumer), and the server (provider) is agnostic. As far as the server is concerned, the objects received are exactly the same. You might think that writing such a construct would be difficult for complex types. A utility class is already provided in SOFA-Hessian

com.alipay.hessian.generic.util.GenericUtilsCopy the code

To assist the user to generate, can be used directly.

SOFARPC vs. Dubbo generalization call comparison

Let’s take a look at how generalized calls compare with some of the industry’s other products, starting with some performance and advantage comparisons of serialization itself.

Comparison of serialization itself

On Github, there is a benchmark for Java serialization that you can use for a little reference. Although in actual scenarios, each serialization scenario may be different from the benchmark results here, the results are still useful for reference. As can be seen from the benchmark test of this project, Json has a considerable disadvantage compared to Hessian in terms of compression ratio and serialization time.

At the same time, although hessian has a slight performance gap compared to Protostuff, Kryo, etc., hessian deserialization does not require a specified type, which is a very valuable advantage.

Dubbo generalization call

Among many RPC frameworks, Dubbo also provides the ability to generalize calls, so let’s talk about Dubbo generalization next. The biggest difference between Dubbo generalization and SOFA RPC generalization is that Dubbo requires the server to support generalization as well, so to provide generalization functionality, the server must also be upgraded, which may seem less friendly than SOFA RPC.


Dubbo’s generalization call flow is shown below:

As you can see, Dubbo’s server side also needs a generalization filter to parse the Map into POJOs to parse the data.

conclusion

This article mainly explains SOFARPC generalization call design and implementation, introduced the generalization call scenario, at the same time, mentioned SOFARPC generalization call API use, also explained in detail SOFARPC generalization design and implementation. Finally, a simple comparison is made between the generalization calls of some RPC frameworks in the community.


Here is a summary of the generalization design and implementation of SOFARPC:

  1. The design goal is that the server does not need to be aware of generalization, everything is handled by the client.

    The advantage is that applications that want to support generalization do not need to change the server side, only the client side. This is the biggest difference from other RPC framework generalization calls.

  2. Implementation method: Generic serialization is supported with SOFA-Hessian serialization. Bolt uses the corresponding serializer based on the context’s serialization marker when making a generalization call. The SOFA-Hessian-specific generic serializer serializes a GenericObject into a stream of bytes for the target object. The server can deserialize as normal. SOFA- Hessian-specific generalized deserializer can also deserialize the target return value into objects such as GenericObject.

reference

https://github.com/eishay/jvm-serializers

https://github.com/alipay/sofa-hessian

http://www.sofastack.tech/sofa-rpc/docs/Generic-Invoke