Hollywood es una construcción de motor de actor ultra rápido para aplicaciones de velocidad y baja latencia. Piense en los servidores de juegos, los corredores de publicidad, los motores comerciales, etc. Puede manejar 10 millones de mensajes en menos de 1 segundo .
El modelo de actor es un modelo computacional utilizado para construir sistemas altamente concurrentes y distribuidos. Fue introducido por Carl Hewitt en 1973 como una forma de manejar sistemas complejos de una manera más escalable y tolerante a fallas.
En el modelo de actor, el bloque de construcción básico es un actor, a veces denominado receptor en Hollywood, que es una unidad de cálculo independiente que se comunica con otros actores al intercambiar mensajes. Cada actor tiene su propio estado y comportamiento, y solo puede comunicarse con otros actores enviando mensajes. Este paradigma que pasa el mensaje permite un sistema altamente descentralizado y tolerante a fallas, ya que los actores pueden continuar operando de forma independiente, incluso si otros actores fallan o no están disponibles.
Los actores pueden organizarse en jerarquías, con actores de nivel superior supervisando y coordinando a los actores de nivel inferior. Esto permite la creación de sistemas complejos que pueden manejar fallas y errores de una manera elegante y predecible.
Al usar el modelo de actor en su aplicación, puede crear sistemas altamente escalables y tolerantes a fallas que puedan manejar una gran cantidad de usuarios concurrentes e interacciones complejas.
Entrega de mensajes garantizado en la falla del actor (mecanismo de amortiguación)
Fuego y olvido o solicitud y mensajes de respuesta, o ambos
DRPC de alto rendimiento como capa de transporte
Proto buffers optimizados sin reflexión
Ligero y altamente personalizable
Soporte de clúster para escribir actores distribuidos de autodchoinsing
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 requiere Golang versión
1.21
Le recomendamos que comience escribiendo algunos ejemplos que se ejecutan localmente. Ejecutar localmente es un poco más simple ya que el compilador puede calcular los tipos utilizados. Cuando se ejecute de forma remota, necesitará proporcionar definiciones de ProtoBuffer para el compilador.
Pasemos por un mensaje de Hello World. El ejemplo completo está disponible en la carpeta Hello World. Comencemos en Main:
Motor, err: = actor.newengine (actor.newengineconfig ())
Esto crea un nuevo motor. El motor es el núcleo de Hollywood. Es responsable de generar actores, enviar mensajes y manejar el ciclo de vida de los actores. Si Hollywood no puede crear el motor, devolverá un error. Para el desarrollo, no debería usar para pasar ninguna opción al motor para que pueda pasar nulo. Veremos las opciones más tarde.
A continuación, tendremos que crear un actor. Algunas veces se denominan Receivers
después de la interfaz que deben implementar. Creemos un nuevo actor que imprima un mensaje cuando reciba un mensaje.
PID: = Engine.spawn (NewHelloer, "Hola")
Esto hará que el motor genere un actor con la identificación "Hola". El actor será creado por la función proporcionada newHelloer
. Los ID deben ser únicos. Devolverá un puntero a un PID. Un PID es un identificador de proceso. Es un identificador único para el actor. La mayoría de las veces usará el PID para enviar mensajes al actor. Con los sistemas remotos, usará la identificación para enviar mensajes, pero en los sistemas locales usará principalmente el PID.
Veamos la función newHelloer
y el actor que regresa.
Tipo Helloer Struct {} func NewHelloer () actor.Receiver {return & Helloer {} }
Bastante simple. La función newHelloer
devuelve un nuevo actor. El actor es una estructura que implementa el actor. Veamos el método Receive
.
Tipo de mensaje struct {} func (h *helloer) recibe (ctx *actor.context) {switch msg: = ctx.message (). (type) {case actor.initialized: fmt.println ("Helloer ha inicializado") actor.started: fmt.println ("Helloer ha comenzado") Caso actor.stopped: fmt.println ("Helloer se ha detenido") Caso *Mensaje: fmt.println ("Hello World", msg.data) } }
Puede ver que definimos una estructura de mensaje. Este es el mensaje que enviaremos al actor más tarde. El método de recepción también maneja algunos otros mensajes. El motor envía estos mensajes de ciclo de vida al actor, los usará para inicializar su actor.
El motor pasa un Receive
. Este contexto contiene el mensaje, el PID del remitente y algunas otras dependencias que puede usar.
Ahora, enviemos un mensaje al actor. Enviaremos un message
, pero puede enviar cualquier tipo de mensaje que desee. El único requisito es que el actor debe poder manejar el mensaje. Para que los mensajes puedan cruzar el cable, deben ser serializables. Para que ProtoBuf pueda serializar el mensaje, debe ser un puntero. Los mensajes locales pueden ser de cualquier tipo.
Finalmente, enviemos un mensaje al actor.
Engine.send (pid, "¡Hola mundo!")
Esto enviará un mensaje al actor. Hollywood enrutará el mensaje al actor correcto. El actor imprimirá un mensaje en la consola.
La carpeta de ejemplos es el mejor lugar para aprender y explorar más a Hollywood.
Cuando genere un actor, necesitará proporcionar una función que devuelva un nuevo actor. Como el actor es engendro, hay algunas opciones sintonizables que puede proporcionar.
E.Spawn (Newfoo, "Myactorname")
A veces querrás pasar argumentos al constructor de actores. Esto se puede hacer usando un cierre. Hay un ejemplo de esto en el ejemplo de solicitud. Veamos el código.
El constructor predeterminado se verá así:
FUNC NewNamerSponder () actor.Receiver {return & namerSponder {nombre: "noname"} }
Para construir un nuevo actor con un nombre, puede hacer lo siguiente:
FUNC NewCustomNePonder (name String) actor.producer {return func () actor.receiver {return & namerSponder {name} } }
Luego puede generar al actor con el siguiente código:
PID: = Engine.spawn (NewCustomNameSponder ("Anthony"), "Respondedor de nombre")
E.Spawn (Newfoo, "Myactorname", actor.withmaxrestarts (4), actor.withinboxsize (1024 * 2), actor.withid ("bar"), ) )
Las opciones deben ser bastante explicativas. Puede establecer el número máximo de reinicios, que le indica al motor cuántas veces se debe reiniciar el actor determinado en caso de pánico, el tamaño de la bandeja de entrada, que establece un límite de cómo y los mensajes sin procesar que la bandeja de entrada puede contener antes de comenzar bloquear.
Los actores sin estado pueden ser generados como una función, porque es rápido y simple.
E.SpawnFunc (func (c *actor.context) {switch msg: = c.message (). (type) {case actor.started: fmt.println ("iniciado") _ = msg } }, "foo")
Los actores pueden comunicarse entre sí a través de la red con el paquete remoto. Esto funciona igual que los actores locales pero "sobre el cable". Hollywood apoya la serialización con ProtoBuf.
Remote.new () toma una dirección de escucha y una estructura remota. Config.
Instanciará un nuevo control remoto con el siguiente código:
tlsconfig: = tlsconfig: & tls.config {certificados: [] tls.certificate {cert}, } config: = remoto.newconfig (). withtls (tlsconfig) remoto: = remoto.new ("0.0.0.0:2222", config) motor, err: = actor.newengine (actor.newengineconfig (). WithRemote (remoto) )
Mire los ejemplos de actores remotos y el cliente y servidor de chat para obtener más información.
En un sistema de producción, eventualmente saldrá mal. Los actores se estrellarán, las máquinas fallarán, los mensajes terminarán en la cola de los lámparas. Puede construir un software que pueda manejar estos eventos de una manera elegante y predecible utilizando la transmisión del evento.
EventStream es una poderosa abstracción que le permite construir sistemas flexibles y conectables sin dependencias.
Suscribir a cualquier actor a una lista diversa de eventos del sistema
Transmitir sus eventos personalizados a todos los suscriptores
Tenga en cuenta que los eventos que no son manejados por ningún actor serán abandonados. Debe tener un actor suscrito a la transmisión del evento para recibir eventos. Como mínimo, querrá manejar DeadLetterEvent
. Si Hollywood no le entrega un mensaje a un actor, enviará un DeadLetterEvent
a la transmisión del evento.
Cualquier evento que cumpla con la interfaz actor.LogEvent
se registrará en el registrador predeterminado, con el nivel de gravedad, el mensaje y los atributos del evento establecido por el método actor.LogEvent
log()
.
actor.ActorInitializedEvent
, un actor ha sido inicializado pero no procesó su actor.Started message
actor.ActorStartedEvent
, un actor ha comenzado
actor.ActorStoppedEvent
, un actor se detuvo
actor.DeadLetterEvent
, no se entregó un mensaje a un actor
actor.ActorRestartedEvent
, un actor se ha reiniciado después de un accidente/pánico.
actor.RemoteUnreachableEvent
, enviando un mensaje sobre el cable a un control remoto que no sea accesible.
cluster.MemberJoinEvent
, un nuevo miembro se une al clúster
cluster.MemberLeaveEvent
, un nuevo miembro dejó el clúster
cluster.ActivationEvent
, un nuevo actor se activa en el clúster
cluster.DeactivationEvent
, un actor está desactivado en el clúster
Hay un ejemplo de monitoreo EventStream que le muestra cómo usar la transmisión del evento. Cuenta con dos actores, uno es inestable y se bloqueará cada segundo. El otro actor está suscrito a la transmisión del evento y mantiene algunos contadores para diferentes eventos, como bloqueos, etc.
La aplicación se ejecutará durante unos segundos y el veneno al actor inestable. Luego consultará el monitor con una solicitud. A medida que los actores flotan dentro del motor, esta es la forma en que interactúas con ellos. Main luego imprimirá el resultado de la consulta y la aplicación saldrá.
Estamos utilizando el patrón de opción de función. Todas las opciones de función están en el paquete de actores e inician su nombre con "EngineOpt". Actualmente, la única opción es proporcionar un control remoto. Esto se hace por
r: = remoto.new (remoto.config {ochearAddr: addr}) motor, err: = actor.newengine (actor.engineoptremote (r)))
ADDR es una cadena con el formato "Host: Port".
Puede agregar middleware personalizado a sus receptores. Esto puede ser útil para almacenar métricas, guardar y actor.Stopped
datos para sus receptores en actor.Started
.
Para ver ejemplos sobre cómo implementar el middleware personalizado, consulte la carpeta de middleware en los ejemplos
Hollywood tiene un registro incorporado. Utilizará el registrador predeterminado del paquete log/slog
. Puede configurar el registrador en su gusto configurando el registrador predeterminado usando slog.SetDefaultLogger()
. Esto le permitirá personalizar el nivel de registro, el formato y la salida. Consulte el paquete slog
para obtener más información.
Tenga en cuenta que algunos eventos podrían registrarse en el registrador predeterminado, como DeadLetterEvent
y ActorStartedEvent
a medida que estos eventos cumplen la interfaz actor.LogEvent
. Consulte la sección EventsTream arriba para obtener más información.
make test
Únase a nuestra comunidad de Discord con más de 2000 miembros para preguntas y una buena conversación.
Este proyecto es utilizado actualmente en producción por las siguientes organizaciones/proyectos:
Sensora IoT
Hollywood tiene licencia bajo la licencia MIT.