Hello everyone, I'm not Cai Chen~

RPC, gRPC, Thrift, HTTP, do you know the connection and difference between them? These are common interview questions. Today, I will show you how to understand RPC and gRPC.

Before talking about gRPC, we need to understand what RPC is.

No BB, go directly to the article directory:

What is RPC?

RPC (Remote Procedure Call Protocol) remote procedure call protocol, the goal is to make remote service calls simpler and more transparent.

The RPC framework is responsible for shielding the underlying transmission method (TCP or UDP), serialization method (XML/Json/binary) and communication details. The service caller can call the remote service provider just like calling the local interface, without caring about the underlying communication Details and calling process.

Why use RPC?

When we have more and more businesses and more and more applications, naturally, we will find that some functions can no longer be simply divided or cannot be divided.

At this time, the public business logic can be extracted and formed into independent service applications, and the original and new applications can interact with those independent service applications to complete complete business functions.

So we urgently need an efficient means of communication between applications to fulfill this requirement, and it's time for RPC to show its talents!

Commonly used RPC frameworks

  • gRPC : Originally developed by Google, it is a language-neutral, platform-neutral, open source remote procedure call (RPC) system.
  • Thrift : thrift is a software framework for the development of scalable and cross-language services. It combines a powerful software stack and code generation engine to build between C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml programming languages Seamless, efficient service.
  • Dubbo : Dubbo is a distributed service framework and SOA governance scheme. Since Dubbo was open sourced in 2011, it has been used by many non-Alibaba companies.
  • Spring Cloud : Spring Cloud is composed of many sub-projects, such as Spring Cloud Config, Spring Cloud Netflix, Spring Cloud Consul, etc., which provide common tools for building distributed systems and microservices.

RPC call flow

To make network communication details transparent to users, we need to encapsulate the communication details. Let's first look at the communication details involved in the next RPC call process:

  1. The service consumer (client) calls the service in the local call mode;
  2. After the client stub receives the call, it is responsible for assembling methods, parameters, etc. into a message body that can be transmitted over the network;
  3. The client stub finds the service address and sends the message to the server;
  4. The server stub decodes the message after receiving it;
  5. The server stub calls the local service according to the decoding result;
  6. The local service executes and returns the result to the server stub;
  7. The server stub packages the returned result into a message and sends it to the consumer;
  8. The client stub receives the message and decodes it;
  9. The service consumer gets the final result.

The goal of RPC is to encapsulate these steps 2~8 so that users can be transparent to these details. Here is another picture on the Internet, which is clear at a glance:

What is gRPC?

gRPC is a high-performance, general-purpose open source RPC framework. It was developed by Google in 2015 mainly for mobile application development and is designed based on the HTTP/2 protocol standard. It is developed based on the ProtoBuf serialization protocol and supports many development languages.

Since it is an open source framework, both parties of the communication can carry out secondary development, so the communication between the client and the server will focus more on the content of the business level, reducing the focus on the underlying communication implemented by the gRPC framework.

As shown in the figure below, the DATA part is the business-level content, and all the following information is encapsulated by gRPC.

Features of gRPC

  • Cross-language use, support C++, Java, Go, Python, Ruby, C#, Node.js, Android Java, Objective-C, PHP and other programming languages;
  • Define the service based on the IDL file, and use the proto3 tool to generate the data structure, server interface and client Stub of the specified language;
  • The communication protocol is based on the standard HTTP/2 design, and supports bidirectional streaming, message header compression, multiplexing of single TCP, server push and other features. These features make gRPC more power-saving and network traffic on mobile devices;
  • Serialization supports PB (Protocol Buffer) and JSON. PB is a language-independent high-performance serialization framework based on HTTP/2 + PB, which ensures the high performance of RPC calls;
  • Easy to install and easy to scale (millions of RPCs per second can be achieved with this framework).

gRPC interaction process

  • After enabling the gRPC function, the switch acts as a gRPC client, and the collection server acts as a gRPC server;
  • The switch will construct the corresponding data format (GPB/JSON) according to the subscribed events, write the proto file through Protocol Buffers, establish a gRPC channel between the switch and the server, and send request messages to the server through the gRPC protocol;
  • After the server receives the request message, the server will interpret the proto file through Protocol Buffers, restore the data structure with the first defined format, and perform business processing;
  • After the data is processed, the server needs to use Protocol Buffers to recompile the response data, and send a response message to the switch through the gRPC protocol;
  • After the switch receives the reply message, it ends the gRPC interaction.

Simply put, gRPC is to establish a connection after the client and server have enabled the gRPC function, and push the subscription data configured on the device to the server.

We can see that the whole process is to use Protocol Buffers to define the structured data that needs to be processed in the proto file.

Protocol Buffers

