Hollywood é uma construção de motor de ator ultra rápida para aplicações de velocidade e baixa latência. Pense em servidores de jogo, corretores de publicidade, mecanismos de negociação, etc ... Ele pode lidar com 10 milhões de mensagens em menos de 1 segundo .
O modelo de ator é um modelo computacional usado para construir sistemas altamente simultâneos e distribuídos. Foi introduzido por Carl Hewitt em 1973 como uma maneira de lidar com sistemas complexos de uma maneira mais escalável e tolerante a falhas.
No modelo de ator, o bloco básico de construção é um ator, às vezes chamado de receptor em Hollywood, que é uma unidade independente de computação que se comunica com outros atores trocando mensagens. Cada ator tem seu próprio estado e comportamento e só pode se comunicar com outros atores enviando mensagens. Esse paradigma que passa de mensagem permite um sistema altamente descentralizado e tolerante a falhas, pois os atores podem continuar operando de forma independente, mesmo que outros atores falhem ou se tornem indisponíveis.
Os atores podem ser organizados em hierarquias, com atores de nível superior supervisionando e coordenando atores de nível inferior. Isso permite a criação de sistemas complexos que podem lidar com falhas e erros de maneira graciosa e previsível.
Ao usar o modelo de ator em seu aplicativo, você pode criar sistemas altamente escaláveis e tolerantes a falhas que podem lidar com um grande número de usuários simultâneos e interações complexas.
Entrega de mensagem garantida na falha do ator (mecanismo de buffer)
Fire e esqueça ou solicita e mensagens de resposta, ou ambos
DRPC de alto desempenho como camada de transporte
Buffers proto otimizados sem reflexão
Leve e altamente personalizável
Suporte ao cluster para escrever atores autodinhados distribuídos
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 requer Golang versão
1.21
Recomendamos que você comece escrevendo alguns exemplos que são executados localmente. A execução localmente é um pouco mais simples, pois o compilador é capaz de descobrir os tipos usados. Ao executar remotamente, você precisará fornecer definições de protobuffer para o compilador.
Vamos passar por uma mensagem do Hello World. O exemplo completo está disponível na pasta Hello World. Vamos começar em Main:
motor, err: = ator.newengine (ator.newengineConfig ())
Isso cria um novo motor. O motor é o núcleo de Hollywood. É responsável pelos atores de desova, enviando mensagens e lidando com o ciclo de vida dos atores. Se Hollywood não criar o mecanismo, ele retornará um erro. Para o desenvolvimento, você não deve usar para passar nenhuma opção para o motor, para que você possa passar por nulo. Veremos as opções mais tarde.
Em seguida, precisamos criar um ator. Estas são algumas vezes chamadas de Receivers após a interface que eles devem implementar. Vamos criar um novo ator que imprimirá uma mensagem quando receber uma mensagem.
PID: = Engine.spawn (Newhelloer, "Hello")
Isso fará com que o motor gera um ator com o ID "Olá". O ator será criado pela função fornecida newHelloer . Os IDs devem ser únicos. Ele retornará um ponteiro para um PID. Um PID é um identificador de processo. É um identificador único para o ator. Na maioria das vezes, você usará o PID para enviar mensagens ao ator. Contra sistemas remotos, você usará o ID para enviar mensagens, mas nos sistemas locais, você usará principalmente o PID.
Vejamos a função newHelloer e o ator que ele retorna.
Tipo helloer struct {} func newHelloer () ator.receiver {return & helloer {}
} Simples o suficiente. A função newHelloer retorna um novo ator. O ator é uma estrutura que implementa o ator. Vamos olhar para o método Receive .
Digite a estrutura da mensagem {} func (h *helloer) recebe (ctx *ator.context) {switch msg: = ctx.message (). (type) {case ator.initialized: fmt.println ("helloer foi inicializado") caso ator.started: fmt.println ("helloer começou") caso ator.stopped: fmt.println ("helloer parou") caso *mensagem: fmt.println ("hello world", msg.data)
}
}Você pode ver que definimos uma estrutura de mensagem. Esta é a mensagem que enviaremos ao ator mais tarde. O método de recebimento também lida com algumas outras mensagens. Essas mensagens do ciclo de vida são enviadas pelo mecanismo ao ator, você as usará para inicializar seu ator
O motor passa um ator.Context para o método Receive . Este contexto contém a mensagem, o PID do remetente e algumas outras dependências que você pode usar.
Agora, vamos enviar uma mensagem ao ator. Enviaremos uma message , mas você pode enviar qualquer tipo de mensagem desejada. O único requisito é que o ator seja capaz de lidar com a mensagem. Para que as mensagens possam atravessar o fio, elas devem ser serializáveis. Para que o Protobuf seja capaz de serializar a mensagem, ela deve ser um ponteiro. As mensagens locais podem ser de qualquer tipo.
Finalmente, vamos enviar uma mensagem ao ator.
Engine.send (PID, "Hello World!")
Isso enviará uma mensagem ao ator. Hollywood irá rotear a mensagem para o ator correto. O ator imprimirá uma mensagem para o console.
A pasta Exemplos é o melhor lugar para aprender e explorar ainda mais Hollywood.
Quando você gera um ator, precisará fornecer uma função que retorne um novo ator. Como o ator é gerado, existem algumas opções ajustáveis que você pode oferecer.
e.spawn (newfoo, "myactorname")
Às vezes, você vai querer passar argumentos para o construtor do ator. Isso pode ser feito usando um fechamento. Há um exemplo disso no exemplo de solicitação. Vejamos o código.
O construtor padrão será algo assim:
func newNineResponder () ator.receiver {return & namerponder {name: "noname"}
}Para construir um novo ator com um nome, você pode fazer o seguinte:
func newcustomnamesponder (name string) ator.producer {return func () ator.receiver {return & namerponder {name}
}
}Você pode gerar o ator com o seguinte código:
PID: = Engine.spawn (NewCustomNineRponder ("Anthony"), "Nome-Responder") e.spawn (newfoo, "myactorname", ator.withmaxrestarts (4), ator.withinboxSize (1024 * 2), ator.withid ("bar"),
)
)As opções devem ser bastante auto -explicativas. Você pode definir o número máximo de reinicializações, que informa ao mecanismo quantas vezes o ator determinado deve ser reiniciado em caso de pânico, o tamanho da caixa de entrada, que define um limite de como e mensagens não processadas que a caixa de entrada pode manter antes de iniciar para bloquear.
Os atores sem estado podem ser gerados como uma função, porque é rápido e simples.
e.spawnfunc (func (c *ator.context) {switch msg: = c.message (). (type) {case ator.started: fmt.println ("iniciado") _ = msg
}
}, "foo")Os atores podem se comunicar sobre a rede com o pacote remoto. Isso funciona da mesma forma que os atores locais, mas "sobre o fio". Hollywood suporta a serialização com o Protobuf.
remote.new () leva um endereço de escuta e um remote.config struct.
Você instanciará um novo controle remoto com o seguinte código:
tlsconfig: = tlsconfig: & tls.config {certificados: [] tls.certificate {cert},
} config: = remote.newconfig (). withtls (tlsconfig) remoto: = remote.new ("0.0.0.0:2222", config) mecanismo, err: = ator.newengine (ator.newengineConfig (). )Veja os exemplos de ator remoto e o cliente e o servidor de bate -papo para obter mais informações.
Em uma coisa do sistema de produção, acabará por dar errado. Os atores travarão, as máquinas falharão, as mensagens acabarão na fila de Deadletter. Você pode criar software que pode lidar com esses eventos de maneira graciosa e previsível usando o fluxo de eventos.
O EventStream é uma abstração poderosa que permite criar sistemas flexíveis e flashes sem dependências.
Inscreva -se qualquer ator em uma lista de eventos do sistema
Transmitir seus eventos personalizados para todos os assinantes
Observe que os eventos que não são tratados por nenhum ator serão descartados. Você deve ter um ator inscrito no fluxo de eventos para receber eventos. No mínimo, você deve lidar com DeadLetterEvent . Se Hollywood não entregar uma mensagem a um ator, ele enviará um DeadLetterEvent para o fluxo de eventos.
Qualquer evento que atenda à interface actor.LogEvent será registrado no logger padrão, com o nível de gravidade, a mensagem e os atributos do evento definido pelo método actor.LogEvent log() .
actor.ActorInitializedEvent , um ator foi inicializado, mas não processou seu actor.Started message
actor.ActorStartedEvent , um ator começou
actor.ActorStoppedEvent , um ator parou
actor.DeadLetterEvent , uma mensagem não foi entregue a um ator
actor.ActorRestartedEvent , um ator reiniciou após um acidente/pânico.
actor.RemoteUnreachableEvent , enviando uma mensagem sobre o fio para um controle remoto que não é acessível.
cluster.MemberJoinEvent , um novo membro se junta ao cluster
cluster.MemberLeaveEvent , um novo membro deixou o cluster
cluster.ActivationEvent , um novo ator é ativado no cluster
cluster.DeactivationEvent , um ator é desativado no cluster
Há um exemplo de monitoramento do EventStream que mostra como usar o fluxo de eventos. Possui dois atores, um é instável e travará a cada segundo. O outro ator está inscrito no fluxo de eventos e mantém alguns contadores para diferentes eventos, como falhas, etc.
O aplicativo será executado por alguns segundos e o veneno é o ator instável. Em seguida, consultará o monitor com uma solicitação. Como os atores estão flutuando dentro do motor, é assim que você interage com eles. O principal imprimirá o resultado da consulta e o aplicativo sairá.
Estamos usando o padrão de opção de função. Todas as opções de função estão no pacote de atores e iniciam seu nome com "Enginepts". Atualmente, a única opção é fornecer um controle remoto. Isso é feito por
R: = Remote.New (Remote.config {listeraddr: addr}) mecanismo, err: = ator.newEngine (ator.engineoptremote (r))Addr é uma string com o formato "Host: Port".
Você pode adicionar middleware personalizado aos seus receptores. Isso pode ser útil para armazenar métricas, economizar e carregar dados para seus receptores no actor.Started e actor.Stopped .
Para exemplos de como implementar middleware personalizado, consulte a pasta de middleware nos exemplos
Hollywood tem alguns registros embutidos. Ele usará o logger padrão do pacote log/slog . Você pode configurar o logger para o seu gosto, configurando o logger padrão usando slog.SetDefaultLogger() . Isso permitirá que você personalize o nível de log, formato e saída. Consulte o pacote slog para obter mais informações.
Observe que alguns eventos podem ser registrados no logger padrão, como DeadLetterEvent e ActorStartedEvent pois esses eventos atendem à interface actor.LogEvent . Veja a seção EventStream acima para obter mais informações.
make test
Junte -se à nossa comunidade Discord com mais de 2000 membros para obter perguntas e um bom bate -papo.
Atualmente, este projeto é usado na produção pelas seguintes organizações/projetos:
Sensora IoT
Hollywood é licenciado sob a licença do MIT.