preface

Remote Procedure Call (RPC) is a Remote Procedure Call (RPC) service. RPC is a service that allows a program to call a class method or function in another address space, usually on another machine. It is a technology that is built on top of a computer network and hides the underlying network. It can call a remote program as if it were a local service, increasing throughput at a low coding cost.

Why do you want to use the RPC service With the rapid development of computer technology, the single machine running service scheme has strong enough to support more and more network request load, distributed scheme began to rise, a business scenario can be broken up run on multiple machines, each machine to complete only one or a few business module. To enable other machines to use the business module methods in one machine, there is an RPC service, which is a service done over a protocol that implements remote method invocation. Nowadays, many mainstream languages support RPC services, such as Java Dubbo, Go NET/RPC & RPCX, Google gRPC and so on.

About gRPC most RPC is based on socket implementation, can be more efficient than HTTP request. GRPC is a high-performance RPC service framework developed and open source by Google. It is based on the HTTP2.0 protocol and currently supports C, C++, Java, Node.js, Python, Ruby, Objective-C, PHP, C# and other languages. To transfer method calls, call parameters, and response parameters between the two servers, these parameters need to be serialized. GRPC uses the protocol buffer syntax (check PROTO), which defines the method to be called, and parameters, and the response format. Remote method calls are easy to do, and it’s great for extending and updating parameters.

Get started with gRPC quickly

To implement remote method calls using gRPC, we need to understand the Protocol Buffer syntax, install tools that support the Protocol Buffer syntax to compile. Proto files, and then complete the server (remote method provider) and client (caller) construction and encapsulation of gRPC.

Understand the protocol buffer

Protocol Buffer is Google’s cross-language, cross-platform, extensible mechanism for serializing structured data – a smaller, faster, and simpler data format than XML. You can define the structure of the data, such as method names, parameters, and response formats, and then easily write and read structured data in various languages in various data flows using the source code generated by the corresponding language tool.

Syntax using

  1. Defining message types
package test;
syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
Copy the code

The example above is a.proto file where the first line specifies the package name so that you can import the file’s definition into another proto file, and the second line says that you are using proto3 syntax: if you don’t, the Protobuf compiler will assume that you are using Proto2. This must be the first non-empty, non-commented line of the file. Proto3 syntax is currently recommended. SearchRequest is the name of the body of the message. It specifies the type and order of the three fields. The order must start from 1 and cannot be repeated.

  1. Specify field rules Message fields can be one of the following:

Singular (default) : A well-formed message can contain zero or one (but no more than one) of this field. Repeated: This field can be repeated any number of times (including zero) in a well-formed message. The order of repeated values is preserved. Such as:

syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  repeated Body body = 4;
}

message Body {
  int32 id = 1;
  string number = 2;
}
Copy the code

The above example defines a format, expressed in our usual JSON format:

{
    "query": str,
    "page_number":int,
    "result_per_page":int,
    "body":[
        {
            "id":int,
            "number":str
        }
    ],
}
Copy the code
  1. Scalar value types A scalar message field can have one of the following types – shown in this table. The type specified in the proto file, and the corresponding type in an automatically generated class:
.proto Type note Python Typ
double float
float float
int32 Using variable-length encoding is inefficient for negative values, so if your field is likely to have negative values, use sint64 instead int
uint32 Use variable-length coding int/long
uint64 Use variable-length coding int/long
sint32 Use variable length codes, which are much more efficient at negative values than INT32 int
sint64 Use variable-length encoding, signed integer values. Encoding is more efficient than the usual INT64. int/long
fixed32 It is always 4 bytes, and if the value is always greater than or equal to 228, this type is more efficient than the uint32. int
fixed64 Always 8 bytes. If the value is always greater than or equal to 256, this type is more efficient than uint64. int/long
sfixed32 It’s always 4 bytes int
sfixed64 It’s always 8 bytes int/long
bool Boolean value bool
string A string must be utF-8 encoded or 7-bit ASCII encoded text. str/unicode
bytes May contain bytes of data in any order. str
  1. Default Value When a message is parsed, if the encoded message does not contain a particular singular element, the corresponding field in the parsed object is set to the default value for that field. These defaults are type specific:
  • For strings, the default is an empty string.
  • For bytes, the default value is null bytes.
  • For bools, the default is false.
  • For numeric types, the default value is zero.
  • For enumerations, the default value is the first enumerated value defined, which must be 0.
  • Duplicate fields default to empty (usually an empty list for the corresponding language)
  1. Enumerated type
