Implémentation asynchrone évolutive et faiblement cohérente du protocole d'appartenance au groupe de processus (SWIM) écrit en Rust.
Avertissement
Avertissement : ce projet est un travail en cours et n'est pas encore prêt pour la production. La base de code peut contenir des bugs ou des fonctionnalités incomplètes. A utiliser avec prudence. Commentaires et contributions bienvenus, merci !.
io_uring
via Tokio (SWIM:Basic)
+ (SWIM+Inf.)
+ (SWIM+Inf.+Susp.)
et étendez-le pour inclure les fonctionnalités de Lifeguard de Hashicorp Research Gossipod utilise 3 types de messages : PING
, PING-REQ
et BROADCAST
(qui incluent les sous-types JOIN
, LEAVE
, SUSPECT
, ALIVE
et CONFIRM
). Les messages PING
et PING-REQ
sont au cœur du mécanisme de détection de panne du système basé sur SWIM, facilitant l'échange d'état constant en s'appuyant sur la diffusion d'informations sur ce processus. Lorsqu'un changement d'état se produit, soit par le biais de demandes volontaires, soit par détection régulière de pannes, le système utilise des messages BROADCAST
pour diffuser aléatoirement ces informations. Chaque nœud du réseau conserve un numéro d'incarnation, commençant à zéro, qui ne peut être incrémenté que par le nœud lui-même. Ce numéro est crucial pour gérer l'état du nœud dans les listes de membres locales des autres nœuds et sert de moyen pour réfuter les soupçons (SWIM+Inf.+Susp.)
des autres nœuds. Cette conception permet à Gossipod d'obtenir une gestion distribuée de l'état et une détection des pannes efficaces et résilientes dans un réseau distribué ou décentralisé, en équilibrant les besoins d'informations à jour, de résolution de conflits et de fiabilité du système.
Par extension, Lifeguard propose des fonctionnalités d'implémentation supplémentaires qui ne sont pas encore intégrées à la version actuelle de Gossipod et qui sont entièrement prises en charge par la liste des membres Hashicorp. Je prévois d'intégrer ces fonctionnalités dans une prochaine version.
diagramme de séquence
participant N1 en tant que nœud 1
participant N2 en tant que nœud 2
participant N3 en tant que nœud 3
participant Nx en tant que nœud x (aléatoire)
Remarque sur N1, Nx : Sondage et diffusion d'informations via le ferroutage
N1->>N2 : PING (avec informations superposées)
N2->>N1 : ACK (avec informations superposées)
N1->>N3 : PING-REQ (pour N2)
N3->>N2 : PING
N2->>N3 : ACK
N3->>N1 : ACK (indirect)
Remarque sur N1,Nx : diffusion des changements d'état
N1->>Nx : DIFFUSION (QUITTER/REJOINDRE/SUSPECT/CONFIRMER)
Nx->>N2 : PING (avec changements d'état superposés)
N2->>N3 : PING (avec changements d'état superposés)
Remarque sur N1, Nx : Détection et récupération des pannes
N1->>N2 : PING (pas de réponse)
N1->>N3 : PING-REQ (pour N2)
N3->>N2 : PING (pas de réponse)
N3->>N1 : NACK
N1->>Nx : DIFFUSION (SUSPECT N2)
Remarque sur N2 : N2 reçoit un message SUSPECT
N2->>N2 : incrémenter le numéro d'incarnation
N2->>Nx : DIFFUSION (ALIVE avec nouveau numéro d'incarnation)
alt N2 réfute avec succès
Nx->>N1 : PING (avec ALIVE N2 superposé)
Remarque sur N1 : Mettre à jour le statut N2 sur Vivant
sinon N2 ne réfute pas à temps
N1->>Nx : DIFFUSION (CONFIRMER N2 comme échec)
fin
Pour voir Gossipod en action, consultez le répertoire ./examples
, qui comprend un certain nombre de démos. Ci-dessous, vous pouvez exécuter deux instances différentes de Gossipod en spécifiant une adresse de port et un nom de nœud différents.
> cargo run -p ping_node -- --name=NODE_1 --port=7948
> cargo run -p pong_node -- --name=NODE_2 --port=7947 --join-addr=127.0.0.1:7948
let config = GossipodConfigBuilder :: new ( )
. with_name ( & args . name )
. with_port ( args . port )
. with_addr ( args . ip . parse :: < Ipv4Addr > ( ) . expect ( "Invalid IP address" ) )
. with_probing_interval ( Duration :: from_secs ( 1 ) )
. with_ack_timeout ( Duration :: from_millis ( 500 ) )
. with_indirect_ack_timeout ( Duration :: from_secs ( 1 ) )
. with_suspicious_timeout ( Duration :: from_secs ( 5 ) )
. with_network_type ( NetworkType :: LAN )
. build ( )
. await ? ;
let gossipod = Gossipod :: new ( config ) . await ? ;
# [ derive ( Clone , Debug , Serialize , Deserialize , PartialEq , Eq , Hash ) ]
struct Metadata {
region : String ,
datacenter : String ,
}
impl NodeMetadata for Metadata { }
let metadata = Metadata {
region : "aws-west-1" . to_string ( ) ,
datacenter : "dc1" . to_string ( ) ,
} ;
let gossipod = Gossipod :: with_metadata ( config , metadata ) . await ? ;
Voici un exemple de base de la façon d'utiliser Gossipod dans votre application :
// Configuration
let config = GossipodConfigBuilder :: new ( )
. with_name ( & args . name )
. with_port ( args . port )
. with_addr ( args . ip . parse :: < Ipv4Addr > ( ) . expect ( "Invalid IP address" ) )
. with_probing_interval ( Duration :: from_secs ( 1 ) )
. with_ack_timeout ( Duration :: from_millis ( 500 ) )
. with_indirect_ack_timeout ( Duration :: from_secs ( 1 ) )
. with_suspicious_timeout ( Duration :: from_secs ( 5 ) )
. with_network_type ( NetworkType :: LAN )
. build ( )
. await ? ;
// New instance
let gossipod = Arc :: new ( Gossipod :: new ( config . clone ( ) ) . await ? ) ;
// Start Gossipod
tokio :: spawn ( gossipod . clone ( ) . start ( ) ) ;
// Wait for Gossipod to start
while !gossipod . is_running ( ) . await {
time :: sleep ( Duration :: from_millis ( 100 ) ) . await ;
}