You can understand that ProtoBuf is a more flexible and efficient data format . Similar to XML and JSON, it is very suitable for some high-performance data transmission scenarios that require response speed.

ProtoBuf has three main functions in the gRPC framework: defining data structures, defining service interfaces, and improving transmission efficiency through serialization and deserialization.

Why does ProtoBuf improve transmission efficiency ?

We know that when using XML and JSON for data compilation, the data text format is easier to read, but when data is exchanged, the device needs to spend a lot of CPU on I/O actions, which will naturally affect the entire transmission rate.

Unlike the former, Protocol Buffers will serialize strings and then transmit them, that is, binary data .

It can be seen that the content of the two is not much different, and the content is very intuitive, but the content encoded by Protocol Buffers is only provided for the operator to read. In fact, the transmission is not in this text form, but the serialized binary Data, the number of bytes will be much less than that of JSON and XML, and the rate will be faster.

How does gPRC support cross-platform and multi-language ?

It is also an advantage that Protocol Buffers comes with a compiler. The proto file mentioned above is compiled by the compiler. The proto file needs to be compiled to generate a similar library file, and data applications can be developed based on the library file.

What programming language is used to compile and generate this library file? Since the operation and maintenance personnel responsible for network equipment and server equipment in the existing network are often different from the same group, the operation and maintenance personnel may be accustomed to using different programming languages for operation and maintenance development, so one of the advantages of Protocol Buffers can be brought into play - cross-language .

From the above introduction, we draw the advantages of Protocol Buffers compared to JSON and XML in terms of encoding:

  • Standard IDL and IDL compiler, which makes it very engineer friendly;
  • The serialized data is very concise and compact. Compared with XML, the amount of serialized data is about 1/3 to 1/10;
  • The parsing speed is very fast, about 20-100 times faster than the corresponding XML;
  • It provides a very friendly dynamic library, which is very simple to use, and only one line of code is required for deserialization.

Protobuf also has its limitations:

  • Since Protobuf was produced by Google, it currently only supports three languages: Java, C++, and Python ;
  • Protobuf supports relatively few data types and does not support constant types;
  • Since its design concept is a pure presentation layer protocol (Presentation Layer), there is currently no RPC framework that specifically supports Protobuf.

Protobuf applicable scenarios:

  • Protobuf has a wide user base, small space overhead and high resolution performance are its highlights, it is very suitable for RPC calls within the company that require high performance ;
  • Since Protobuf provides standard IDL and corresponding compiler, its IDL file is a very strong business constraint for all parties involved;
  • Protobuf has nothing to do with the transport layer, using HTTP has good access properties across firewalls, so Protobuf is also suitable for scenarios with high performance requirements between companies;
  • Due to its high parsing performance and relatively small amount of data after serialization, it is very suitable for application layer object persistence scenarios;
  • The main problem is that it supports relatively few languages , and because there is no standard underlying transport layer protocol bound, it is relatively troublesome to debug the transport layer protocol between companies.

Design based on HTTP 2.0 standard

In addition to Protocol Buffers, as can be seen from the interaction diagram and layered framework, gRPC has another advantage - it is based on the HTTP 2.0 protocol.

Since gRPC is designed based on the HTTP 2.0 standard, it brings more powerful functions, such as multiplexing, binary frames, header compression, and push mechanisms.

These features bring significant benefits to the device, such as saving bandwidth, reducing the number of TCP connections, saving CPU usage, etc. gRPC can be applied both on the client side and on the server side, enabling communication between both ends in a transparent manner and simplifying communication Construction of the system.

HTTP 1.X defines four ways to interact with the server, namely GET, POST, PUT, and DELETE. These are all reserved in HTTP 2.0. Let's take a look at the new features of HTTP 2.0: bidirectional streaming, multiplexing, binary Frame, header compression.

Performance comparison

protobuf in binary format can be up to 5 times faster than JSON in text format!

The performance test results performed by the Auth0 website show that the advantages of protobuf and JSON are particularly obvious in environments such as Java and Python. The following figure shows the comparison test results performed by Auth0 between two Spring Boot applications.

The results show that protobuf takes at most about 20% of the request time of JSON, which is 5 times as fast!

Let's take a look at the performance and space overhead comparison.

From the above figure the following conclusions can be drawn:

  • XML serialization (Xstream) is poor in terms of performance and simplicity.
  • Compared with Protobuf, Thrift has certain disadvantages in terms of space and time overhead.
  • Protobuf and Avro are excellent in both areas.

gRPC in action

1. Project structure

Let's take a look at the project structure first:

2. Generate protobuf file

File helloworld.proto:

 syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

A SayHello() method is provided here, and the input parameter is HelloRequest, and the return value is HelloReply. You can see that the proto file only defines the format of the input parameter and return value, as well as the calling interface. As for the internal implementation of the interface, the file is completely Don't care.

