Modern Backend Engineering
Chapitre 1
Chapitre 01 — Fondamentaux Backend
Chapitre 01 — Fondamentaux Backend
Cours complet — Fondamentaux Backend
1. HTTP/1.1, HTTP/2, HTTP/3
HTTP/1.1 (RFC 2616, 1999)
- Connexion : TCP par requête ou keep-alive (Connection: keep-alive)
- Pipeline : théoriquement possible, rarement implémenté (HOL blocking)
- Head-of-Line blocking : une requête lente bloque les suivantes sur la même connexion
- Compression : gzip, deflate sur le body (pas les headers)
- Methodes : GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE, CONNECT
- Status codes : 1xx (info), 2xx (success), 3xx (redirect), 4xx (client error), 5xx (server error)
Problèmes HTTP/1.1 :
- Multiplexage impossible (1 requête = 1 connexion TCP)
- Headers non compressés (répétés, verbeux)
- Priority non gérée
- Server push inexistant
HTTP/2 (RFC 7540, 2015)
- Binaire (pas textuel) : frames + streams
- Multiplexage : plusieurs requêtes simultanées sur une connexion TCP
- HPACK : compression des headers (Huffman + table)
- Server push : serveur envoie des ressources avant demande
- Stream priority : arbre de priorisation
- Flow control : contrôle de flux par stream
┌─────────────────────────────────────────┐
│ Connexion TCP unique │
├────────┬────────┬────────┬──────────────┤
│Stream1 │Stream2 │Stream3 │ ... │
│Req 1 │Req 2 │Req 3 │ │
│Frame 1 │Frame 1 │Frame 1 │ │
│Frame 2 │Frame 2 │... │ │
│... │... │ │ │
└────────┴────────┴────────┴──────────────┘
HOL blocking persiste : un paquet TCP perdu bloque tous les streams (car un seul TCP).
HTTP/3 (RFC 9114, 2022)
- QUIC (transport UDP) au lieu de TCP
- 0-RTT : reprise de session sans handshake
- Connection migration : changement d'IP sans reconnexion (mobile)
- No HOL blocking : perte d'un paquet n'affecte que le stream concerné
- QPACK : compression headers adaptée à QUIC
- Built-in TLS 1.3 : chiffrement natif
QUIC vs TCP :
| Feature | TCP | QUIC |
|---|---|---|
| Transport | TCP (kernel) | UDP (userspace) |
| Handshake | 3-way + TLS = 2-3 RTT | 0-1 RTT |
| HOL blocking | Oui (TCP) | Non (par stream) |
| Migration | Non | Oui |
| User-space | Non | Oui (implémentations ex: quinn, s2n-quic) |
HTTP Methods (idempotence)
| Method | Idempotent | Safe | Payload |
|---|---|---|---|
| GET | Oui | Oui | Non |
| HEAD | Oui | Oui | Non |
| POST | Non | Non | Oui |
| PUT | Oui | Non | Oui |
| DELETE | Oui | Non | Non* |
| PATCH | Non* | Non | Oui |
| OPTIONS | Oui | Oui | Non |
2. TCP/IP, UDP, Sockets
TCP (Transmission Control Protocol)
- Orienté connexion : 3-way handshake (SYN, SYN-ACK, ACK)
- Fiable : accusés de réception, retransmission
- Ordonné : séquencement des paquets
- Flow control : sliding window (récepteur contrôle débit)
- Congestion control : slow start, congestion avoidance, fast retransmit (algo: Reno, Cubic, BBR)
- Headers : 20-60 bytes (source/dest port, seq/ack num, flags, window size, checksum)
3-way handshake :
Client → SYN (seq=x) → Serveur
Client ← SYN-ACK (seq=y, ack=x+1) ← Serveur
Client → ACK (seq=x+1, ack=y+1) → Serveur
TCP Flags : SYN, ACK, FIN, RST, PSH, URG
UDP (User Datagram Protocol)
- Sans connexion : pas de handshake
- Non fiable : pas de garantie de livraison
- Non ordonné : pas de séquencement
- Léger : 8 bytes de header
- Usages : DNS, DHCP, streaming, VoIP, QUIC, gaming
TCP vs UDP
| Critère | TCP | UDP |
|---|---|---|
| Fiabilité | Garantie | Aucune |
| Ordre | Garanti | Non |
| Vitesse | Plus lent | Plus rapide |
| Overhead | Élevé (20-60 bytes) | Faible (8 bytes) |
| Cas d'usage | HTTP, SMTP, SSH, FTP | DNS, VoIP, gaming, QUIC |
Sockets
- socket() : création (domain, type, protocol)
- bind() : attache à une adresse/port
- listen() : écoute (TCP serveur)
- connect() : connexion (TCP client)
- accept() : accepte connexion (TCP serveur)
- send()/recv() : envoi/réception
- close() : fermeture
Socket types :
SOCK_STREAM: TCP (fiable, ordonné)SOCK_DGRAM: UDP (non fiable)SOCK_RAW: accès direct IP
Non-blocking I/O :
// set non-blocking
fcntl(sock, F_SETFL, O_NONBLOCK);
// ou utiliserr select()/poll()/epoll()/kqueue
epoll (Linux) / kqueue (BSD/macOS) / IOCP (Windows)
- select() : O(n), limite FD_SETSIZE (1024)
- poll() : O(n), pas de limite fixe
- epoll() : O(1), edge/level-triggered, scalable (millions de sockets)
- kqueue : O(1), BSD/macOS, filtres variés
- IOCP : Windows, completion ports
3. Processus, Threads, Async
Processus
- Espace mémoire isolé : heap, stack, code, data segments
- Communication : pipes, sockets, shared memory, message queues
- Création : fork() (Unix), CreateProcess() (Windows)
- État : running, ready, blocked, terminated
- Context switch : lourd (changement d'espace d'adressage)
Threads
- Même espace mémoire (heap partagé, stack privé)
- Création : pthread_create(), std::thread
- Context switch : léger (même espace d'adressage)
- Problèmes : race conditions, deadlocks, data races
- Synchronisation : mutex, semaphore, condition variable, barrier
Race condition :
// Thread 1 et 2 incrémentent x en même temps
// Problème : read-modify-write non atomique
// Solution : mutex ou std::atomic<int>
Deadlock :
Thread A: lock(m1) → lock(m2)
Thread B: lock(m2) → lock(m1)
// Chacun attend l'autre → deadlock
// Solution : lock ordering, std::lock()
Async (I/O non bloquant)
- Event loop : boucle qui attend des événements et exécute des callbacks
- Approches :
- Callbacks : Node.js (callback hell)
- Promises : chaînage .then()
- Async/await : syntaxe synchrone pour code async
- Coroutines : asyncio (Python), tokio (Rust)
- Fibers : green threads (Ruby, Java Loom)
I/O Models (Unix) :
- Blocking I/O : read() bloque jusqu'à données
- Non-blocking I/O : read() retourne EAGAIN
- I/O Multiplexing : select/poll/epoll
- Signal-driven I/O : SIGIO
- Asynchronous I/O : aio_read(), io_uring
io_uring (Linux 5.1+) :
- File d'attente de soumission (SQ) et de complétion (CQ)
- Zero-copy, system call avoidance
- Bests performances pour I/O-bound workloads
4. Concurrence et Parallélisme
Concurrence vs Parallélisme
- Concurrence : gérer plusieurs tâches (logique) — 1 cœur
- Parallélisme : exécuter plusieurs tâches simultanément (physique) — N cœurs
Modèles de concurrence
- Preemptive (threads OS) : scheduler noyau décide des switchs
- Cooperative (goroutines, coroutines) : tâches yield explicitement
- Actor model (Erlang, Akka) : acteurs communiquent par messages
- CSP (Go, Clojure core.async) : channels + goroutines
- STM (Haskell, Clojure) : Software Transactional Memory
Amdahl's Law
Speedup = 1 / ((1 - P) + P/N)
Où P = proportion parallélisable, N = nombre de cœurs
Limite : même avec N→∞, speedup ≤ 1/(1-P)
Gustafson's Law
Scaled Speedup = (1 - P) + N*P
Plus optimiste : les problèmes grandissent avec les ressources.
5. Gestion Mémoire
Hiérarchie mémoire
Registers ~1ns ~1KB
L1 Cache ~2ns ~32KB
L2 Cache ~7ns ~256KB
L3 Cache ~15ns ~8MB
RAM ~80ns ~32GB
SSD ~100μs ~1TB
HDD ~10ms ~10TB
Network ~50ms ∞
Virtual Memory
- Page table : mapping mémoire virtuelle → physique
- TLB : Translation Lookaside Buffer (cache de page table)
- Page fault : accès à une page non mappée (lente !)
- MMU : Memory Management Unit (hardware)
Memory allocators
- malloc/free : glibc (ptmalloc), jemalloc, tcmalloc, mimalloc
- Arena : zones mémoire pour réduire contention multi-thread
- Slab allocator : pour objets de taille fixe (noyau Linux)
Garbage Collection
- Reference counting : Python, Swift (mais cycles !)
- Mark & Sweep : marque objets vivants, sweep morts (Go, Java CMS)
- Copying : copie objets vivants dans semi-espace (Java parallel)
- Generational : young/old generation (Java G1, ZGC)
- Concurrent : pas de stop-the-world (Go, Java ZGC/Shenandoah)
Memory Leaks in GC languages
- Références circulaires non détectées (ref counting)
- Caches sans éviction
- Closures capturant trop de scope
- Event listeners non déréférencés
6. DNS
Hiérarchie DNS
Root (.) → 13 serveurs racines (a.root-servers.net → m.root-servers.net)
├── TLD (.com, .org, .fr, .io, ...)
│ ├── Authoritative (example.com)
│ │ ├── A/AAAA records (IPv4/IPv6)
│ │ ├── CNAME (alias)
│ │ ├── MX (mail exchange)
│ │ ├── TXT (SPF, DKIM, DMARC)
│ │ └── NS (name servers)
Résolution DNS
- Client → Resolver local (/etc/resolv.conf)
- Resolver → Root server → TLD server → Authoritative
- Résultat : cache local + TTL
DNS Record Types :
| Type | Usage | Exemple |
|---|---|---|
| A | IPv4 | 192.0.2.1 |
| AAAA | IPv6 | 2001:db8::1 |
| CNAME | Alias | www → example.com |
| MX | mail.example.com | |
| TXT | Texte | v=spf1 include:_spf.google.com |
| NS | Serveur DNS | ns1.example.com |
| PTR | Reverse | 192.0.2.1 → hostname |
| SRV | Service | _sip._tcp.example.com |
DNS Propagation : TTL (60s-86400s), négative caching (NXDOMAIN)
DNS Security : DNSSEC (signatures), DoH/DNS over HTTPS, DoT/DNS over TLS
7. Load Balancing
Algorithmes
| Algo | Description | Usage |
|---|---|---|
| Round Robin | Distribution cyclique | Simple, prévisible |
| Least Connections | À la plus petite charge | Sessions longues |
| IP Hash | Hachage IP client | Session persistence |
| Random | Aléatoire | Tests |
| Weighted | Poids configurable | Hétérogène |
| Geographic | Par région | Latence minimale |
| Consistent Hashing | Anneau de hachage | Cache, scaling |
Reverse Proxy
Client → [Nginx/Caddy/Traefik] → App Server 1
App Server 2
App Server 3
Fonctions : SSL termination, caching, compression, rate limiting, WAF, static files
Layer 4 vs Layer 7
| Critère | L4 (TCP/UDP) | L7 (HTTP/gRPC) |
|---|---|---|
| Décision | IP + port | URL, headers, cookies |
| Performance | Très haute | Haute |
| Features | Limitée | Routing intelligent |
| Exemples | HAProxy, AWS NLB | Nginx, Traefik, AWS ALB |
Health Checks
- Passive : détection d'erreurs sur le trafic réel (5xx consecutifs → retrait)
- Active : requêtes périodiques /health (interval, timeout, threshold)
Connection Draining
- Serveur marqué "draining"
- Nouvelles connexions redirigées ailleurs
- Connexions existantes ont un timeout (graceful shutdown)
- Une fois vide, serveur retiré
Références
- RFC 7230-7235 (HTTP/1.1), RFC 7540 (HTTP/2), RFC 9114 (HTTP/3)
- TCP/IP Illustrated (Stevens)
- Computer Networking: A Top-Down Approach (Kurose & Ross)
- The Linux Programming Interface (Kerrisk)
- System Performance (Gregg)