Aller au contenu

Client-serveur

La rupture avec le mainframe — quand la puissance de calcul a migré vers le bureau.


Pourquoi le client-serveur

Le mainframe fonctionnait, mais il avait deux limites que le marche ne tolerait plus à la fin des années 1980. D'abord, l'interface : les terminaux 3270 affichaient du texte en mode caractère, alors que le Macintosh et Windows montraient ce que les utilisateurs pouvaient attendre — des fenêtres, des menus, de la souris. Ensuite, le coût d'entree : un mainframe coûtait des millions de dollars, alors qu'un PC coûtait quelques milliers.

Le modèle client-serveur repose sur une idée simple : séparer le programme en deux parties qui communiquent via un réseau. Le client — un PC sur le bureau — géré l'interface utilisateur. Le serveur — une machine partagee — géré les données et une partie du traitement. Orfali et Harkey, dans Client/Server Survival Guide, ont documente cette revolution en temps réel, avec un pragmatisme rare.

Gardarin (Le Client-serveur) complète cette vision en detaillant les mécanismes côté données : protocoles d'accès, optimisation des requêtes, répartition de charge entre client et serveur.


Le modèle 2-tiers

Le modèle 2-tiers (deux niveaux) est la forme la plus directe du client-serveur. Le client contient à la fois la logique de présentation et la logique métier. Le serveur est une base de données relationnelle — Oracle, Sybase, SQL Server.

graph TB
    subgraph "Tier 1 : Client lourd"
        UI["Interface graphique<br/>Windows / VB / Delphi"]
        BL["Logique metier"]
        UI --- BL
    end

    subgraph "Tier 2 : Serveur BDD"
        DB["Base de donnees<br/>Oracle / Sybase / SQL Server"]
        SP["Procedures stockees"]
        DB --- SP
    end

    BL --SQL via ODBC/JDBC--> DB

Le client lourd (fat client) fait tout sauf stocker les données. Il valide les saisies, applique les règles métier, généré les rapports, et envoie des requêtes SQL directement au serveur de base de données. Le protocole de communication est souvent propriétaire — chaque éditeur de base de données avait son propre driver.

Avantages du 2-tiers

Le 2-tiers avait des qualites réelles :

  • Rapidité de développement : des outils comme Visual Basic, Delphi ou PowerBuilder permettaient de créer des interfaces riches en glissant-deposant des composants
  • Reactivite : la logique s'executait localement, sans aller-retour réseau pour chaque validation
  • Richesse d'interface : fenêtres, graphiques, drag-and-drop — tout ce que le terminal 3270 ne pouvait pas faire

Limites du 2-tiers

Mais les problèmes sont apparus rapidement à l'échelle :

  • Déploiement : chaque modification de la logique métier imposait de reinstaller le client sur tous les postes. Avec 500 utilisateurs, c'est un cauchemar logistique
  • Couplage au schéma : le client envoyait du SQL brut au serveur. Changer une table imposait de modifier et redeployer tous les clients
  • Sécurité : le client avait un accès direct à la base. Si on pouvait decompiler le client, on avait les identifiants de la base
  • Scalabilité : chaque client maintenait une connexion persistante à la base. A 500 connexions simultanées, la base saturait

Warning

Le syndrome du "DLL hell" a marque toute une génération de développeurs Windows. Deux applications installees sur le même PC pouvaient necessiter des versions différentes de la même DLL, provoquant des crashes impossibles a diagnostiquer.


Le modèle 3-tiers

Le modèle 3-tiers ajoute un niveau intermédiaire entre le client et la base de données. Ce niveau — le serveur d'application — contient la logique métier. Le client ne fait plus que de la présentation. La base ne fait que du stockage.

graph TB
    subgraph "Tier 1 : Client leger"
        UI2["Interface graphique<br/>Navigateur ou client leger"]
    end

    subgraph "Tier 2 : Serveur d'application"
        BL2["Logique metier"]
        TX["Gestion des transactions"]
        SEC["Securite"]
        BL2 --- TX
        BL2 --- SEC
    end

    subgraph "Tier 3 : Serveur de donnees"
        DB2["Base de donnees"]
    end

    UI2 --HTTP / RPC--> BL2
    BL2 --SQL--> DB2

La séparation en trois niveaux résout les problèmes majeurs du 2-tiers :

Problème 2-tiers Solution 3-tiers
Déploiement client coûteux La logique est sur le serveur — on deploie une seule fois
Couplage client/schéma BDD Le client ne parle qu'au serveur d'application
Accès direct à la base Seul le serveur d'application à les credentials BDD
Connexions BDD limitees Le serveur d'application géré un pool de connexions

Le serveur d'application est devenu une catégorie de produit a part entière : BEA WebLogic, IBM WebSphere, Microsoft MTS/COM+ — des plateformes qui gereront les transactions, la sécurité, le pooling de connexions et le threading pour le compte des développeurs.


Fat client vs thin client

