Questions d'Entretien sur les Reseaux Distribues: CAP, Consensus et Conception Systemes (2026)

Les reseaux distribues sont fondamentaux pour les systemes modernes - des microservices a la blockchain en passant par les CDN mondiaux. Comprendre ces concepts est crucial pour les postes d'ingenieur senior. Ce guide couvre les questions d'entretien les plus courantes avec des reponses detaillees et des exemples pratiques.

Fondamentaux

1. Qu'est-ce qu'un systeme distribue ?

Reponse : Un systeme distribue est un ensemble d'ordinateurs independants qui apparaissent aux utilisateurs comme un systeme unique et coherent. Caracteristiques cles :

  • Concurrence : Les composants s'executent simultanement
  • Pas d'horloge globale : Les noeuds ont des horloges independantes
  • Pannes independantes : Les composants peuvent tomber en panne sans affecter les autres
  • Passage de messages : Communication via des messages reseau

Exemples : Google Search, streaming Netflix, reseau Bitcoin.

2. Expliquez le theoreme CAP

Reponse : Le theoreme CAP stipule qu'un systeme distribue ne peut garantir que deux des trois proprietes suivantes :

  • Coherence : Tous les noeuds voient les memes donnees au meme moment
  • Disponibilite : Chaque requete recoit une reponse
  • Tolerance aux partitions : Le systeme continue de fonctionner malgre les pannes reseau

En pratique, la tolerance aux partitions est requise (les reseaux tombent en panne), vous choisissez donc entre CP (coherence) ou AP (disponibilite) :

CP Systems: MongoDB, HBase, Redis Cluster
- Sacrifice availability during partitions
- Strong consistency guarantees

AP Systems: Cassandra, DynamoDB, CouchDB
- Remain available during partitions
- Eventually consistent

3. Qu'est-ce que la coherence eventuelle ?

Reponse : La coherence eventuelle garantit que si aucune nouvelle mise a jour n'est effectuee, toutes les repliques convergeront finalement vers la meme valeur. C'est une garantie plus faible que la coherence forte mais elle permet une plus grande disponibilite.

// Example: DNS propagation
// Update takes time to propagate globally
// Different users may see different values temporarily
// Eventually, all DNS servers have the same record

// Conflict resolution strategies:
// 1. Last-write-wins (LWW) - timestamp-based
// 2. Vector clocks - track causality
// 3. CRDTs - mathematically guaranteed convergence

4. Expliquez la difference entre la scalabilite horizontale et verticale

Reponse :

AspectScalabilite verticaleScalabilite horizontale
MethodeAjouter des ressources a une machineAjouter plus de machines
CoutCouteux a grande echelleMateriel standard
LimitePlafond materielTheoriquement illimitee
ComplexiteSimpleNecessite une logique de distribution
Temps d'arretGeneralement requisZero temps d'arret possible

Consensus et coordination

5. Qu'est-ce que l'algorithme de consensus Raft ?

Reponse : Raft est un algorithme de consensus pour la gestion d'un journal replique. Il est concu pour etre comprehensible (contrairement a Paxos). Composants cles :

  • Election du leader : Un noeud est elu leader et gere toutes les requetes clients
  • Replication du journal : Le leader replique les entrees vers les suiveurs
  • Securite : Seuls les noeuds avec des journaux a jour peuvent devenir leader
Raft states:
1. Follower - Default state, responds to leader
2. Candidate - Requesting votes for leadership
3. Leader - Handles all client requests

Election process:
1. Follower timeout expires
2. Becomes candidate, increments term
3. Requests votes from peers
4. Majority votes = new leader
5. Sends heartbeats to maintain leadership

6. Qu'est-ce qu'un verrou distribue ?

Reponse : Un verrou distribue garantit qu'un seul processus parmi plusieurs noeuds peut acceder a une ressource. Defis d'implementation :

// Redis distributed lock (Redlock algorithm)
const Redis = require('ioredis');

async function acquireLock(redis, key, ttl) {
  const token = crypto.randomUUID();
  const result = await redis.set(key, token, 'NX', 'PX', ttl);
  return result === 'OK' ? token : null;
}

async function releaseLock(redis, key, token) {
  // Lua script for atomic check-and-delete
  const script = `
    if redis.call("get", KEYS[1]) == ARGV[1] then
      return redis.call("del", KEYS[1])
    else
      return 0
    end
  `;
  return redis.eval(script, 1, key, token);
}

// Usage
const token = await acquireLock(redis, 'my-resource', 30000);
if (token) {
  try {
    // Critical section
  } finally {
    await releaseLock(redis, 'my-resource', token);
  }
}

7. Expliquez les horloges vectorielles