message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }
  Corpus corpus = 4;
}
Copy the code

The first constant mapping of the Corpus enumeration is zero: Each enumeration definition must contain a constant mapping to zero as its first element. This is because:

  • There must be a zero value so that we can use 0 as the numeric default.
  • The zero value must be the first element in order to be compatible with proto2 semantics, where the first enumeration value is always the default.
  1. Define methods
Service SearchService {RPC Search (SearchRequest) returns (SearchResponse); }Copy the code

The above statement defines the Search method name for the remote call. After compiling the source code for the corresponding language, you can use the remote call. For example, if you initialize the SearchService method in Python, execute the Search method. It is to call the method of the remote machine in the format of SearchRequest, and then return the call result according to the defined SearchResponse format. According to proto’s syntactic definition, it is even possible to use such remote calls across platforms and languages.

Use tools to generate source code for the corresponding language

Generate the following custom message types for Java, Python, C ++, Go, Ruby, Objective-C, or C #. Proto files based on the actual work you need to run the Protobuf compiler on protoc. If you do not have a compiler installed, download the package and follow the instructions in the readme file. The Protobuf compiler makes the following calls:

protoc --proto_path = IMPORT_PATH --cpp_out = DST_DIR --java_out = DST_DIR --python_out = DST_DIR --go_out = DST_DIR --ruby_out = DST_DIR --objc_out = DST_DIR --csharp_out = DST_DIR  path / to / file .proto

Copy the code

Python generates the corresponding source code

  1. Install the gRPC source package grpcio for Python to implement the various underlying protocols and request response methods of gRPC
  2. Install grpcio-tools, which generates Python source code based on gRPC proto
sudo python -m pip install grpcio

python -m pip install grpcio-tools
Copy the code
  1. Proto serialization protocol source code for Python
Compile the proto filepython -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. test.proto python -m grpc_tools.protoc: The Protoc compiler for Python is implemented by python modules, so this step is very easy --python_out=. : --grpc_python_out=. : Compile generates path for grPC-related code to the current directory -- i.test. proto: Path to the proto file, where the proto file is in the current directoryCopy the code

Source code generated after compilation:

  • Test_pb2.py: used to interact with protobuf data. This is the Pythonized data structure file generated according to the data structure type defined by proto
  • Test_pb2_grpc. py: used to interact with GRPC. This is the class that defines RPC methods, including the request parameters and response of the class, and can be directly instantiated by Python

Build the Python gRPC service

With the gRPC class generated that Python can instantiate and invoke directly, we can start building the server (remote call provider) and client (caller) sides of RPC.

  1. Build the server server.py
from concurrent import futures
import time
import grpc
import test_pb2
import test_pb2_grpc

# Implement the SearchService defined in the proto file
class RequestRpc(test_pb2_grpc.SearchService):
    Implement RPC calls defined in the proto file
    def doRequest(self, request, context):
        return test_pb2.Search(query = 'hello {msg}'.format(msg = request.name)) The # return data is in the defined SearchResponse format

def serve():
    The maximum receive and send sizes (in M) can be defined. The default size is only 4M
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10), options=[
        ('grpc.max_send_message_length', 100 * 1024 * 1024),
        ('grpc.max_receive_message_length', 100 * 1024 * 1024)])
    
    test_pb2_grpc.add_SearchServiceServicer_to_server(RequestRpc(), server)
    server.add_insecure_port('[: :] : 50051')
    server.start()
    try:
        while True:
            time.sleep(60*60*24) # one day in seconds
    except KeyboardInterrupt:
        server.stop(0)

if __name__ == '__main__':
    serve()
Copy the code
  1. Build the client client.py
