Je viens d'entrer en contact avec RPC (appel à distance de procédure), qui est une méthode qui peut appeler des programmes sur des machines distantes localement. J'ai vu une simple implémentation NodeJS, qui est très bonne pour apprendre le principe de RPC: Nodejs Light_RPC
Exemple d'utilisation:
La copie de code est la suivante:
// côté serveur
var light_rpc = require ('./ index.js');
Var Port = 5556;
var rpc = new Light_rpc ({
combine: fonction (a, b, rappel) {
rappel (a + b);
},
multiplier: fonction (t, cb) {
CB (t * 2);
}
}). écouter (port);
Exemple de client:
La copie de code est la suivante:
//Client
RPC.Connect (5556, 'LocalHost', fonction (Remote, Conn) {
Remote.combine (1, 2, fonction (res) {
if (res! = 3) {
console.log ('error', res);
}
});
});
Parlons brièvement de l'ensemble du processus:
1. Le côté serveur démarre le programme, écoute le port, implémente les fonctions fournies au client pour appeler (comme combiner et se multiplier dans l'exemple ci-dessus), et les enregistre dans un objet.
2. Le côté client démarre le programme, se connecte au serveur et envoie une commande de décrire une fois la connexion terminée, obligeant le serveur à renvoyer le nom de la fonction qu'il peut fournir à l'appel.
La copie de code est la suivante:
connection.on ('connect', function () {
connection.write (commande (descrcmd));
});
3. Le côté serveur reçoit la commande décrite, enveloppe le nom de la fonction qu'il peut appeler et l'envoie ("combiner", "multiplier")
4. Le côté client reçoit le nom de fonction envoyé par le serveur, le registre dans son propre objet et enveloppe une méthode pour chaque nom de fonction, de sorte que lorsque ces fonctions sont appelées localement, une demande est réellement envoyée du côté serveur:
La copie de code est la suivante:
pour (var p dans cmd.data) {
RemoteObj [p] = getRemoteCallFunction (p, self.callbacks, connexion);
// La mise en œuvre de GetRemoteCallFunction est indiquée ci-dessous
}
5. Le côté client appelle les fonctions côté serveur:
1) Générez un ID unique pour la fonction de rappel passé, appelé callbackid, et enregistrez-le dans un objet du client.
2) Emballez les données suivantes et envoyez-la à côté du serveur: nom de la fonction d'appel, liste de paramètres sérialisée JSON, callbackid
La copie de code est la suivante:
fonction getRemoteCallFunction (cmdname, rappel, connexion) {
return function () {
var id = uuid.generate ();
if (typeof arguments [arguments.length-1] == 'fonction') {
rappels [id] = arguments [arguments.length-1];
}
var args = parseargumentStoArray.Call (this, arguments);
var newcmd = commande (cmdname, {id: id, args: args});
connection.write (newcmd);
}
}
6. Le côté serveur reçoit les informations ci-dessus, analyse les données, désérialise la liste des paramètres et appelle la fonction en fonction du nom et des paramètres de la fonction.
La copie de code est la suivante:
var args = cmd.data.args;
args.push (getSendCommandBackFunction (C, cmd.data.id));
self.wrapper [cmd.command] .Apply ({}, args);
7. Une fois la fonction terminée, sérialisez le résultat et renvoyez-le du côté client avec l'ID callbackID que j'ai reçu auparavant.
La copie de code est la suivante:
fonction getSendCommandBackFunction (connexion, cmDid) {
return function () {
var innerargs = parseargumentStoArray.Call ({}, arguments);
var resultCommand = Command (resultCmd, {id: cmDid, args: innerargs});
Connection.Write (ResultCommand);
};
}
8. Le côté client reçoit la fonction en cours d'exécution et callbackID, élimine la fonction de rappel basée sur CallbackID et transmet le résultat d'exécution dans la fonction de rappel pour l'exécution.
9. L'ensemble du processus est terminé, veuillez vous référer au code source: https://github.com/romulka/nodejs-light_rpc
Quelques notes:
1. Le client et le serveur sont toujours connectés tout au long du processus, contrairement au protocole HTTP qui déconnecte le lien après l'envoi et la réception, de sorte que la déconnexion ne peut pas être utilisée pour déterminer si la transmission des données est terminée en la déconnectant. Afin de déterminer que la réception des données est terminée, les données envoyées par le client et le serveur suivent un protocole simple: ajoutez la longueur du paquet de données et du séparateur avant les données, telles que le délimiteur IS / N: [Données de longueur de paquet / n]. De cette façon, après avoir reçu les données, la longueur du paquet de données est d'abord récupérée, puis détermine en continu si les paquets de données reçus accumulés sont égaux ou dépassent cette longueur. Si c'est le cas, la transmission des données est terminée et les données peuvent être analysées et extraites.
2. Le RPC le plus simple est qu'il ne considère pas le type de fonction dans le paramètre. Par exemple, si un paramètre est un objet, il y a des membres de la fonction sous cet objet. Lorsque JSON sérialise, la fonction sera ignorée et cette fonction ne peut pas être exécutée du côté serveur.
Pour résoudre ce problème, un traitement complexe est requis:
1. Traversé profondément chaque paramètre à envoyer à l'extrémité distante, extraire le membre de la fonction, générer un ID unique pour cette fonction, le mettre dans un objet local, remplacer le membre de la fonction par cette chaîne d'ID et identifier que ce membre est en fait une fonction. De cette façon, l'objet peut être sérialisé et envoyé.
2. Lorsque le serveur reçoit un appel, lorsqu'il souhaite utiliser la fonction dans l'objet de paramètre, il détermine qu'il s'agit d'une fonction traitée par le client, avec un ID, renvoyez cet ID au client et transmettez l'ID de fonction de rappel au client de la même manière, en attendant le rappel du côté client.
3. Le côté client reçoit cet ID de fonction, trouve cette entité de fonction, l'appelle et la renvoie du côté serveur en fonction de l'ID de rappel donné par le côté serveur.
4. Le côté serveur reçoit le résultat, trouve la fonction de rappel, continue d'exécuter et se termine.
La méthode d'enregistrement d'une fonction peut être terminée d'une autre manière. L'idée générale est de remplacer la fonction par quelque chose de sérialisable, afin que la fonction puisse être trouvée localement lorsqu'elle est appelée du côté distant. Vous pouvez vous référer à l'implémentation de DNODE.