O arquivo de configuração clássico para entradas de tarefas cron no sistema operacional Linux é o seguinte:
Os cinco primeiros parâmetros indicam o tempo de execução dessa tarefa e o sexto - o próprio comando, que deve ser executado. Os parâmetros de tempo são: minutos, horas, dias, meses e dia da semana. Além disso, todos os números devem ser representados como números inteiros ou na forma de sintaxe especial .Como resultado, o intervalo mínimo possível para iniciar um comando é uma vez por minuto.Para muitas tarefas, a execução de comandos é necessária com muito mais frequência, por exemplo, a cada 10 segundos. Para algumas tarefas de automação de processos de negócios, o atraso máximo permitido geralmente não passa de 1 a 1,5 segundos.Obviamente, o cron clássico não é adequado para isso - ele precisa ser aprimorado.Abaixo está uma implementação passo a passo da criação de funcionalidades adicionais (em PHP) para o cron clássico no Linux usando proteção adicional contra processos de reinicialização.Configuração de tarefas e configuração de cron
Por exemplo, usaremos a seguinte tarefa:- No front end, o usuário pode iniciar a execução de alguma tarefa complexa clicando no botão "Executar";
- O back-end após escrever uma nova linha no banco de dados informa o usuário da confirmação;
- Através do cron, “rastrearemos” essas novas tarefas e as concluiremos o mais rápido possível, para que o usuário não obtenha o resultado em um minuto, mas imediatamente *.
* Se você usar o início de comandos, apenas por minuto, a tarefa será iniciada quando os segundos atingirem o zero mais próximo (o início de um novo minuto). Portanto, na forma clássica, o usuário precisará aguardar a execução de 0 a 59 segundos.Portanto, o cron será configurado em sua forma máxima, ou seja, uma vez por minuto:* * * * * /usr/bin/php -q /run.php > /dev/null 2>&1
Ciclo único
Inicialmente, você deve decidir com que frequência solicitaremos novas tarefas no banco de dados - dependendo disso, o número de ciclos e o sono lógico (função sleep
) serão alterados.Noexemplo atual, uma etapa de 10 segundos é usada. Portanto, o número de ciclos é 60/10 = 6. Total, o código geral é o seguinte:for ($cycle = 1; $cycle <= 6; $cycle++) {
$all_tasks = get_all_tasks();
if ($all_tasks) {
foreach($all_tasks as $one_task) {
solve_one_task($one_task);
}
}
sleep(10);
}
Esclarecimento : neste exemplo, é usada uma etapa de 10 segundos, que pode fornecer um intervalo mínimo de execução de script uma vez a cada 10 segundos. Assim, para execução mais frequente, você deve alterar o número de ciclos e o "tempo de suspensão".Evitando a execução repetida de tarefas
Na forma apresentada, há uma incerteza, a saber, a execução repetida da tarefa, se ela já foi iniciada. Isso se torna especialmente verdadeiro se a tarefa for "difícil" e exigir alguns segundos para implementá-la.Isso cria um problema de reexecução:- A função
solve_one_task()
já está em execução, mas ainda não concluiu seu trabalho; - Portanto, no banco de dados a tarefa ainda está marcada como não realizada;
- O próximo ciclo receberá essa tarefa novamente e executará a função
solve_one_task()
novamente, com a mesma tarefa.
Obviamente, isso pode ser resolvido, por exemplo, alterando algum status no banco de dados para esta tarefa.Mas não carregaremos o banco de dados: com base nos meus testes, o MYSQL pode aceitar a solicitação, mas não processá-la imediatamente. Uma diferença de até 0,5 segundos pode levar à execução repetida - o que categoricamente não é adequado.Também neste caso, estamos falando apenas sobre o status das tarefas, portanto, é melhor usar o sistema de arquivos do servidor.O modelo básico de verificação é construído usando flock
- uma função que define e desbloqueia um arquivo.Na execução do PHP, a função pode ser representada da seguinte maneira:$lock_file_abs = 'file';
$fp = fopen($lock_file_abs,"w+");
if (flock($fp, LOCK_EX | LOCK_NB)) {
solve_one_task($one_task);
flock($fp, LOCK_UN);
}
else {
}
fclose($fp);
unlink($lock_file_abs);
Resultado
A visão geral de todo o ciclo é a seguinte:for ($cycle = 1; $cycle <= 6; $cycle++) {
$all_tasks = get_all_tasks();
if ($all_tasks) {
foreach($all_tasks as $one_task) {
$lock_file_abs = __DIR__.'/locks/run_'.$one_task['id'];
$fp = fopen($lock_file_abs,"w+");
if (flock($fp, LOCK_EX | LOCK_NB)) {
solve_one_task($one_task);
flock($fp, LOCK_UN);
}
else {
}
fclose($fp);
unlink($lock_file_abs);
}
}
sleep(10);
}
Portanto, esse algoritmo permite iniciar uma execução cíclica do processamento de tarefas e não se preocupa que a tarefa seja processada mais de uma vez.E agora basta executar esse código através do cron a cada minuto e, por sua vez, executará ciclos menores já dentro de si.