File pom.xml:

 <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>rpc-study</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>grpc-demo</artifactId>

    <dependencies>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>1.14.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.14.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.14.0</version>
        </dependency>
    </dependencies>

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.5.0.Final</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.14.0:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>6</source>
                    <target>6</target>
                </configuration>
            </plugin>

        </plugins>
    </build>
</project>

The build here is actually to install the protobuf plugin. There are actually 2 plugins we need to use, namely protobuf:compile and protobuf:compile-javanano. When we execute it directly, the file on the left will be generated, of which GreeterGrpc provides the call Interface, the function of the file at the beginning of Hello is mainly to serialize the data, and then process the input parameters and return values.

Some students may ask, you generate files into target, I want to put them in main.src, you can copy these files, or you can generate them through tools:

3. Server and Client

File HelloWorldClient.java:

 public class HelloWorldClient {
    private final ManagedChannel channel;
    private final GreeterGrpc.GreeterBlockingStub blockingStub;
    private static final Logger logger = Logger.getLogger(HelloWorldClient.class.getName());

    public HelloWorldClient(String host,int port){
        channel = ManagedChannelBuilder.forAddress(host,port)
                .usePlaintext(true)
                .build();

        blockingStub = GreeterGrpc.newBlockingStub(channel);
    }


    public void shutdown() throws InterruptedException {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
    }

    public  void greet(String name){
        HelloRequest request = HelloRequest.newBuilder().setName(name).build();
        HelloReply response;
        try{
            response = blockingStub.sayHello(request);
        } catch (StatusRuntimeException e)
        {
            logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
            return;
        }
        logger.info("Message from gRPC-Server: "+response.getMessage());
    }

    public static void main(String[] args) throws InterruptedException {
        HelloWorldClient client = new HelloWorldClient("127.0.0.1",50051);
        try{
            String user = "world";
            if (args.length > 0){
                user = args[0];
            }
            client.greet(user);
        }finally {
            client.shutdown();
        }
    }
}

This is too simple, just connect to the service port and call the sayHello() method.

File HelloWorldServer.java:

 public class HelloWorldServer {
    private static final Logger logger = Logger.getLogger(HelloWorldServer.class.getName());

    private int port = 50051;
    private Server server;

    private void start() throws IOException {
        server = ServerBuilder.forPort(port)
                .addService(new GreeterImpl())
                .build()
                .start();
        logger.info("Server started, listening on " + port);

        Runtime.getRuntime().addShutdownHook(new Thread() {

            @Override
            public void run() {

                System.err.println("*** shutting down gRPC server since JVM is shutting down");
                HelloWorldServer.this.stop();
                System.err.println("*** server shut down");
            }
        });
    }

    private void stop() {
        if (server != null) {
            server.shutdown();
        }
    }

    // block 一直到退出程序
    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        final HelloWorldServer server = new HelloWorldServer();
        server.start();
        server.blockUntilShutdown();
    }

    // 实现 定义一个实现服务接口的类
    private class GreeterImpl extends GreeterGrpc.GreeterImplBase {
        @Override
        public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
            HelloReply reply = HelloReply.newBuilder().setMessage(("Hello " + req.getName())).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
            System.out.println("Message from gRPC-Client:" + req.getName());
            System.out.println("Message Response:" + reply.getMessage());
        }
    }
}

It mainly implements the sayHello() method, which simply processes the data. The input parameter is "World", and the return is "Hello World".

4. Start the service

Start the Server first and return as follows:

Restart the Client and return as follows:

At the same time, the Server returns the following:

Source address: https://github.com/lml200701158/rpc-study

write at the end

This article was actually written by me last year. This time it is reorganized. The article explains in detail RPC and gRPC, as well as application examples of gRPC. It is very comprehensive. I will sort out Thrift later.

This Demo looks very simple, I actually worked on it for a long time. At first, it was because I didn't know that I needed to execute 2 different plugins to generate protobuf. I thought that I only needed to click protobuf:compile. Need to click.

One last word (don't be a prostitute, please pay attention)

Every article of Chen Mou is carefully output. He has written 3 columns and organized them into PDF . The way to get it is as follows:

  1. "Spring Cloud Advanced" PDF: Follow the official account: [ Java Backend Interviewer ] Reply to the keyword Spring Cloud Advanced Get it!
  2. "Spring Boot Advanced" PDF: Follow the official account: [ Java Backend Interviewer ] Reply to the keyword Spring Boot Advanced Get!
  3. "Mybatis Advanced" PDF: Follow the official account: [ Java back-end interviewer ] Reply to the keyword Mybatis Advanced Get it!

If this article is helpful or inspiring to you, please help to like , watch , forward , and favorite . Your support is the biggest motivation for me to persevere!


码猿技术专栏
486 声望105 粉丝