Reponse : Les horloges vectorielles suivent la causalite entre les evenements dans les systemes distribues. Chaque noeud maintient un vecteur d'horodatages logiques :

// Vector clock example with 3 nodes
// Initial: [0, 0, 0]

// Node A sends message: [1, 0, 0]
// Node B receives and sends: [1, 1, 0]
// Node C receives: [1, 1, 1]

// Comparison rules:
// V1 < V2 if all V1[i] <= V2[i] and at least one V1[i] < V2[i]
// V1 || V2 (concurrent) if neither V1 < V2 nor V2 < V1

class VectorClock {
  constructor(nodeId, numNodes) {
    this.nodeId = nodeId;
    this.clock = new Array(numNodes).fill(0);
  }

  increment() {
    this.clock[this.nodeId]++;
    return [...this.clock];
  }

  update(received) {
    for (let i = 0; i < this.clock.length; i++) {
      this.clock[i] = Math.max(this.clock[i], received[i]);
    }
    this.clock[this.nodeId]++;
  }

  compare(other) {
    let less = false, greater = false;
    for (let i = 0; i < this.clock.length; i++) {
      if (this.clock[i] < other[i]) less = true;
      if (this.clock[i] > other[i]) greater = true;
    }
    if (less && !greater) return -1; // this happened before
    if (greater && !less) return 1;  // other happened before
    return 0; // concurrent
  }
}

Protocoles reseau

8. Comparez TCP et UDP pour les systemes distribues

Reponse :

CaracteristiqueTCPUDP
ConnexionOriente connexionSans connexion
FiabiliteLivraison garantieAu mieux
OrdreOrdonnePas d'ordre
VitessePlus lent (poignee de main)Plus rapide
Cas d'usageHTTP, bases de donneesDNS, streaming, jeux
// When to use each:
// TCP: When you need reliability
// - Database connections
// - File transfers
// - API calls

// UDP: When speed matters more than reliability
// - Real-time gaming
// - Video streaming
// - DNS queries
// - Health checks

9. Qu'est-ce que gRPC et quand l'utiliseriez-vous ?

Reponse : gRPC est un framework RPC haute performance utilisant Protocol Buffers et HTTP/2 :

  • Protocole binaire : Charges utiles plus petites que JSON
  • Streaming : Support du streaming bidirectionnel
  • Generation de code : Clients/serveurs type-safe
  • HTTP/2 : Multiplexage, compression des en-tetes
// user.proto
syntax = "proto3";

service UserService {
  rpc GetUser(GetUserRequest) returns (User);
  rpc ListUsers(ListUsersRequest) returns (stream User);
  rpc CreateUser(User) returns (User);
}

message User {
  string id = 1;
  string name = 2;
  string email = 3;
}

message GetUserRequest {
  string id = 1;
}

10. Expliquez l'architecture service mesh

Reponse : Un service mesh est une couche d'infrastructure pour la communication service-a-service. Composants :

  • Plan de donnees : Les proxies sidecar (Envoy) gerent le trafic
  • Plan de controle : Gere la configuration des proxies
# Istio example - traffic splitting
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-service
spec:
  hosts:
  - my-service
  http:
  - route:
    - destination:
        host: my-service
        subset: v1
      weight: 90
    - destination:
        host: my-service
        subset: v2
      weight: 10

Avantages : mTLS, observabilite, gestion du trafic, nouvelles tentatives sans modification du code.

Tolerance aux pannes

11. Qu'est-ce que le pattern circuit breaker ?

Reponse : Le circuit breaker previent les pannes en cascade en echouant rapidement lorsqu'un service est defaillant :

class CircuitBreaker {
  constructor(options) {
    this.failureThreshold = options.failureThreshold || 5;
    this.resetTimeout = options.resetTimeout || 30000;
    this.state = 'CLOSED';
    this.failures = 0;
    this.lastFailure = null;
  }

