Retour à l'accueil

Zig pour les démons réseau : expérience MTProto et IA

L'article analyse l'utilisation de Zig dans MTProto-proxy à haute charge et infrastructure IA edge. Décrit l'architecture avec slots epoll, comptime pour TLS, dispatch vtable. Comparaison avec C et Rust sur mémoire, erreurs, build.

Zig vs Rust : proxy et IA edge sans allocations
Advertisement 728x90

Zig pour les services réseau à haute charge : retours d'expérience réels

Dans les services réseau haute performance et les applications edge, Zig excelle grâce à son contrôle explicite de la mémoire et son architecture minimaliste. Deux projets — un proxy MTProto et une infrastructure d'agents IA — montrent comment le langage gère l'analyse sans allocation, la manipulation de bits et des limites RAM strictes, sans les pièges de sécurité de C ni la rigidité de Rust.

MTProto.zig masque le trafic en HTTPS pour contourner l'inspection approfondie des paquets (DPI). Nullclaw intègre une pile IA complète dans un binaire pour des appareils à 32 Mo de RAM, avec fournisseurs interchangeables à chaud.

Comparaison des langages sur les aspects clés

| Critère | C | Rust | Zig |

Google AdInline article slot

|-------------------|----------------------------|-----------------------------------|----------------------------------|

| Mémoire | Implicite, malloc dans libs| Implicite, allocateur global | Explicite, allocateur en arg |

| Multithreading | Pthreads, synchro manuelle | Send/Sync, Borrow Checker | Atomics, délégué au code |

Google AdInline article slot

| Erreurs | Codes de retour, errno | Result<T, E>, ? | !T, catch, errdefer |

| Métaprogrammation | Macros préprocesseur | Macros procéduraux | comptime |

| Build | CMake, Makefiles | Cargo | build.zig, LLVM natif |

Google AdInline article slot

La gestion explicite de la mémoire de Zig élimine les allocations cachées, essentielle pour les boucles d'événements sur epoll.

Architecture de MTProto.zig : sans allocation et avec slots

Le serveur tourne en boucle d'événements mono-threadée, sans threads par connexion. Au lieu d'allouer pour chaque client, il utilise un pool pré-alloué de slots :

  • Démarrage : Allouer un tableau de slots en mémoire statique.
  • Nouveau client : Prendre un slot libre en O(1).
  • Lecture : Asynchrone via epoll.
  • Déconnexion : Réinitialiser l'état du slot.

Cela évite la fragmentation du tas et les erreurs OOM dues aux sockets idle longue durée. En Rust, des threads avec piles de 256 Ko épuiseraient vite la RAM.

L'approche classique threads + malloc provoque des allocations dans les hot paths. Les machines d'état de Zig assurent la prévisibilité.

Logging et défis d'optimisation

Le logger par défaut de Zig bloque la boucle d'événements avec des write() bloquants sous charge. Des centaines d'appels log.debug depuis les connexions ont provoqué des échecs en cascade.

Solution : Profil de build ReleaseFast supprime les logs debug à la compilation. Le logging est sorti des hot paths.

Dispatch dynamique dans Nullclaw

Pour interchangeable à chaud les fournisseurs IA, il utilise une vtable inspirée du noyau Linux :

const AiProvider = struct {
    ptr: *anyopaque, // Pointeur brut vers l'état
    vtable: const VTable,

    pub const VTable = struct {
        generate_response: *const fn(ptr: *anyopaque, prompt: []const u8) anyerror![]const u8,
        deinit: *const fn(ptr: *anyopaque) void,
    };

    pub fn ask(self: AiProvider, prompt: []const u8) ![]const u8 {
        return self.vtable.generate_response(self.ptr, prompt);
    }
};

Cela permet des appels sans fat-pointer en 2 ms au cold start. Le dyn Trait + Arc<Mutex<Box<dyn Provider>>> + Tokio de Rust gonfle le binaire.

Comptime pour un faux TLS

La génération de faux ServerHello TLS 1.3 se fait à la compilation :

const NGINX_HELLO_BYTES = comptime blk: {
    var template: [128]u8 = undefined;
    fillFakeTlsExtensions(&template);
    std.debug.assert(template.len == EXPECTED_TLS_SIZE);
    break :blk template;
};

Le tableau de bytes est gravé dans .rodata. À l'exécution, seul le patch des données de session — sans allocations. Rust nécessiterait des macros procéduraux.

Enseignements clés

  • Mémoire explicite : Pas d'allocations cachées, idéal pour l'analyse zero-copy et les appareils edge.
  • Comptime : Métaprogrammation comme du code normal, générant des structures de paquets valides au build.
  • Minimalisme : Binaires sans runtime, cold starts <3 ms, respect strict des limites RAM.
  • Dispatch Vtable : Architecture plugin sans surcharge des traits Rust.
  • Epoll + Slots : Scalabilité sans threads, O(1) par connexion.

Recommandations finales

Zig remplace C dans les démons bas niveau exigeants en mémoire et performance. Pour des backends d'équipe complexes, Rust l'emporte avec ses vérifications à la compilation. L'écosystème Zig grandit mais prudence avec les dépendances.

— Editorial Team

Advertisement 728x90

Lire ensuite