Hollywood est une construction de moteurs à acteur ultra rapide pour la vitesse et les applications à faible latence. Pensez aux serveurs de jeux, aux courtiers publicitaires, aux moteurs de trading, etc ... il peut gérer 10 millions de messages en moins de 1 seconde .
Le modèle d'acteur est un modèle de calcul utilisé pour construire des systèmes très concurrents et distribués. Il a été introduit par Carl Hewitt en 1973 comme un moyen de gérer les systèmes complexes d'une manière plus évolutive et tolérante aux pannes.
Dans le modèle d'acteur, le bloc de construction de base est un acteur, parfois appelé récepteur à Hollywood, qui est une unité de calcul indépendante qui communique avec d'autres acteurs en échangeant des messages. Chaque acteur a son propre état et son propre comportement, et ne peut communiquer avec d'autres acteurs qu'en envoyant des messages. Ce paradigme de transfert de message permet un système hautement décentralisé et tolérant aux pannes, car les acteurs peuvent continuer à fonctionner indépendamment même si d'autres acteurs échouent ou deviennent indisponibles.
Les acteurs peuvent être organisés en hiérarchies, avec des acteurs de niveau supérieur supervisant et coordonnant les acteurs de niveau inférieur. Cela permet la création de systèmes complexes qui peuvent gérer les échecs et les erreurs d'une manière gracieuse et prévisible.
En utilisant le modèle d'acteur dans votre application, vous pouvez créer des systèmes hautement évolutifs et tolérants aux pannes qui peuvent gérer un grand nombre d'utilisateurs simultanés et des interactions complexes.
Livraison de messages garantis sur la défaillance de l'acteur (mécanisme tampon)
Messagerie d'incendie et d'oubli ou de demande et de réponse, ou les deux
DRPC haute performance comme couche de transport
Tampons proto optimisés sans réflexion
Léger et hautement personnalisable
Support de cluster pour l'écriture d'acteurs à découverte auto distribuée
make bench
spawned 10 engines spawned 2000 actors per engine Send storm starting, will send for 10s using 20 workers Messages sent per second 3244217 .. Messages sent per second 3387478 Concurrent senders: 20 messages sent 35116641, messages received 35116641 - duration: 10s messages per second: 3511664 deadletters: 0
go get github.com/anthdm/hollywood/...
Hollywood a besoin de Golang Version
1.21
Nous vous recommandons de commencer par écrire quelques exemples qui s'exécutent localement. L'exécution localement est un peu plus simple car le compilateur est capable de déterminer les types utilisés. Lors de l'exécution à distance, vous devrez fournir des définitions de protobuffer pour le compilateur.
Passons par un message Hello World. L'exemple complet est disponible dans le dossier Hello World. Commençons par le principal:
moteur, err: = acteur.newEngine (acteur.newEngineConfig ())
Cela crée un nouveau moteur. Le moteur est au cœur d'Hollywood. Il est responsable de la frai des acteurs, de l'envoi de messages et de la gestion du cycle de vie des acteurs. Si Hollywood ne crée pas le moteur, il renverra une erreur. Pour le développement, vous ne devez pas utiliser pour transmettre des options au moteur afin que vous puissiez passer nil. Nous examinerons les options plus tard.
Ensuite, nous devrons créer un acteur. Ce sont parfois appelés Receivers
après l'interface qu'ils doivent implémenter. Créons un nouvel acteur qui imprimera un message lorsqu'il recevra un message.
pid: = moteur.spawn (newhelloer, "bonjour")
Cela fera engendrera le moteur un acteur avec l'identifiant "bonjour". L'acteur sera créé par la fonction fournie newHelloer
. Les ID doivent être uniques. Il renverra un pointeur à un PID. Un PID est un identifiant de processus. C'est un identifiant unique pour l'acteur. La plupart du temps, vous utiliserez le PID pour envoyer des messages à l'acteur. Contre les systèmes distants, vous utilisez l'ID pour envoyer des messages, mais sur les systèmes locaux, vous utiliserez principalement le PID.
Regardons la fonction newHelloer
et l'acteur qu'il renvoie.
type helloer struct {} func newhelloer () acteur.receiver {return & helloer {} }
Assez simple. La fonction newHelloer
renvoie un nouvel acteur. L'acteur est une structure qui implémente l'acteur. Regardons la méthode Receive
.
Type Message struct {} func (h * helloer) reçoit (ctx * acteur.context) {switch msg: = ctx.mesage (). (type) {case acteur.initialized: fmt.println ("Helloer a initialisé") acteur.started: fmt.println ("Helloer a démarré") Case acteur.stopped: fmt.println ("Helloer a arrêté") Case * Message: fmt.println ("Hello World", msg.data) } }
Vous pouvez voir que nous définissons une structure de message. C'est le message que nous enverrons à l'acteur plus tard. La méthode de réception gère également quelques autres messages. Ces messages de cycle de vie sont envoyés par le moteur à l'acteur, vous les utiliserez pour initialiser votre acteur
Le moteur transmet un acteur.Conteal à la méthode Receive
. Ce contexte contient le message, le PID de l'expéditeur et certaines autres dépendances que vous pouvez utiliser.
Maintenant, envoyons un message à l'acteur. Nous enverrons un message
, mais vous pouvez envoyer n'importe quel type de message souhaité. La seule exigence est que l'acteur doit être en mesure de gérer le message. Pour que les messages puissent traverser le fil, ils doivent être sérialisables. Pour que Protobuf puisse sérialiser le message, il doit être un pointeur. Les messages locaux peuvent être de n'importe quel type.
Enfin, envoyons un message à l'acteur.
moteur.Send (pid, "Hello World!")
Cela enverra un message à l'acteur. Hollywood acheminera le message vers le bon acteur. L'acteur imprimera ensuite un message à la console.
Le dossier Exemples est le meilleur endroit pour apprendre et explorer davantage Hollywood.
Lorsque vous engendrez un acteur, vous devrez fournir une fonction qui renvoie un nouvel acteur. Comme l'acteur se reproduit, il existe quelques options réglables que vous pouvez fournir.
e.spawn (newfoo, "myActorname")
Parfois, vous voudrez passer des arguments au constructeur d'acteur. Cela peut être fait en utilisant une fermeture. Il y en a un exemple dans l'exemple de la demande. Regardons le code.
Le constructeur par défaut ressemblera à ceci:
func newnamiseRonder () acteur.receiver {return & nameResponder {name: "noname"} }
Pour construire un nouvel acteur avec un nom, vous pouvez faire ce qui suit:
func newCustomNameResponder (name String) actor.producer {return func () acteur.receiver {return & nameRester {name} } }
Vous pouvez ensuite engendrer l'acteur avec le code suivant:
pid: = moteur.spawn (newCustomNameResponder ("Anthony"), "nom-réponse")
e.spawn (newfoo, "MyActorname", acteur.WithMaxRestarts (4), acteur.WithinboxSize (1024 * 2), acteur.withId ("bar"), ) )
Les options devraient être assez explicites. Vous pouvez définir le nombre maximum de redémarrages, qui indique au moteur combien de fois l'acteur donné doit être redémarré en cas de panique, la taille de la boîte de réception, qui fixe une limite sur la façon et les messages non transformés que la boîte de réception peut retenir avant de commencer pour bloquer.
Les acteurs sans état peuvent être engendrés en fonction, car il est rapide et simple.
e.spawnfunc (func (c * actor.context) {switch msg: = c.Message (). (type) {case acteur.started: fmt.println ("démarré") _ = msg } }, "foo")
Les acteurs peuvent communiquer entre eux sur le réseau avec le package distant. Cela fonctionne de la même manière que les acteurs locaux mais "Over the Wire". Hollywood prend en charge la sérialisation avec Protobuf.
Remote.new () prend une adresse d'écoute et une structure Remote.Config.
Vous allez instancier une nouvelle télécommande avec le code suivant:
tlsconfig: = tlsconfig: & tls.config {certificats: [] tls.certificate {cert}, } config: = reight.newConfig (). withtls (tlsconfig) Remote: = reight.new ("0.0.0.0:2222", config) moteur, err: = actor.newEngine (actor.newEngineConfig (). WithReMote (distant) )
Regardez les exemples de l'acteur distant et le client de chat et le serveur pour plus d'informations.
Dans un système de production, il finira par aller mal. Les acteurs se bloqueront, les machines échoueront, les messages finiront dans la file d'attente de la mort. Vous pouvez créer un logiciel qui peut gérer ces événements d'une manière gracieuse et prévisible en utilisant le flux d'événements.
L'événement est une abstraction puissante qui vous permet de construire des systèmes flexibles et enfichables sans dépendances.
Abonnez-vous à tout acteur à une liste variée des événements système
Diffuser vos événements personnalisés à tous les abonnés
Notez que les événements qui ne sont gérés par aucun acteur seront abandonnés. Vous devriez avoir un acteur abonné au flux d'événements afin de recevoir des événements. Au strict minimum, vous voudrez gérer DeadLetterEvent
. Si Hollywood ne fait pas de message à un acteur, il enverra un DeadLetterEvent
au flux d'événements.
Tout événement qui remplit l'interface actor.LogEvent
sera enregistré à la bûcheron par défaut, avec le niveau de gravité, le message et les attributs de l'événement défini par la méthode actor.LogEvent
log()
.
actor.ActorInitializedEvent
, un acteur a été initialisé mais n'a pas traité son actor.Started message
actor.ActorStartedEvent
, un acteur a commencé
actor.ActorStoppedEvent
, un acteur s'est arrêté
actor.DeadLetterEvent
, un message n'a pas été transmis à un acteur
actor.ActorRestartedEvent
, un acteur a redémarré après un accident / panique.
actor.RemoteUnreachableEvent
, envoyant un message sur le fil à une télécommande qui n'est pas accessible.
cluster.MemberJoinEvent
, un nouveau membre rejoint le cluster
cluster.MemberLeaveEvent
, un nouveau membre a quitté le cluster
cluster.ActivationEvent
, un nouvel acteur est activé sur le cluster
cluster.DeactivationEvent
, un acteur est désactivé sur le cluster
Il existe un exemple de surveillance des éléments d'événements qui vous montre comment utiliser le flux d'événements. Il dispose de deux acteurs, l'un est instable et se bloquera chaque seconde. L'autre acteur est abonné au flux d'événements et conserve quelques compteurs pour différents événements tels que des accidents, etc.
L'application fonctionnera pendant quelques secondes et le poison l'acteur instable. Il interrogera ensuite le moniteur avec une demande. Alors que les acteurs flottent à l'intérieur du moteur, c'est ainsi que vous interagissez avec eux. Main imprimera ensuite le résultat de la requête et l'application sortira.
Nous utilisons le modèle d'option de fonction. Toutes les options de fonction sont dans le package d'acteurs et démarrent leur nom avec "EngineOPT". Actuellement, la seule option est de fournir une télécommande. Ceci est fait par
R: = Remote.New (Remote.Config {écouteraDDR: Addr}), err: = Actor.NewEngine (Actor.EngineOptremote (R))
Addr est une chaîne avec le format "hôte: port".
Vous pouvez ajouter des middleware personnalisés à vos récepteurs. Cela peut être utile pour stocker des mesures, enregistrer et charger des données pour vos récepteurs sur actor.Started
et actor.Stopped
.
Pour des exemples sur la façon d'implémenter le middleware personnalisé, consultez le dossier middleware dans les exemples
Hollywood a une journalisation intégrée. Il utilisera le journaliste par défaut du package log/slog
. Vous pouvez configurer le journaliste à votre goût en définissant le journal par défaut à l'aide de slog.SetDefaultLogger()
. Cela vous permettra de personnaliser le niveau de journal, le format et la sortie. Veuillez consulter le package slog
pour plus d'informations.
Notez que certains événements peuvent être enregistrés dans le journaliste par défaut, comme DeadLetterEvent
et ActorStartedEvent
car ces événements remplissent l'interface actor.LogEvent
. Voir la section EventsTream ci-dessus pour plus d'informations.
make test
Rejoignez notre communauté Discord avec plus de 2000 membres pour des questions et une belle conversation.
Ce projet est actuellement utilisé dans la production par les organisations / projets suivants:
Sensora IoT
Hollywood est autorisé sous la licence du MIT.