  async call(fn) {
    if (this.state === 'OPEN') {
      if (Date.now() - this.lastFailure > this.resetTimeout) {
        this.state = 'HALF_OPEN';
      } else {
        throw new Error('Circuit breaker is OPEN');
      }
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  onSuccess() {
    this.failures = 0;
    this.state = 'CLOSED';
  }

  onFailure() {
    this.failures++;
    this.lastFailure = Date.now();
    if (this.failures >= this.failureThreshold) {
      this.state = 'OPEN';
    }
  }
}

12. Expliquez le pattern bulkhead

Reponse : Le bulkhead isole les composants pour que les pannes ne se propagent pas. Comme les compartiments d'un navire :

// Thread pool bulkhead
const criticalPool = new ThreadPool({ size: 10 });
const nonCriticalPool = new ThreadPool({ size: 5 });

// If non-critical service exhausts its pool,
// critical operations still have dedicated resources

// Semaphore bulkhead
class Bulkhead {
  constructor(maxConcurrent) {
    this.maxConcurrent = maxConcurrent;
    this.current = 0;
    this.queue = [];
  }

  async execute(fn) {
    if (this.current >= this.maxConcurrent) {
      throw new Error('Bulkhead full');
    }

    this.current++;
    try {
      return await fn();
    } finally {
      this.current--;
    }
  }
}

13. Qu'est-ce que la tolerance aux pannes byzantines ?

Reponse : La BFT gere les noeuds qui se comportent de maniere arbitraire (malveillante ou a cause de bugs). Le probleme des generaux byzantins :

  • Necessite 3f+1 noeuds pour tolerer f pannes byzantines
  • Utilise dans la blockchain (PBFT, Tendermint)
  • Plus couteux que la tolerance aux pannes par crash
PBFT phases:
1. Pre-prepare: Leader proposes value
2. Prepare: Nodes broadcast prepared messages
3. Commit: Nodes commit after 2f+1 prepares
4. Reply: Send result to client

Requires 2f+1 matching messages at each phase

Repartition de charge et routage

14. Comparez les algorithmes de repartition de charge

Reponse :

// Round Robin - Simple rotation
class RoundRobin {
  constructor(servers) {
    this.servers = servers;
    this.current = 0;
  }

  next() {
    const server = this.servers[this.current];
    this.current = (this.current + 1) % this.servers.length;
    return server;
  }
}

// Weighted Round Robin - Based on capacity
class WeightedRoundRobin {
  constructor(servers) {
    // servers: [{ host: 'a', weight: 3 }, { host: 'b', weight: 1 }]
    this.servers = [];
    for (const s of servers) {
      for (let i = 0; i < s.weight; i++) {
        this.servers.push(s.host);
      }
    }
    this.current = 0;
  }
}

// Least Connections - Route to least busy
class LeastConnections {
  constructor(servers) {
    this.connections = new Map(servers.map(s => [s, 0]));
  }

  next() {
    let min = Infinity, selected;
    for (const [server, count] of this.connections) {
      if (count < min) {
        min = count;
        selected = server;
      }
    }
    return selected;
  }
}

// Consistent Hashing - For caches/sharding
// Minimizes redistribution when nodes change

15. Expliquez le hachage coherent

Reponse : Le hachage coherent distribue les donnees entre les noeuds tout en minimisant la redistribution lorsque des noeuds sont ajoutes ou supprimes :

const crypto = require('crypto');

class ConsistentHash {
  constructor(replicas = 100) {
    this.replicas = replicas;
    this.ring = new Map();
    this.sortedKeys = [];
  }

  hash(key) {
    return crypto.createHash('md5')
      .update(key)
      .digest('hex')
      .substring(0, 8);
  }

  addNode(node) {
    for (let i = 0; i < this.replicas; i++) {
      const hash = this.hash(`${node}:${i}`);
      this.ring.set(hash, node);
      this.sortedKeys.push(hash);
    }
    this.sortedKeys.sort();
  }

  removeNode(node) {
    for (let i = 0; i < this.replicas; i++) {
      const hash = this.hash(`${node}:${i}`);
      this.ring.delete(hash);
      this.sortedKeys = this.sortedKeys.filter(k => k !== hash);
    }
  }

  getNode(key) {
    const hash = this.hash(key);
    for (const nodeHash of this.sortedKeys) {
      if (hash <= nodeHash) {
        return this.ring.get(nodeHash);
      }
    }
    return this.ring.get(this.sortedKeys[0]);
  }
}

Messagerie et files d'attente

16. Comparez les patterns de files de messages

Reponse :

  • Point-a-point : Un producteur, un consommateur (files de taches)
  • Pub/sub : Un producteur, plusieurs consommateurs (diffusion d'evenements)
  • Requete/reponse : Pattern de messagerie synchrone
Delivery guarantees:
- At most once: May lose messages (fastest)
- At least once: May duplicate (requires idempotency)
- Exactly once: Most complex, often needs transactions

Technologies:
- RabbitMQ: Traditional message broker, AMQP
- Kafka: Distributed log, high throughput
- Redis Streams: Simple, built into Redis
- AWS SQS: Managed, scalable

17. Qu'est-ce que le pattern outbox ?

Reponse : Le pattern outbox garantit une publication fiable des messages avec des transactions de base de donnees :

// Instead of:
await db.transaction(async (tx) => {
  await tx.insert('orders', order);
  await messageQueue.publish('order.created', order); // Can fail!
});

// Use outbox pattern:
await db.transaction(async (tx) => {
  await tx.insert('orders', order);
  await tx.insert('outbox', {
    event_type: 'order.created',
    payload: JSON.stringify(order),
    created_at: new Date()
  });
});

// Separate process polls outbox and publishes
async function processOutbox() {
  const events = await db.query(
    'SELECT * FROM outbox WHERE processed = false LIMIT 100'
  );

  for (const event of events) {
    await messageQueue.publish(event.event_type, event.payload);
    await db.update('outbox', { id: event.id }, { processed: true });
  }
}

Observabilite

18. Qu'est-ce que le tracage distribue ?

Reponse : Le tracage distribue suit les requetes a travers les frontieres des services :

// OpenTelemetry example
const { trace } = require('@opentelemetry/api');

const tracer = trace.getTracer('my-service');

async function handleRequest(req) {
  const span = tracer.startSpan('handleRequest');

  try {
    span.setAttribute('user.id', req.userId);

    // Child span for database call
    const dbSpan = tracer.startSpan('database.query', {
      parent: span
    });
    const result = await db.query('...');
    dbSpan.end();

    return result;
  } catch (error) {
    span.recordException(error);
    span.setStatus({ code: SpanStatusCode.ERROR });
    throw error;
  } finally {
    span.end();
  }
}

// Trace context propagation
// W3C Trace Context header: traceparent
// Format: version-trace_id-span_id-flags
// Example: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01

19. Expliquez les trois piliers de l'observabilite

Reponse :

  • Logs : Evenements discrets avec contexte
  • Metriques : Mesures numeriques dans le temps
  • Traces : Flux des requetes a travers les services
Logs: What happened?
- Structured JSON logs
- Correlation IDs
- Log levels (debug, info, warn, error)

Metrics: How is the system performing?
- Counters: request_count
- Gauges: active_connections
- Histograms: request_duration

Traces: Where did the request go?
- Spans with timing
- Parent-child relationships
- Cross-service context

Securite

20. Comment securisez-vous la communication service-a-service ?

Reponse :

  • mTLS : Authentification TLS mutuelle
  • Service mesh : mTLS automatique (Istio, Linkerd)
  • Cles API : Simple mais moins securise
  • JWT : Authentification sans etat
# Istio PeerAuthentication for mTLS
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: my-namespace
spec:
  mtls:
    mode: STRICT

Questions de conception de systeme

21. Concevez un limiteur de debit distribue

Reponse :

// Token bucket with Redis
class DistributedRateLimiter {
  constructor(redis, options) {
    this.redis = redis;
    this.capacity = options.capacity;
    this.refillRate = options.refillRate; // tokens per second
  }

  async isAllowed(key) {
    const now = Date.now();
    const script = `
      local key = KEYS[1]
      local capacity = tonumber(ARGV[1])
      local refillRate = tonumber(ARGV[2])
      local now = tonumber(ARGV[3])

      local bucket = redis.call('HMGET', key, 'tokens', 'lastRefill')
      local tokens = tonumber(bucket[1]) or capacity
      local lastRefill = tonumber(bucket[2]) or now

      -- Refill tokens
      local elapsed = (now - lastRefill) / 1000
      tokens = math.min(capacity, tokens + elapsed * refillRate)

      if tokens >= 1 then
        tokens = tokens - 1
        redis.call('HMSET', key, 'tokens', tokens, 'lastRefill', now)
        redis.call('EXPIRE', key, 60)
        return 1
      else
        return 0
      end
    `;

    return await this.redis.eval(script, 1, key,
      this.capacity, this.refillRate, now);
  }
}

22. Comment concevriez-vous un cache distribue ?

Reponse : Considerations cles :

  • Partitionnement : Hachage coherent entre les noeuds
  • Replication : Primaire-replica pour la tolerance aux pannes
  • Eviction : Basee sur LRU, LFU ou TTL
  • Coherence : Write-through, write-behind ou cache-aside
Cache-aside pattern:
1. Check cache
2. If miss, read from database
3. Update cache
4. Return data

Write-through pattern:
1. Write to cache
2. Cache writes to database
3. Ensures consistency but adds latency

Write-behind pattern:
1. Write to cache
2. Cache async writes to database
3. Better performance, eventual consistency

Conclusion

Les systemes distribues sont complexes mais comprendre ces concepts fondamentaux - consensus, tolerance aux pannes, reseaux et observabilite - vous preparera aux entretiens d'ingenieur senior. Concentrez-vous sur les compromis : il y a rarement une solution parfaite, seulement des compromis appropries pour des exigences specifiques.