Introduction to the implementation principle of RPC of remote procedure call in node.js

  • 2020-03-30 04:29:23
  • OfStack

Just come into contact with the RPC (remote procedure call (RPC), is that you can in the local call remote computer program, the method of see a simple nodejs, used to study the principle of RPC is very good: (link: https://github.com/romulka/nodejs-light_rpc)

Examples of use:


//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);

The Sample client:


//Client
rpc.connect(5556, 'localhost', function(remote, conn){
    remote.combine(1, 2, function(res){
        if(res != 3){
            console.log('ERROR', res);
        }
    });
});

A brief account of the process:

1. The server side starts the program, listens for the port, implements the functions provided to the client (such as combine and multiply in the above example), and stores them in an object.
2. The client side starts the program, connects to the server side, and after the connection is completed, sends a describe command, asking the server to return the name of the function it can call.


connection.on('connect', function(){
  connection.write(command(descrCmd));
});

3. The server side receives the describe command, wraps the name of the function it can call and sends it (" combine ", "multiply")
4. The client side receives the function name sent by the server, registers it in its own object, and wraps a method for each function name, so that when calling these functions locally, it actually sends a request to the server side:


for(var p in cmd.data){
  remoteObj[p] = getRemoteCallFunction(p, self.callbacks, connection);
  //The implementation of getRemoteCallFunction is shown below
}

5. The client side calls the function of the server side:

1) generate a unique ID for the passed callback function, called callbackId, to be recorded in an object of the client.
2) wrap the following data and send it to the server side: the name of the calling function, the list of parameters after JSON serialization, and the callbackId


function getRemoteCallFunction(cmdName, 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(cmdName, {id: id, args: args});
    connection.write(newCmd);
  }
}

6. The server side receives the above information, parses the data, deserializes the parameter list, and calls the function according to the function name and parameter.


var args = cmd.data.args;
args.push(getSendCommandBackFunction(c, cmd.data.id));
self.wrapper[cmd.command].apply({}, args);

7. When the function is completed, serialize the result and send it back to the client with the previously received callbackId


function getSendCommandBackFunction(connection, cmdId){
  return function(){
    var innerArgs = parseArgumentsToArray.call({}, arguments);
    var resultCommand = command(resultCmd, {id: cmdId, args: innerArgs});
    connection.write(resultCommand);
  };
}

8. The client side receives the result of the function operation and the callbackId, takes out the callback function according to the callbackId, and passes the result into the callback function for execution.

9. The entire process is complete, see the source code: (link: https://github.com/romulka/nodejs-light_rpc)

A few points to note:

1. The connection between client and server is maintained throughout the whole process. Unlike the HTTP protocol, the connection is broken after sending and receiving. In order to judge the data reception is complete, the client and the server sends the data to follow a simple protocol: the data with the length of the packet and separator, such as pronounce separator \ n: [\ n data packet length], so after receiving the data first take out the length of the packet, and continuously accumulated the received data packets in judging whether equals or exceeds the length, if it is a data transfer is completed, you can start to extract data.

2. The simplicity of RPC is that it does not consider the case of function type in the parameter. For example, the parameter is an object, which has function members.

To solve this problem, a complex process is required:

1. Depth traverses each parameter to be sent to the far end, pulls out the function member, generates a unique id for the function, places it in a local object, replaces the function member with the id string, and identifies the member as actually a function. This object can then be serialized and sent out.
2. The server receives the call. When the function in parameter object is to be used, it is judged that this is a function processed by the client.
3. The client side receives the function id, finds the function entity, calls it, and sends it back to the server side according to the callback id given by the server side
4. Server side received the result, found the callback function, continue to execute, complete.

The method of recording a function can be done in other ways. The general idea is to replace the function with something serializable, recording the function so that the function can be found locally when the remote is called. You can refer to the implementation of dnode.


Related articles: