Here is my substack link: open.substack.com/pub/minyizo…
This is my third article for 第六届青训营。
Concept
You have the option to create your own RPC framework, which is in line with the approach taken by the CMU lab’ s requirement:
www.andrew.cmu.edu/course/14-7…
For this lab, I designed a client that allows developers to interact with objects stored on remote servers and receive replies from methods implemented in the server. The library enables developers to invoke remote methods on server objects and get return values in a method. I've created a server that calls the NewService and server start functions in the remote library using a for-loop and checks the server's status at the end of each iteration. The server will implement all interface methods and provide the results to the client.
The server creates a service object, which accepts a server object and the server interface as input parameters. The server interface explains the methods that the client can call from a remote location, whereas the server object contains the actual server name. The NewService function initializes the service object and starts listening for incoming connections on a given port. The Start method accepts connections and the Stop method ends the service.
The client uses the StubFactory() function to construct a stub object that corresponds to the server interface. This function accepts the stub object, the server address, and two boolean values. After the stub object is constructed, the client may use its methods to remotely call the server object's equivalent methods. However, in order to present the responses better I provide considerable log print and hardcode the logics. The client application initiates a request to the server to determine whether John is a student, using the method IsStudent, with a string argument and a boolean response. The client also requests TA records by calling ReturnTARecords, which returns a map. Next, the client registers a person named John using the method Register, which takes a Person object as an argument and returns a boolean value. Finally, the client creates a student record for John using the NewCSStudent method, which requires a Person object and returns a Student object.
To implement a basic RPC framework in go is easy but when it comes to optimize code would be complicated.
Furthermore, if RPC catches your interest, feel free to review this paper as well.
web.eecs.umich.edu/~mosharaf/R…
During the RPC runtime, when a user invokes a function locally, the user stub will encapsulate the message and send it to the callee. Upon receiving the message, the server-stub will extract and execute the corresponding function, then send the response back to the caller.
Compared to local function calls, RPC calls need to address the following issues:
- Function mapping
- Data conversion to byte streams
- Network transmission
The architecture of Thrift looks like this:
reference: sunchao.github.io/posts/2015-…
Processor & Protocol
Both the client and server utilize the same Interface Definition Language (IDL) file, allowing it to support code generation in various programming languages.
Some important points:
Language-specific formats, such as java.io.Serializable, are designed for a particular programming language or platform.
Text formats, such as JSON, XML, CSV, etc., are human-readable and widely used for data interchange between systems.
Binary encodings, like Thrift's BinaryProtocol and Protobuf, offer more compact and efficient representations. Implementations may vary, such as TLV (Tag-Length-Value) encoding and Varint encoding.
Protocol
- LENGTH (32 bits): Represents the size of the remaining part of the data packet in bytes, excluding the length of the LENGTH field itself.
- HEADER MAGIC (16 bits): Set to the value 0x1000, serving as an identifier for the protocol version, enabling quick verification during protocol parsing.
- FLAGS (16 bits): A reserved field currently unused, with the default value of 0x0000.
- SEQUENCE NUMBER (32 bits): Indicates the sequence ID of the data packet, allowing for multiplexing, and should ideally be monotonically increasing within a single connection.
- HEADER SIZE (16 bits): Equals the number of header bytes divided by 4. The header length is calculated from the 14th byte until just before the PAYLOAD section. (Note: The maximum length of the header is 64K).
- PROTOCOL ID (uint8 encoding): Can have two values: - ProtocolIDBinary = 0 - ProtocolIDCompact = 2
- NUM TRANSFORMS (uint8 encoding): Represents the number of TRANSFORMs.
- TRANSFORM ID (uint8 encoding): Indicates the compression method, such as zlib or snappy.
- INFO ID (uint8 encoding): Specific values are defined in the subsequent sections and used to transmit custom meta-information.
- PAYLOAD: Contains the actual message content.
Retrieve the MagicNumber, which corresponds to the HEADER MAGIC and indicates the protocol type. Next, determine the payload type using the PayloadCodec, and finally, decode the payload accordingly.
Transport
Use socket API to implement the transport layer, here is the layer model:
How to optimize RPC
Stability
send backup request when timeout
Left one is normal request, right one is using backup request
Scalability
Middleware: Middleware will be constructed into an ordered chain of calls, executed one by one, for tasks such as service discovery, routing, load balancing, timeout control, etc. Option: Used as initialization parameters. The core layer is extensible: encoding, protocols, and network transport layer. The code generation tool also supports plugin extensions.
Other idea:
Reuse goroutine: create goroutine pool.
Useful link: juejin.cn/post/719226…