juillet
2009
Dans le dernier ticket, j’ai parlé du modèle client-serveur avec passage de message pour la programmation parallèle en dodo.
Cela semble une bonne idée en théorie, mais comment cela marche en pratique?
À mon idée, les services peuvent tourner dans la même tâche, une autre tâche (le plus courant), un autre processus ou même sur une autre machine. La communication par message entre un client et un serveur doit être efficace, contrôlée et sure.
Il faut donc s’attendre à un système de messages assez complexe.
Je vais essayer de décrire ce qui peut se passer quand un client envoie une requête au serveur.
D’abord, le client doit disposer d’une capability qui l’autorise à faire la requête. Le code situé dans un constructeur ou une méthode, qui peut donc avoir des effets de bord, peut généralement obtenir une capability sans ambages. Le reste du code peut la recevoir en paramètre.
La capability contient un code (comme un mot de passe) plus ou moins sophistiqué reconnu par le serveur. Celui-ci est utilisé pour encoder la requête.
Pour savoir où envoyer la requête, il faut que le serveur enregistre les services qu’il offre auprès des clients. Alors le client peut choisir un serveur à qui envoyer la requête (ou peut-être plusieurs serveurs).
Puis le message qui forme la requête entre dans une file d’attente associée avec le serveur. Les messages sortent de la file d’attente suivant le principe du premier venu, premier servi (par défaut) ou suivant un système de priorité mis en place par le programmeur.
Je pense que chaque tâche maintient sa liste de files d’attente pour les services qu’elle utilise, et le serveur fait le tour des tâches pour trouver les files d’attentes qui lui sont destinées. Si le serveur se trouve dans un autre processus ou sur une autre machine, il y a un serveur spécial auprès du client qui s’occupe de récupérer les messages destinés au serveur pour les lui envoyer.
Une fois le message récupéré, le serveur le décode à l’aide du code correspondant à la capability utilisée. Alors la méthode désignée est exécutée avec les paramètres reçus dans le message. Les paramètres doivent être validés avant d’être passés à la méthode.
Si la méthode doit reporter une exception ou un résultat au client, il faut mettre à jour l’objet retourné par la requête (message) et en informer le client. Le message retourné a plusieurs états possibles, comme « en attente », « résultat partiel », « résultat final », « exception »…
Le client, de son côté, peut faire ce qu’il veut en attendant le résultat. Jusqu’à ce qu’il demande à utiliser ce résultat, auquel cas il s’arrête jusqu’à obtenir le résultat final ou une exception. Il devrait être possible d’accéder au résultat partiel à l’aide d’un handler muni d’un timeout.
Le compilateur peut se charger d’optimiser tout cela, peut-être en se passant d’encodage et décodage dans certains cas, ou même de passage de message. La file d’attente de messages doit être efficace et éviter de bloquer les tâches. Si le handler l’autorise, on pourrait renoncer à envoyer un message plutôt que de bloquer quand la file d’attente est trop pleine.
Y a-t-il des questions qui n’ont pas été encore abordées? Des point qui ne sont pas clairs ou erronés? J’aimerais avoir votre opinion.