Pomelo research notes-RPC server, pomelo-rpc server

Source: Internet
Author: User
Tags emit

Pomelo research notes-RPC server, pomelo-rpc server

POMELO adopts a multi-process architecture to achieve the scalability of game servers (processes) and meet the requirements of supporting more online users and reducing server pressure. Inter-process communication is completed in the form of RPC, And the RPC implementation of pomelo is quite delicate. You can call the services provided by the remote server in the following way:

proxies.user.test.service.echo(routeParam, 'hello', function(err, resp) {    if(err) {        console.error(err.stack);        return;    }    console.log(resp);});

The preceding RPC call can be understood:
Call the echo interface of the service module whose namespace type is user and whose server type is test
Now, it's okay to listen to some tips, and let me analyze them slowly :)

Server Source Code Analysis

I have read and debug the source code of pomelo-rpc for no less than 30 times. Next I will introduce the source code architecture of the server and client in sequence based on the distribution and processing methods from the underlying data exchange module to the upper layer business logic.

1. Data Communication Module Based on socket. io Module
Generally, we have to solve several problems in writing the socket data communication module, for example:
  • Stick package problems
  • Packet Loss and disorder
  • IP address filtering
  • Implementation of Buffer Queue
  • Interaction Mode with upper-layer modules
Here we will talk about the implementation of pomelo-rpc. Nodejs has an embedded events module. This also leads to the fact that it is quite natural to encapsulate a module into an event Transceiver:
var Acceptor = function(opts, cb) {    EventEmitter.call(this);    this.bufferMsg = opts.bufferMsg;    this.interval = opts.interval || 300;    this.whitelist= opts.whitelist;    this._interval = null;    this.sockets = {};    this.msgQueues = {};    this.server = null;    this.notify = cb;};util.inherits(Acceptor, EventEmitter);

Using the inheritance function provided by util built in node, the Acceptor inherits the events. Open the node. js source code in two simple sentences.inheritsFunction implementation is also quite simple:

var inherits = function(sub, super) {    var tmp = function() {}    tmp.prototype = super.prototype;    sub.prototype = new tmp();}

Using this parasitic combined inheritance avoids calling the constructor of the parent class twice, so it will not be expanded here.

The Acceptor constructor receives some configuration information:

bufferMsg: Configure whether to enable the Buffer Queueinterval: Configure the interval of the timed data sending module. When the Acceptor enables the listener, determine whether to enable a timer based on the configuration information and regularly refresh the buffer:

    if(this.bufferMsg) {        this._interval = setInterval(function() {            flush(self);        }, this.interval);    }

The flush function is mainly used to write the buffered data through the socket. io Interface:

var flush = function(acceptor) {    var sockets = acceptor.sockets;    var queues = acceptor.msgQueues;    var queue, socket;    for(var socketId in queues) {        socket = sockets[socketId];        if(!socket) {            delete queues[socketId];            continue;        }        queue = queues[socketId];        if(!queue.length) {            continue;        }        socket.emit('message', queue);        queues[socketId] = [];    }};

Each client link corresponds to a Data Buffer Queue. Data is sent by sending a 'message' message.

IP address filtering
After the listener is enabled, if there is a client connection (on connection event), the first thing is IP address filtering. The IP address whitelist is also injected through the constructor:whitelist. If the IP address is invalid, close the link and output a warning.

Data Processing Module
The upper-layer module injects a notification configuration informationnotifyCallback function,acceptorAfter listening to the data, the data is first sent to the upper layer. After the upper-layer processing is complete, write the data to the queue if buffering is required. Otherwise, the data will be sent immediately:

 acceptor.notify.call(null, pkg.msg, function() {        var args = Array.prototype.slice.call(arguments);           for(var i = 0, l = args.length; i < l; i++) {            if(args[i] instanceof Error) {                args[i] = cloneError(args[i]);            }        }        var resp = {id: pkg.id, resp: Array.prototype.slice.call(args)};        if(acceptor.bufferMsg) {            enqueue(socket, acceptor, resp);        }        else {            socket.emit('message', resp);        }    });
2. Route request distribution module

What is mounted on the acceptor module isgatewayModule, which is mainly responsible for the creation and destruction of the acceptor module and status control. First, input a function when creating the acceptor module:

this.acceptor = this.acceptorFactory.create(opts, function(msg, cb) {        dispatcher.route(msg, cb);    });

Build an acctpor instance using the factory method, so that the underlying data processing module can easily change the communication protocol. Here, the callback function calls the distribution function to send requests to a specific service provider. Let's take a look at the implementation of dispatcher:

var Dispatcher = function(services) {    EventEmitter.call('this');    var self = this;    this.on('reload', function(services) {        self.services = services;    });    this.services = services;};util.inherits(Dispatcher, EventEmitter);

The Dispatcher module also becomes an event transceiver. At the same time, the constructor receivesservicesParameters. The request can be sent to a specific sub-module based on the parameters introduced when the modified parameters are used in combination with the route request. So,dispatcher.route(msg, cb);It is just matching the parameter to call the corresponding interface. The constructor also listens toreloadWhat is the role of an event? This is actually the RPC of pomelo.Hot plugging Module. Easy to implement:

var watchServices = function(gateway, dispatcher) {    var paths = gateway.opts.paths;    var app = gateway.opts.context;    for(var i = 0; i < paths.length; i++) {        (function(index) {            fs.watch(paths[index].path, function(event, name) {                if(event === 'change') {                    var res = {};                    var item = paths[index];                    var m = Loader.load(item.path, app);                    if(m) {                        res[namespace] = res[namespace] || {};                        for(var s in m) {                            res[item.namespace][s] = m[s];                        }                    }                    dispatcher.emit('reload', res);                }            });        })(i);    }};

gatewayThe module callswatchServicesListener module changes. Reload if the data file changes.servicesAnd notifies the routing distribution module.To ensure the normal communication between the server and the client, in addition to the consistency of the underlying data format, there is also a matching of route information.. If Gateway is called, the input configuration path is in the following format:

var paths = [    {namespace: 'user', path: __dirname + '/remote/test'}];

Assume that the current directory contains/remote/test/service.jsFile. The file contains two interfaces.test1/test2.loadThe format of the returned object is as follows:

{    service: {        test1: 'function xxx',        test2: 'function yyy'    }}

At the same time, you have a system RPC service and a custom RPC service in pomelo. The complete routing information is as follows:

services: {    sys: {        sys_module1: {            sys_module1_interface1: 'xxx'        }    },    user: {        user_module1: {            user_module1_interface1: 'yyy'        }    }}

Other things on the server are relatively simple. In order to clarify the context, the above Code has been deleted. If you are interested, you can retrieve it here.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.