Le debat entre client lourd et client léger a traverse toute l'ere client-serveur. Orfali et Gardarin documentent les arguments des deux camps avec la rigueur que ce debat meritait.

Critère Client lourd (fat) Client léger (thin)
Logique métier Sur le client Sur le serveur
Interface Riche (native OS) Simple (HTML ou terminal)
Déploiement Installation sur chaque poste Rien a installer (navigateur)
Performance réseau Moins d'appels serveur Plus d'appels mais plus légers
Maintenance Coûteuse — N postes a mettre à jour Simple — un seul serveur
Mode déconnecté Possible Impossible
Coût par poste Élevé (PC puissant requis) Faible (machine simple suffisante)

Le client léger a gagne pour une raison simple : le coût de déploiement. Quand on a 10 000 postes, installer une mise à jour sur chacun est un projet en soi. Avec un client léger (navigateur web), il suffit de déployer la nouvelle version sur le serveur et tout le monde en beneficie au prochain chargement de page.

Note

Ce debat n'est pas tranche definitivement. Les applications Electron (VS Code, Slack, Teams) sont des clients lourds deguises en clients légers. Les PWA et les SPA modernes sont des clients de plus en plus "gras" qui tournent dans un navigateur. L'histoire est cyclique.


Middleware : la colle entre les tiers

Le middleware est le logiciel qui permet aux tiers de communiquer entre eux. Sans middleware, chaque développeur devait programmer les sockets, gérer les erreurs réseau, serialiser les données — un travail repetitif et source de bugs.

RPC (Remote Procédure Call)

Le RPC permet d'appeler une procédure sur une machine distante comme si elle etait locale. Le programmeur écrit un appel de fonction normal ; le middleware se charge de serialiser les parametres, de les envoyer via le réseau, d'attendre la réponse et de deserialiser le résultat.

sequenceDiagram
    participant C as Client
    participant CS as Client Stub
    participant R as Runtime RPC
    participant SS as Server Stub
    participant S as Serveur

    C->>CS: appel local calculerPaie(id)
    CS->>R: serialisation + envoi
    R->>SS: transport reseau
    SS->>S: appel reel calculerPaie(id)
    S-->>SS: resultat
    SS-->>R: serialisation reponse
    R-->>CS: reception
    CS-->>C: retour comme si local

Le problème du RPC est qu'il masque la réalité du réseau. Un appel local prend quelques nanosecondes ; un appel RPC prend des millisecondes — trois ordres de grandeur de différence. Et il peut échouer pour des raisons qu'un appel local ne connait pas : timeout, partition réseau, serveur en panne. Peter Deutsch a formalisé ces problèmes dans ses celebres "8 fallacies of distributed computing".

Message queues

Les files de messages offrent une alternative au RPC en decouplant temporellement l'émetteur et le récepteur. L'émetteur place un message dans une file et continue son exécution. Le récepteur consomme le message quand il est pret. Si le récepteur est temporairement indisponible, les messages s'accumulent dans la file et seront traites au redémarrage.

IBM MQSeries (lance en 1993 sur mainframe, puis porte sur d'autres plateformes) et Microsoft MSMQ sont les produits fondateurs de cette catégorie. Le concept de file de messages est ne sur mainframe et a survecu a toutes les revolutions architecturales — on le retrouve aujourd'hui dans Kafka, RabbitMQ, SQS et NATS.


Les leçons du client-serveur

Le client-serveur a apporte la decentralisation et l'interface graphique riche, mais il a aussi révélé des problèmes fondamentaux que chaque génération suivante devra résoudre :

Le déploiement distribué est un problème d'ingénierie a part entière. Installer et mettre à jour du logiciel sur des milliers de postes est un cauchemar que le mainframe n'avait jamais connu. C'est ce problème qui a pousse vers le web et les clients légers.

Le couplage entre les tiers crée de la fragilite. Quand le client connait le schéma de la base, toute évolution du schéma impose une mise à jour coordonnée. La solution — des interfaces stables entre les tiers — est le germe de la notion de contrat de service.

La transparence de la distribution est un mensonge dangereux. Le RPC prétend que l'appel distant est identique à l'appel local, mais le réseau introduit de la latence, des pannes partielles et de la perte d'ordre. Ignorer ces différences conduit à des systèmes fragiles.

La séparation des responsabilités (présentation, logique, données) est un principe durable. Même si les technologies changent, cette séparation en couches reste le fondement de toute architecture bien structurée.

Le modèle 3-tiers a résolu les problèmes les plus aigus du 2-tiers, mais il restait fondamentalement limite a un seul langage, une seule plateforme, un seul réseau d'entreprise. Pour connecter des systèmes hétérogènes — des applications Java avec des applications C++, des serveurs Unix avec des serveurs Windows — il fallait un mécanisme plus ambitieux. C'est ce qu'ont tente les objets distribués.


Chapitre suivant : Objets distribues et middleware — quand on a voulu faire communiquer des objets à travers le réseau.