Introduction to the implementation principle of RPC (Remote process call) in node. js, node. jsrpc
When I first came into contact with RPC (Remote process call), I was able to locally call the method of the Program on the remote host. I saw a simple nodejs implementation to learn the RPC principle: nodejs light_rpc
Example:
Copy codeThe Code is as follows:
// Server
Var light_rpc = require ('./index. js ');
Var port = 5556;
Var rpc = new light_rpc ({
Combine: function (a, B, callback ){
Callback (a + B );
},
Multiply: function (t, cb ){
Cb (t * 2 );
}
}). Listen (port );
Sample client:
Copy codeThe Code is as follows:
// Client
Rpc. connect (5556, 'localhost', function (remote, conn ){
Remote. combine (1, 2, function (res ){
If (res! = 3 ){
Console. log ('error', res );
}
});
});
Let's briefly talk about the entire process:
1. Start the program on the server and listen on the port to implement the functions provided to the client (such as combine and multiply in the preceding example) and save them in an object.
2. The client starts the program and connects to the server. After the connection is complete, the describe command is sent, requiring the server to return the function name that can be called.
Copy codeThe Code is as follows:
Connection. on ('connect ', function (){
Connection. write (command (descrCmd ));
});
3. The server receives the describe command and packs the function names that can be called and sends them out ("combine", "multiply ")
4. the client receives the function name sent by the server, registers it to its own object, and packs a method for each function name so that the local call to these functions actually sends a request to the server:
Copy codeThe Code is as follows:
For (var p in cmd. data ){
RemoteObj [p] = getRemoteCallFunction (p, self. callbacks, connection );
// The implementation of getRemoteCallFunction can be found below
}
5. The client calls the server functions:
1) generate a unique ID for the incoming callback function, called callbackId, and record it to an object of the client.
2) wrap the following data and send it to the server: Call function name, JSON serialized parameter list, callbackId
Copy codeThe Code is as follows:
Function getRemoteCallFunction (callback name, callbacks, connection ){
Return function (){
Var id = uuid. generate ();
If (typeof arguments [arguments. length-1] = 'function '){
Callbacks [id] = arguments [arguments. length-1];
}
Var args = parseArgumentsToArray. call (this, arguments );
Var newCmd = command (partition name, {id: id, args: args });
Connection. write (newCmd );
}
}
6. The server receives the above information, parses the data, deserializes the parameter list, and calls the function based on the function name and parameter.
Copy codeThe Code is as follows:
Var args = cmd. data. args;
Args. push (getSendCommandBackFunction (c, cmd. data. id ));
Self. wrapper [cmd. command]. apply ({}, args );
7. After the function is run, serialize the result together with the previously received callbackId and send it back to the client.
Copy codeThe Code is as follows:
Function getSendCommandBackFunction (connection, consumer ID ){
Return function (){
Var innerArgs = parseArgumentsToArray. call ({}, arguments );
Var resultCommand = command (resultCmd, {id: Region id, args: innerArgs });
Connection. write (resultCommand );
};
}
8. The client receives the function running result and callbackId, extracts the callback function based on the callbackId, and transfers the running result to the callback function for execution.
9. The whole process is completed, see the source code: https://github.com/romulka/nodejs-light_rpc
Notes:
1. The client and server keep connected throughout the process. Unlike sending and receiving the http protocol, the connection is disconnected. Therefore, you cannot determine whether a data transfer is completed by disconnecting the connection. To determine whether the data is received, the client and server follow a simple protocol: add the length and separator of the data packet before the data. For example, the separator is \ n: [packet length \ n data], so that after receiving the data, the length of the data packet is first taken out, and then the accumulated received data packets are constantly judged to be equal to or greater than this length, if the data transfer is complete, parse and extract the data.
2. this RPC is simple because there is no function type in the parameter. For example, if a parameter is an object with a function member, the function is ignored during JSON serialization, this function cannot be executed on the server.
To solve this problem, complicated processing is required:
1. traverse each parameter to be sent to the remote end in depth, extract the function Member, generate a unique id for this function, put it in a local object, and replace this function member with this id string, and identifies the member as a function. In this way, the object can be serialized and sent out.
2. the server receives the call. When the function in the parameter object is used, it is determined that this is a function processed by the client. There is an id that is sent back to the client, send the callback function id to the client in the same way, and wait for the client to callback.
3. The client receives the function id, finds the function entity, calls it, and sends it back to the server based on the callback id sent by the server.
4. The server receives the result, finds the callback function, continues the execution, and completes.
The function recording method can be completed in other ways. The general idea is to replace the function with something that can be serialized, and record the function so that this function can be found locally when called by the remote end. See dnode implementation.