webhook-proxy
Une couche intermédiaire entre nos services et les webhooks Discord. Elle met les événements en file par cible, respecte les limites 429 et réessaie les erreurs 5xx, de sorte que les notifications ne se perdent pas lors des pics de trafic. Un point unique par lequel passe toute la communication sortante.
Un point de sortie unique pour toute la communication sortante vers Discord. Il met les événements en file par cible, respecte les limites 429 avec un backoff correct et rejoue les erreurs 5xx, au lieu de perdre des notifications lors d'un pic de trafic. Le résultat est simple à énoncer : un événement ne disparaît pas juste parce qu'une limite de débit est tombée.
Introduction
Nos services parlent beaucoup sur Discord : alarmes de monitoring, notifications d'événements, journaux d'exploitation. Chacun d'eux part par un webhook, et les webhooks de Discord ont des limites de débit strictes. Tant que le trafic est calme, personne ne le sent. Le problème commence exactement quand la notification est la plus nécessaire - lors d'un pic, quand plusieurs services émettent à la fois.
webhook-proxy est né pour arrêter de bricoler la gestion des limites dans chaque service séparément. À la place, toute la communication sortante passe par un point qui prend la livraison à sa charge : il met en file, attend quand il le faut, rejoue quand cela en vaut la peine et ne perd pas l'événement en route. Pour les services derrière lui, rien ne change - ils envoient comme avant.
Le problème qu'on ne voit que sous charge
Sous un trafic calme, l'envoi naïf d'un webhook fonctionne parfaitement, et c'est justement ce qui le rend traître. Le code tire une requête, obtient un 2xx, continue - et personne n'a de raison de soupçonner quoi que ce soit de fragile. Ce qui est fragile, c'est seulement le chemin d'erreur, qui sous un trafic calme ne se déclenche presque jamais.
Il suffit d'un pic pour l'exposer. Plusieurs services envoient des notifications à la fois, vous atteignez la limite et obtenez un 429, ou Discord renvoie momentanément un 5xx. Dans l'approche naïve, cet événement disparaît simplement : le code continue, l'exception atterrit au mieux dans un journal, et sur Discord il manque l'entrée qui devait s'y trouver. Le pire, c'est que vous le perdez précisément au pic de trafic, c'est-à-dire quand les notifications comptent le plus.
C'est pourquoi le proxy n'est pas une optimisation de performance. C'est une garantie de livraison dans des conditions où l'envoi naïf échoue en silence.
chaque webhook a sa propre file, de sorte qu'une cible lente ne bloque pas les autres.
sur une limite, le proxy attend exactement aussi longtemps que l'indique l'en-tête Retry-After, pas au juge.
une erreur serveur signifie un backoff exponentiel et une nouvelle tentative, pas un événement perdu.
un payload malformé ne deviendra pas valide à la troisième tentative, donc nous n'y gaspillons pas de tentatives.
Une file qui ne bloque pas le reste
La première décision est une file séparée pour chaque cible. Si tous les événements passaient par une file commune, un seul webhook lent ou bloqué arrêterait la livraison à tous les autres - une cible bouchée et le silence partout. Séparer les files fait qu'un problème avec un webhook reste avec ce webhook-là.
La même structure donne de la résilience aux pics. Quand une vague arrive, les événements ne sont pas perdus - ils atterrissent dans la file de leur cible et attendent une fenêtre où la limite se relâche. De l'extérieur, cela ressemble à un retard momentané plutôt qu'à une perte, et c'est une qualité tout à fait différente : une notification retardée remplit encore son rôle, une notification perdue n'en remplit aucun.
Le cœur : la boucle de livraison
Toute la logique se réduit à une décision par réponse : le succès la termine, 429 dit d'attendre, 5xx dit de rejouer, un autre 4xx signifie s'arrêter sans rejouer. Distinguer ces cas, c'est la différence entre un proxy qui livre et un qui perd des événements ou mouline sans fin sur un payload cassé.
Asymétrie : 429 contre 5xx
La clé, c'est l'asymétrie entre 429 et 5xx. Sur un 429, Discord dit lui-même combien de temps attendre - nous le lisons dans l'en-tête et attendons exactement cela, ni moins ni plus. Sur un 5xx, personne ne dit rien, donc nous ajoutons un backoff exponentiel pour ne pas marteler un serveur momentanément tombé avec une rafale de tentatives immédiates.
L'erreur la plus fréquente est de tout rejouer aveuglément. Sur un 429, rejouer a du sens, mais sur un 400 ou un 404, non : le payload ne se réparera pas tout seul, et vous ne faites qu'atteindre la limite plus vite et mouliner sans fin sur quelque chose qui ne passera jamais. Rejouez ce qui est transitoire, et rien d'autre. Le reste devrait échouer immédiatement, bruyamment et une seule fois.
L'effet : un pic que l'autre côté ne voit jamais
Pour les services derrière lui, le proxy est invisible - ils envoient exactement comme avant et ne savent rien des limites ni des rejeux. Toute cette complexité se trouve à un seul endroit au lieu d'être étalée sur chaque service séparément. Cela signifie aussi qu'une correction dans la gestion des limites se fait une fois, pas dans plusieurs bases de code à la fois.
Ce que le proxy fait d'une réponse (à titre illustratif)
La différence ne se voit qu'à la prochaine vague de trafic, et c'est tout l'intérêt. Les notifications ne disparaissent pas en silence au moment où elles sont les plus nécessaires - elles attendent dans la file et arrivent dès que la limite se relâche. Un point de sortie a transformé la perte silencieuse d'événements en un retard tout au plus momentané, et c'est toute la différence entre un monitoring auquel on peut se fier et un monitoring qui se tait pile quand le feu se déclare.
Plus de projets
D'autres réalisations de la même catégorie - découvrez comment nous abordons des défis similaires.
Vous avez un projet similaire ?
Contactez-nous - le devis est gratuit et arrive sous une heure.



