Hace poco implemente una funcionalidad para un webhook, el cual recibía una petición de actualización de información por parte de un proveedor. Todo funcionaba correctamente de mi lado, pero al realizar el envío de la información actualizada que solicitaba el proveedor, ésta fallaba debido a que yo realizaba el envío antes de darle una respuesta a la petición, por lo que el deber ser era: recibir la petición , responder la petición y por último realizar el envío de la información solicitada a su servicio web.
Esto en Java me hubiera resultado muy sencillo con el uso de Threads, pero en PHP jamás había hecho algo similar, investigando un poco del cómo hacerlo en Laravel me encontré con las Queue, lo cual resultó bastante sencillo, pero para que funcione adecuadamente se deben realizar ciertas configuraciones adicionales, las cuales voy a explicar en este post.
Configurar driver para las Queue
Para que las Queue funcionen adecuadamente es necesario usar uno de los drivers existentes:
- Database
- Redis
- Beanstalkd
El driver que utilice fue el de database, para configurarlo es necesario editar el archivo environment (.env) y cambiar la propiedad QUEUE_CONNECTION, por default viene como sync, debemos cambiarla por database. Una vez hecho lo anterior debemos crear una tabla en la base de datos, para lo cual ya existe un comando que nos genera la migración de dicha tabla, el comando es php artisan queue: table, al ejecutar el comando nos va a crear el archivo de migración, para que se cree la tabla en la base de datos basta con ejecutar el comando php artisan migrate, el inconveniente es que ejecutará todas la migraciones existentes y esto puede borrar información, lo recomendable es ejecutar únicamente el archivo que acabamos de crear, para ello debemos agregar el parámetro path al comando anterior, y le debemos pasar la ruta del archivo a ejecutar.
php artisan migrate --path database/migrations/2021_10_18_175402_create_jobs_table.php
Ya que tenemos esta configuración, podemos comenzar a crear nuestras clases Queue en PHP, las cuales deben implementar la interfaz ShouldQueue , también es necesario heredar las clases Dispatchable, Queueable, InteractsWithQueue, SerializesModels y por último debemos implementar el método handle, el cual contiene el código a ejecutar. Si requerimos parámetros para lo que vamos a ejecutar, estos se pasan a través del constructor.
A continuación un ejemplo de nuestra Clase:
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
class MyQueue implements ShouldQueue
{
use Dispatchable, Queueable, InteractsWithQueue, SerializesModels;
protected $param;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(string $param)
{
$this->param = $param;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
return 'done';
}
}
Por último, para agregar nuestro proceso en la cola, basta con ejecutar el siguiente código en donde se crea conveniente:
$job = (new MyQueue('1234'))
->delay(Carbon::now()->addSeconds(15));
$this->dispatch($job);
*Nota: el ejemplo anterior retrasa la ejecución por 15 segundos, esto ya dependerá del tiempo que requerimos, pueden ser minutos, horas, etc;
Con todo lo anterior ya se realiza el registro de nuestro procesos a ejecutar en la BD, pero estos no se van a ejecutar, para que se ejecuten es necesario ejecutar un comando en la terminal para que Laravel este leyendo los procesos registrados y los ejecute a su debido tiempo. Si estamos en local basta con ejecutar el siguiente comando para poder probar que todo funcione:
php artisan queue:work
Una vez ejecutado este comando podemos empezar a programar procesos y ver como se van ejecutando, pero si cerramos la terminal se dejarán de ejecutar. Hay varias formas de solucionar este inconveniente, el más sencillo es ejecutar el comando en segundo plano, con esto al cerrar la terminal seguirá leyendo y ejecutando los procesos:
nohup php artisan queue:work --daemon &
Debemos tener en cuenta que si por X razón se llega a reiniciar el servidor, debemos volver a ejecutar el comando anterior. La otra opción es usar supervisor el cual se encargará de iniciar o reiniciar nuestro proceso en caso de que este se detenga.
Instalando supervisor
Para instalarlo debemos ejecutar el siguiente comando:
sudo apt-get install supervisor
Una vez instalado debemos acceder a su carpeta de configuración ubicada en /etc/supervisor/conf.d, una vez dentro debemos crear el archivo de configuración para nuestro proceso y abrirlo con un editor como vim:
sudo vi archivo.conf
Una vez en el editor agregamos la configuración necesaria:
[program:send-menu] #Nombre del programa
process_name=%(program_name)s_%(process_num)02d #Nombre del proceso
command=php /var/www/html/artisan queue:work #Comando a ejecutar
autostart=true
autorestart=true
user=userename #Usuario
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/html/storage/logs/supervisord.log #Ubicación y nombre del archivo log
Ahora debemos decirle a supervisor que vuelva a leer y actualice las configuraciones con los siguientes comandos:
sudo supervisorctl reread
sudo supervisorctl update
También se pueden utilizar los siguientes comandos:
sudo supervisorctl reload
sudo service supervisor restart