import grpc
import helloworld_pb2
import helloworld_pb2_grpc

def run():
    Connect to the RPC server
    channel = grpc.insecure_channel('localhost:50051')
    Call the RPC service
    stub = test_pb2_grpc.SearchServiceStub(channel)
    response = stub.doRequest(test_pb2.SearchRequest(query='henry'))
    print("client received: ", response)

if __name__ == '__main__':
    run()
Copy the code

Best practices

  1. When writing proto files, pay attention to the definition of data format, to consider more expansibility, for example, you can define API_version to distinguish versions, to prevent future versions of large data format update when compatible;
  2. For immutable types, enumerations are recommended. For example, when a field type is requested, enumerations can be used if the value is fixed.
  3. You are advised to specify the maximum receiving and sending sizes for the server and client to avoid data overflow exceptions.
  4. GRPC occasionally breaks and reconnects, so add an exception handling mechanism to catch a remote call failure due to reconnection and retry (more on that in a future article);
  5. GRPC can use SSL or TLS protocols to implement HTTP2.0 encrypted transmission and improve system security (more on this in the next article).
  6. For services with heavy traffic and concurrency, some applications or components (such as ISTIO) of microservices can be used to fuse and limit traffic to improve stability.

The advantage of gRPC

performance

GRPC messages are serialized using protobuf, a valid binary message format. Protobuf serialization on the server and client is very fast. Protobuf serialized messages are small and payload capable, making them important in limited bandwidth scenarios such as mobile applications.

GRPC is designed for HTTP/2, a major version of HTTP with significant performance advantages over HTTP 1.x:

  • Binary framework and compression. The HTTP/2 protocol is compact and efficient in both sending and receiving.
  • Multiplexing multiple HTTP/2 calls over a single TCP connection. Multiplexing eliminates wire head blocking.

Code generation

All gRPC frameworks provide first-class support for code generation. The core file of gRPC development is the *.proto file, which defines the conventions for gRPC services and messages. From this file, the gRPC framework will generate the service base classes, messages, and complete client code.

By sharing *.proto files between the server and client, messages and client code can be generated end-to-end. Client-side code generation eliminates duplicate messages on both the client and server and creates a strongly typed client for you. No need to write client-side code can save a lot of development time in an application with many services.

Strict specification

There is no formal specification for the HTTP API with JSON. Developers don’t need to discuss the best format for urls, HTTP verbs, and response code. (Think about it: Post or Get? Better to use Get or Put? Is the thought of having a phobia of choice causing you to struggle and waste a lot of time?)

The gRPC specification is the format that the gRPC service must follow. GRPC eliminates debate and saves developers time because gPRC is consistent across platforms and implementations.

flow

HTTP/2 provides the basis for long-term real-time communication flows. GRPC provides first-class support for streaming media over HTTP/2.

The gRPC service supports all stream combinations:

  • One yuan (no streaming)
  • Server-to-client flow
  • Client-to-server flow
  • Two-way streaming end time/timeout and cancel gRPC allow clients to specify how long they are willing to wait for the RPC to complete. The deadline is sent to the server, which can decide what action to take if the deadline is exceeded. For example, the server might cancel an ongoing gRPC/HTTP/database request when a timeout occurs.

Resource usage restrictions can be enforced through subGRPC call expiration times and cancellation operations.

GRPC scenarios are recommended

  • Microservices – gRPC is designed for low latency and high throughput communication. GRPC is well suited for lightweight microservices where efficiency is critical. Point-to-point real-time communication – gRPC provides excellent support for two-way streaming media. The gRPC service can push messages in real time without polling. Multilingual Hybrid Development Environment – THE gRPC tool supports all popular development languages, making gRPC an ideal choice for multilingual development environments.
  • Network Constrained Environment – Serialize gRPC messages using Protobuf, a lightweight message format. The gRPC message is always smaller than the equivalent JSON message.

reference

  1. Juejin. Cn/post / 684490…
  2. Doc.oschina.net/grpc?t=5800…
  3. Juejin. Cn/post / 684490…
  4. www.jianshu.com/p/43fdfeb10…