In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article focuses on "what are the RPC details of the micro-service architecture". Interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Now let the editor take you to learn "what are the RPC details of the micro-service architecture"?
What are the benefits of service?
One of the benefits of service-oriented is that it does not limit the technology selection used by the service provider, and can achieve technology decoupling across teams of large companies, as shown in the following figure:
Service A: European team maintenance, technical background is Java
Service B: maintenance by team America, implemented with C++
Service C: China team maintenance, technology stack is go
The upstream caller of the service can complete the call to the remote service according to the interface and protocol.
But in fact, most Internet companies, with limited R & D teams, use the same set of technology systems to implement services:
In this way, without a unified service framework, the service providers of each team need to implement a set of "out-of-business" repetitive technical efforts such as serialization, deserialization, network framework, connection pooling, sending and receiving threads, timeout processing, state machines, and so on, resulting in overall inefficiency.
Therefore, the unified service framework unifies the above-mentioned "out-of-business" work, which is the primary problem to be solved.
What is RPC?
Remote Procedure Call Protocol, remote procedure call.
What is "remote" and why "far"?
Let's take a look at what "near" is, that is, "local function call".
When we write down:
Int result = Add (1,2)
What happened to this line of code?
Pass two input parameters
The functions in the local code snippet are called to perform the operation logic
Returns an output parameter
All three actions take place in the same process space, which is a local function call.
Is there any way to call a cross-process function?
Typically, this process is deployed on another server.
The easiest thing to think of is that two processes agree on a protocol format that uses Socket communication to transmit:
Input parameter
Which function to call
Out of parameter
If it can be implemented, then this is a "remote" procedure call.
Socket communication can only transmit a continuous byte stream, how to put input parameters and functions into a continuous byte stream?
Suppose, design an 11-byte request message:
Fill in the first 3 bytes with the function name "add"
Fill in the first parameter "1" in the middle 4 bytes
Fill in the second parameter "2" in the last 4 bytes.
Similarly, you can design a 4-byte response message:
4 bytes fill in the processing result "3"
The caller's code may become:
Request = MakePacket ("add", 1,2); SendRequest_ToService_B (request); response = RecieveRespnse_FromService_B (); int result = unMakePacket (respnse)
The four steps are:
(1) change the incoming parameter to a byte stream
(2) send the byte stream to service B
(3) accept the returned byte stream from Service B
(4) change the returned byte stream into outgoing parameters
The code of the service party may become:
Request = RecieveRequest (); args/function = unMakePacket (request); result = Add (1,2); response = MakePacket (result); SendResponse (response)
These five steps are also easy to understand:
(1) the server receives the byte stream
(2) transfer the byte flow to function name and parameter
(3) call the function locally to get the result.
(4) convert the result to byte stream.
(5) send the byte stream to the caller
The process is described with a diagram as follows:
The processing steps of both the caller and the server are very clear.
What is the biggest problem with this process?
The caller is too troublesome to pay attention to a lot of low-level details each time:
Input parameters to the conversion of byte streams, that is, serializing application layer protocol details
Socket transmission, that is, network transport protocol details
Socket reception
Conversion from byte stream to output parameters, that is, deserialization of application layer protocol details
Can the calling layer not pay attention to this detail?
Yes, the RPC framework solves this problem by allowing callers to "call remote functions (services) like local functions".
At this point, do you feel a little bit about RPC, about serialization paradigm serialization? Looking down, there are more underlying details.
What is the responsibility of the RPC framework?
The RPC framework shields all kinds of complexity to the caller and to the service provider:
The service caller client feels like calling a local function to invoke the service
The service provider server feels like implementing a local function to implement the service
Therefore, the whole RPC framework is divided into client part and server part. It is the responsibility of the RPC framework to achieve the above goals and shield complexity.
As shown in the figure above, the responsibilities of the business side are:
Caller A, pass in parameters, execute the call, and get the result
Server B, receives the parameters, executes the logic, and returns the result
The responsibility of the RPC framework is, in the middle of the big blue box:
Client side: serialization, deserialization, connection pool management, load balancing, failover, queue management, timeout management, asynchronous management, etc.
Server side: server components, server transceiver queues, IO threads, worker threads, serialization and deserialization, etc.
We all know more about the technology on the server side. Next, we will focus on the technical details of the client side.
Let's take a look at the "serialization and deserialization" section of the RPC-client section.
Why serialize?
Engineers usually use "objects" to manipulate data:
Class User {std::String user_name; uint64_t user_id; uint32_t user_age;}; User u = new User ("shenjian"); u.setUid (123); u.setAge (35)
However, when data needs to be stored or transferred, "object" is not so easy to use, and it is often necessary to convert the data into a "binary byte stream" in continuous space. Some typical scenarios are:
Disk storage of database index: the index of the database is a b + tree in memory, but this format cannot be stored directly on disk, so it is necessary to convert the b + tree into a continuous binary byte stream before it can be stored on disk.
Cached KV storage: redis/memcache is a KV type cache. The value stored in the cache must be a binary byte stream of continuous space, not a User object.
Network transmission of data: the data sent by socket must be a binary byte stream in continuous space, and it cannot be an object.
The so-called serialization (Serialization) is the process of transforming the "object" morphological data into "continuous spatial binary byte stream" morphological data. The inverse of this process is called deserialization.
How to serialize?
This is a very detailed question. What would you do if you were asked to convert the "object" into a byte stream? One easy way to think of is a markup language with self-description features such as xml (or json):
When the transformation rules are defined, it is easy for the sender to serialize an object of the User class into xml, and after the server receives the xml binary stream, it is also easy to serialize it into a User object.
Voiceover: this is easy when language supports reflection
The second method is to implement the binary protocol for serialization, or take the above User object as an example, you can design a general protocol like this:
The first four bytes represent the serial number
The four bytes after the sequence number represent the length m of the key
The next m bytes represent the value of key
The next four bytes represent the length n of the value
The next n bytes represent the value of value
Recursively like xml until the entire object is described
The above User object, described in this protocol, might look like this:
The first line: serial number 4 bytes (set 0 for class name), class name length 4 bytes (length 4), the next 4 bytes is the class name ("User"), a total of 12 bytes
The second line: sequence number 4 bytes (1 represents the first attribute), attribute length 4 bytes (length 9), the next 9 bytes are attribute name ("user_name"), attribute value length 4 bytes (length 8), attribute value 8 bytes (value "shenjian"), a total of 29 bytes
The third line: serial number 4 bytes (2 represents the second attribute), attribute length 4 bytes (length 7), the next 7 bytes are attribute name ("user_id"), attribute value length 4 bytes (length 8), attribute value 8 bytes (value 123), total 27 bytes
The fourth line: the sequence number is 4 bytes (3 represents the third attribute), the attribute length is 4 bytes (length 8), the next 8 bytes is the attribute name ("user_name"), the attribute value is 4 bytes long (length 4), and the attribute value is 4 bytes (35) for a total of 24 bytes.
The entire binary byte stream has a total of 12 "29" 27 "24" 92 bytes.
The actual serialization protocol needs to consider much more details than this, for example: strongly typed languages should restore not only attribute names, attribute values, but also attribute types; complex objects should consider not only common types, but also object nesting types. In any case, the idea of serialization is similar.
What factors should be considered in the serialization protocol?
Whether you use the mature protocol xml/json or a custom binary protocol to serialize objects, you need to consider the following factors when serializing the protocol design.
Parsing efficiency: this should be the primary consideration for serialization protocols. For example, xml/json parsing is time-consuming, doom tree parsing is needed, and binary custom protocols are very efficient when parsing.
Compression ratio, transmission effectiveness: for the same object, xml/json has a large number of xml tags for transmission, so the validity of information is low, and the space occupied by binary custom protocols is relatively small.
Expansibility and compatibility: whether it is convenient to add fields and whether old clients need to be forced to upgrade after adding fields are all issues that need to be considered. Xml/json and the above binary protocol can be easily extended.
Readability and debuggability: this is easy to understand. Xml/json is much better readable than binary protocols.
Cross-language: the above two protocols are cross-language, and some serialization protocols are closely related to the development language. For example, the serialization protocol of dubbo can only support RPC calls of Java.
Versatility: xml/json is very universal, has a good third-party parsing library, and it is very convenient to parse each language. Although the above-defined binary protocols can cross languages, each language has to write a simple protocol client.
What are the common serialization methods?
Xml/json: poor parsing efficiency and compression ratio, good expansibility, readability and versatility
Thrift
Protobuf:Google product, must be a high-quality product, all aspects are good, highly recommended, belongs to the binary protocol, poor readability, but there are similar to-string protocols to help debug problems
Avro
CORBA
Mc_pack: those who understand, and those who don't understand don't understand. I used it in 2009. It is said that all aspects have surpassed protobuf. Students who know how to do it can talk about the current situation.
...
RPC-client except:
Serialize the deserialized part (1, 4 in the figure above)
It also includes:
The part that sends the byte stream and receives the byte stream (2, 3 in the figure above)
This part is divided into synchronous invocation and asynchronous invocation, which are introduced below.
Voiceover: it's not easy to get through RPC-client.
The code snippet for the synchronous call is:
Result = Add (Obj1, Obj2); / / blocked before getting Result
The code snippet for the asynchronous call is:
Add (Obj1, Obj2, callback); / / returns directly after calling, not waiting for the result
The processing result is called back as follows:
Callback (Result) {/ / will call this callback function after getting the processing result. }
These two types of calls are implemented in completely different ways in RPC-client.
What is the architecture of RPC-client synchronous invocation?
The so-called synchronous call is always in a blocking state until the result is obtained, and will occupy a worker thread all the time. The figure above simply illustrates the components, interactions, and process steps:
The large box on the left represents a worker thread of the caller
The pink middle box on the left represents the RPC-client component
The orange box on the right represents RPC-server
Two small blue boxes represent the two core components of synchronous RPC-client, the serialization component and the connection pool component
The small white process box and the arrow serial number 1-10 represent the serial execution steps of the entire worker thread:
1) the business code initiates the RPC call:
Result=Add (Obj1,Obj2)
2) Serialize the component, serializing the object calls into a binary byte stream, which can be understood as a packet packet1 to be sent
3) get an available connection connection through the connection pool component
4) send the packet packet1 to RPC-server by connecting to connection
5) the sending packet is transmitted over the network and sent to RPC-server
6) the response packet is transmitted over the network and sent back to RPC-client
7) receive the response package packet2 from RPC-server by connecting to connection
8) put the conneciont back into the connection pool through the connection pool component
9) serialize the component, serializing the packet2 norm into a Result object and returning it to the caller
10) the business code gets the Result result, and the worker thread continues to go down
Voiceover: please read against the 1-10 steps in the architecture diagram.
What is the purpose of connection pooling components?
The load balancing, failover, transmission timeout and other features supported by the RPC framework lock are all realized through the connection pool component.
The interfaces provided by a typical connection pool component are:
Int ConnectionPool::init (…) ; Connection ConnectionPool::getConnection (); int ConnectionPool::putConnection (Connection t)
What did init do?
And downstream RPC-server (usually a cluster) to establish N tcp persistent connections, the so-called connection "pool".
What did getConnection do?
Take a connection from the connection pool, lock it (set a flag bit), and return it to the caller.
What did putConnection do?
Put an allocated connection back into the connection "pool" and unlock it (also set a flag bit).
How to achieve load balancing?
A connection to a RPC-server cluster is established in the connection pool, and the connection pool needs to be random when returning the connection.
How to achieve failover?
A connection to a RPC-server cluster is established in the connection pool. When the connection pool finds that the connection of a certain machine is abnormal, the connection of the machine needs to be excluded and returned to the normal connection. After the machine is restored, the connection is added back.
How to achieve send timeout?
Because it is a synchronous blocking call, after getting a connection, you can use send/recv with timeout to send and receive with timeout.
Generally speaking, the implementation of synchronous RPC-client is relatively easy, and serialization components and connection pool components can be implemented with multiple worker threads.
For the remaining problems, what is the most appropriate setting for the number of worker threads?
This question is discussed in "what is the most appropriate number of worker threads?", and I won't go any further here.
What about the RPC-client asynchronous callback architecture?
The so-called asynchronous callback will not be blocked until the result is obtained. theoretically, no thread is blocked at any time, so the model of asynchronous callback, in theory, only a few worker threads and service connections are needed to achieve high throughput, as shown in the figure above:
The box on the left is for a small number of worker threads (a few will do) to make calls and callbacks.
The pink frame in the middle represents the RPC-client component
The orange box on the right represents RPC-server
Six small blue boxes represent the six core components of asynchronous RPC-client: context manager, timeout manager, serialization component, downstream transceiver queue, downstream transceiver thread, connection pool component
The small white process box and the arrow serial number 1-17 represent the serial execution steps of the entire worker thread:
1) the business code initiates an asynchronous RPC call
Add (Obj1,Obj2, callback)
2) context manager, which stores requests, callbacks, and contexts
3) serialize the component, serializing the object calls into a binary byte stream, which can be understood as a packet packet1 to be sent
4) downstream sending and receiving queue, put the message in the "waiting queue". Call return at this time, and the worker thread will not be blocked.
5) the downstream sending and receiving thread takes the message out of the "queue to be sent" and gets an available connection connection through the connection pool component
6) send the packet packet1 to RPC-server by connecting to connection
7) the sending packet is transmitted over the network and sent to RPC-server
8) the response packet is transmitted over the network and sent back to RPC-client
9) receive the response package packet2 from RPC-server by connecting to connection
10) downstream sending and receiving thread, put the message into the "accepted queue", and put the conneciont back into the connection pool through the connection pool component
11) in the downstream sending and receiving queue, the message is fetched, and the callback is about to begin and will not block the worker thread
12) serialize the component, serializing the packet2 norm into a Result object
13) context manager, take out the result, callback, context
14) call back the business code through callback, return the Result result, and the worker thread continues to go down.
If the request does not return for a long time, the processing flow is as follows:
15) context manager, the request has not been returned for a long time
16) the timeout manager gets the context of the timeout
17) call back the business code through timeout_cb, and the worker thread continues to go down
Voiceover: please take a closer look at this process in conjunction with the architecture diagram.
Serialization components and connection pooling components have been described above, and sending and receiving queues and threads are easier to understand. The following focuses on the two general components, the context manager and the timeout manager.
Why do I need a context manager?
Due to the sending of the request packet, the callback of the response packet is asynchronous and not even completed in the same worker thread, so a component is needed to record the context of a request and match some information such as request-response-callback.
How to match the request-response-callback information?
This is a very interesting question. We sent three request packets to the downstream service through a connection, and received three response packets asynchronously:
How do I know which request packet corresponds to which response packet?
How do I know which response packet corresponds to which callback function?
Request-response-callback concatenation can be achieved through the "request id".
The whole processing flow is as above, which corresponds to the mapping relationship between request-response-callback through request id and context manager:
1) generate request id
2) generate request context context, which contains information such as sending time time, callback function callback, etc.
3) the context manager records the mapping between req-id and context context
4) send req-id to RPC-server in the request package
5) RPC-server returns req-id in the response package
6) find the original context context through the context manager from the req-id in the response package
7) get the callback function callback from the context context
8) callback brings back Result to promote further execution of the business.
How to achieve load balancing and failover?
Similar to the idea of synchronous connection pooling, except that:
Synchronous connection pooling uses blocking to send and receive. Multiple connections need to be established with a single ip of a service.
Asynchronous transceiver, an ip of a service only needs to establish a small number of connections (for example, a tcp connection)
How to achieve overtime sending and receiving?
The implementation of timeout transceiver is different from the implementation of synchronous blocking transceiver:
Synchronous blocking timeout can be directly realized by using send/recv with timeout
Asynchronous non-blocking nio network message sending and receiving, because the connection will not always wait for the return packet, the timeout is implemented by the timeout manager
How does the timeout manager implement timeout management?
The timeout manager is used to implement the timeout callback processing of the request return packet.
When each request is sent to the downstream RPC-server, the req-id and context information are saved in the context manager, and a lot of information related to the request is saved in the context, such as req-id, callback, timeout callback, sending time, and so on.
The timeout manager starts timer to scan the context in the context manager to see if the sending time of the request in the context is too long. If it is too long, it will no longer wait for the return packet, but will directly call back the timeout to push the business process down and delete the context.
If the normal return packet arrives after the timeout callback is executed, and the context is not found in the context manager through req-id, the request is discarded directly.
Voiceover: the context cannot be restored because it has timed out.
In any case, compared with synchronous callback, in addition to serialization component and connection pool component, asynchronous callback has more components such as context manager, timeout manager, downstream transceiver queue, downstream transceiver thread and so on, and has an impact on the caller's calling habits.
Voiceover: programming habit, from synchronization to callback.
Asynchronous callback can improve the overall throughput of the system. Which method is used to implement RPC-client can be selected in combination with business scenarios.
At this point, I believe you have a deeper understanding of "what are the RPC details of the micro-service architecture?" you might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.