Básico de Active Jobs — Ruby on Rails Guides
Básico de Active Jobs
Este guia oferece a você tudo o que você precisa para começar a criar, enfileirar e executar jobs em background
Como enfileirar jobs
como executar jobs em segundo plano
Como enviar emails de sua aplicação de maneira assíncrona
O que é Active Job?
Crie o Job
Enfileirar o Job
Iniciando o Backend
Tipos suportados por argumentos
Reexecução ou Descarte de jobs falhos
1 O que é Active Job?
O Active Job é um framework para declarar jobs e fazê-los executar em uma variedade de backends de fila. Estes jobs podem ser qualquer coisa, de limpezas programadas regularmente, a cobranças de despesas, a envio de emails. Qualquer coisa que possa ser cortada em pequenas unidades de trabalho e executadas paralelamente, sério.
2 O Propósito do Active Job
O ponto principal é garantir que todas as aplicações Rails terão uma infraestrutura de jobs no lugar. Nós podemos então ter features de frameworks e outras gems construídas em cima dela, sem ter que nos preocupar com diferenças de API entre vários executadores de job como Delayed Job e Resque. Dessa forma, escolher o seu backend de enfileiramento se torna mais uma preocupação operacional. E você poderá alternar entre eles sem ter que reescrever os seus jobs.
Rails por padrão vem com uma implementação de fila assíncrona que executa jobs com uma pool de threads no processo. Jobs serão executados da maneira assíncrona, mas quaisquer jobs na fila serão derrubados ao reinicializar.
3 Criando um Job
Esta seção fornecerá um guia passo a passo para criar um job e enfileirá-lo.
3.1 Crie o Job
O Active Job fornece um gerador Rails para criar jobs. O seguinte criará um job em app/jobs (com um teste anexado em test/jobs):
Você também pode criar um job que será executado em uma fila específica:
Se você não quiser usar um gerador, você pode criar seu próprio arquivo dentro de app/jobs, apenas certifique-se de que herda de ApplicationJob.
Aqui está a aparência de um job:
Observe que você pode definir perform com quantos argumentos quiser.
3.2 Enfileirar o Job
Enfileirar um job assim:
# Enfileirar um _job_ para ser executado assim que o sistema de enfileiramento estiver
# livre.
# Enfileirar um _job_ para ser executado assim que o sistema de enfileiramento estiver # livre. GuestsCleanupJob.perform_later guest
# Enfileirar um _job_ para ser executado amanhã ao meio-dia.
# Enfileirar um _job_ para ser executado amanhã ao meio-dia. GuestsCleanupJob.set(wait_until: Date.tomorrow.noon).perform_later(guest)
# Enfileirar um _job_ para ser executado em 1 semana a partir de agora.
# Enfileirar um _job_ para ser executado em 1 semana a partir de agora. GuestsCleanupJob.set(wait: 1.week).perform_later(guest)
# `perform_now` e `perform_later` irão chamar `perform` por baixo dos panos, então
# você pode passar quantos argumentos forem definidos no último.
# `perform_now` e `perform_later` irão chamar `perform` por baixo dos panos, então # você pode passar quantos argumentos forem definidos no último. GuestsCleanupJob.perform_later(guest1, guest2, filter: 'some_filter')
4 Execução de Job
Para enfileirar e executar jobs em produção você precisa configurar um backend de filas, ou seja, você precisa decidir por uma lib de enfileiramento de terceiros que o Rails deve usar, o Rails em si fornece apenas um sistema de filas em processo, que só mantém os jobs em memória(RAM). Se o processo quebra ou a máquina é reiniciada, todos os jobs pendentes serão perdidos com o backend assíncrono padrão. Isso pode ser bom para aplicações menores ou jobs não críticos, mas a maioria das aplicações em produção precisará escolher um backend de persistência.
O Active Job tem adaptadores built-in para múltiplos backends de fila (Sidekiq Resque, Delayed Job e outros). Para obter uma lista atualizada dos adaptadores, consulte a documentação da API para ActiveJob::QueueAdapters.
4.2 Configurando o Backend
Você pode definir facilmente o backend de fila:
Você também pode configurar seu backend por um job base:
# Agora o seu job vai usar `resque` como gerenciados de fials sobrescrevendo o
# que estava configurado em `config.active_job.queue_adapter`.
class GuestsCleanupJob < ApplicationJob self.queue_adapter = :resque # ... end # Agora o seu job vai usar `resque` como gerenciados de fials sobrescrevendo o # que estava configurado em `config.active_job.queue_adapter`.
4.3 Iniciando o Backend
Uma vez que os jobs são executados em paralelo à sua aplicação Rails, a maioria das bibliotecas de filas exigem que você inicie um serviço de enfileiramento específico (além de iniciar sua aplicação Rails) para que o processamento do job funcione. Consulte a documentação da biblioteca para obter instruções sobre como iniciar o backend da fila.
Aqui está uma lista não abrangente de documentação:
A maioria dos adapters suportam múltiplas filas. Com o Active Job você pode agendar o job para executar em uma fila específica:
Você pode prefixar o nome da fila para todos os jobs usando config.active_job.queue_name_prefix in application.rb:
# Agora seu job irá executar na fila production_low_priority no seu
# ambiente de produção e na staging_low_priority
# no seu ambiente de desenvolvimento
# app/jobs/guests_cleanup_job.rb class GuestsCleanupJob < ApplicationJob queue_as :low_priority # ... end # Agora seu job irá executar na fila production_low_priority no seu # ambiente de produção e na staging_low_priority # no seu ambiente de desenvolvimento
Você também pode configurar o prefixo para cada job.
# Agora a fila do seu job não terá um prefixo, sobrescrevendo o que
# foi configurado em `config.active_job.queue_name_prefix`.
class GuestsCleanupJob < ApplicationJob queue_as :low_priority self.queue_name_prefix = nil # ... end # Agora a fila do seu job não terá um prefixo, sobrescrevendo o que # foi configurado em `config.active_job.queue_name_prefix`.
O prefixo delimitador padrão de nome de fila é '_'. Isso pode ser alterado configurando o config.active_job.queue_name_delimiter no application.rb:
# Agora seu job irá executar na fila production.low_priority no seu
# ambiente de produção e na staging.low_priority
# no seu ambiente de staging
# app/jobs/guests_cleanup_job.rb class GuestsCleanupJob < ApplicationJob queue_as :low_priority # ... end # Agora seu job irá executar na fila production.low_priority no seu # ambiente de produção e na staging.low_priority # no seu ambiente de staging
Se você quiser mais controle em qual fila um job será executado, você pode passar uma opção :queue ao set:
Para controlar a fila a partir do nível do job, você pode passar um bloco para queue_as. O bloco será executado no contexto do job (o que te permite acessar self.arguments), e ele deve retornar o nome da fila:
# Processa o vídeo
class ProcessVideoJob < ApplicationJob queue_as do video = self.arguments.first if video.owner.premium? :premium_videojobs else :videojobs end end def perform(video) # Processa o vídeo end end
Tenha certeza de que o seu backend de fila "escuta" o nome da fila. Para alguns backends você precisará especificar as filas a serem "ouvidas".
O Active Job fornece Hooks para disparar lógica durante o ciclo de vida de um Job. Assim como em outros callbacks no Rails, você pode implementar callbacks como métodos comuns e usar um método macro de classe para registrá-los como callbacks:
Os métodos macro de classe podem também receber um bloco. Considere usar esse estilo, se o código dentro do bloco for tão pequeno que cabe numa única linha. Por exemplo, você pode enviar métricas para cada job enfileirado:
6.1 Callbacks disponíveis
Um dos jobs mais comuns em uma aplicação web moderna é enviar e-mails fora do ciclo de request-response, para que o usuário não tenha que esperar por ele. O Active Job está integrado com o Action Mailer, o que te permite enviar e-mails assincronamente:
Se você usar uma fila assíncrona de uma Rake task (por exemplo, para enviar um e-mail usando .deliver_later), geralmente não irá funcionar porque a Rake irá provavelmente terminar, fazendo com que o processo interno do thread pool seja removido, antes de qualquer ou todos os e-mails serem processados. Para evitar esse problema, use o .deliver_now ou execute uma fila persistente no ambiente de desenvolvimento.
Cada job usa o I18n.locale configurado quando o job é criado. Isso é útil se você enviar e-mails assincronamente:
UserMailer.welcome(@user).deliver_later # O e-mail será localizado para Esperanto.
I18n.locale = :eo UserMailer.welcome(@user).deliver_later # O e-mail será localizado para Esperanto.
9 Tipos suportados por argumentos
O ActiveJob suporta os seguintes tipos de argumentos por padrão:
O Active Job suporta o GlobalID como parâmetros. Isso possibilita passar objetos ativos do Active Record para o job ao invés do par classe/id, que você deve desserializar manualmente. Anteriormente, os jobs eram feitos dessa forma:
Agora são feitos dessa forma:
Isso funciona com qualquer classe que está mesclada em GlobalID::Identification que, por padrão, já está mesclada dentro das classes Active Record.
Você pode ampliar a lista de tipos de argumentos suportados. Só é preciso definir seu próprio serializer:
# Checa se um argumento pode ser serializado a partir desse serializer.
# Converte um objeto em um representante mais simples usando tipos de objetos suportados.
# O representante recomendado é uma Hash com uma key específica. As Keys podem ser somente de tipos básicos.
# Você deve invocar o `super` para adicionar o serializer customizado na hash.
# Converte o valor serializado em um objeto apropriado.
class MoneySerializer < ActiveJob::Serializers::ObjectSerializer # Checa se um argumento pode ser serializado a partir desse serializer. def serialize?(argument) argument.is_a? Money end # Converte um objeto em um representante mais simples usando tipos de objetos suportados. # O representante recomendado é uma Hash com uma key específica. As Keys podem ser somente de tipos básicos. # Você deve invocar o `super` para adicionar o serializer customizado na hash. def serialize(money) super( "amount" => money.amount, "currency" => money.currency ) end # Converte o valor serializado em um objeto apropriado. def deserialize(hash) Money.new(hash["amount"], hash["currency"]) end end
e adicionar o serializer na lista:
Os Active Jobs fornecem uma maneira de capturar exceções criadas durante a execução do job.
Se a exceção não for tratada dentro do job, então o job é reconhecido como "failed (falhado)"
10.1 Reexecução ou Descarte de jobs falhos
Um job que falhou não será executado novamente, exceto se configurado para fazer isso.
É possível também reexecutar ou descartar um job se uma exceção for gerada durante a execução. Por exemplo:
Para mais detalhes Veja a Documentação da API para ActiveJob::Exceções.
10.2 Desserialização
GlobalID permite serializar todos os objetos do Active Record passado para o #perform.
Se um registro for deletado após o job ser enfileirado mas antes do método #perform ser chamado, o Active Job vai lançar a exceção ActiveJob::DeserializationError.