You've already forked Php-Template
feat: implement queue system with consumer and message handling (#14)
All checks were successful
🧪✨ Tests Workflow / 🛡️ 🔒 License Check (push) Successful in 3m1s
🧪✨ Tests Workflow / 🧪 ✨ Database Migrations (push) Successful in 3m16s
🧪✨ Tests Workflow / 🛡️ 🔒 Library Audit (push) Successful in 3m13s
🧪✨ Tests Workflow / 📝 ✨ Code Lint (push) Successful in 3m5s
🧪✨ Tests Workflow / 🐙 🔍 Code Sniffer (push) Successful in 3m11s
🧪✨ Tests Workflow / 🧪 ✅ Unit Tests (push) Successful in 1m51s
All checks were successful
🧪✨ Tests Workflow / 🛡️ 🔒 License Check (push) Successful in 3m1s
🧪✨ Tests Workflow / 🧪 ✨ Database Migrations (push) Successful in 3m16s
🧪✨ Tests Workflow / 🛡️ 🔒 Library Audit (push) Successful in 3m13s
🧪✨ Tests Workflow / 📝 ✨ Code Lint (push) Successful in 3m5s
🧪✨ Tests Workflow / 🐙 🔍 Code Sniffer (push) Successful in 3m11s
🧪✨ Tests Workflow / 🧪 ✅ Unit Tests (push) Successful in 1m51s
Reviewed-on: #14 Co-authored-by: Ron Rise <ron@siteworxpro.com> Co-committed-by: Ron Rise <ron@siteworxpro.com>
This commit was merged in pull request #14.
This commit is contained in:
142
src/Async/Consumer.php
Normal file
142
src/Async/Consumer.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
declare(ticks=1);
|
||||
|
||||
namespace Siteworxpro\App\Async;
|
||||
|
||||
use Siteworxpro\App\Annotations\Async\HandlesMessage;
|
||||
use Siteworxpro\App\Async\Queues\Queue;
|
||||
use Siteworxpro\App\Services\Facades\Logger;
|
||||
|
||||
class Consumer
|
||||
{
|
||||
private static bool $shutDown = false;
|
||||
|
||||
private const array QUEUES = [
|
||||
Queues\DefaultQueue::class,
|
||||
];
|
||||
|
||||
private array $queues = [];
|
||||
|
||||
private array $handlers = [];
|
||||
|
||||
private const string HANDLER_NAMESPACE = 'Siteworxpro\\App\\Async\\Handlers\\';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
foreach (self::QUEUES as $queueClass) {
|
||||
$this->queues[] = new $queueClass();
|
||||
}
|
||||
|
||||
$this->registerHandlers();
|
||||
}
|
||||
|
||||
private function registerHandlers(): void
|
||||
{
|
||||
$recursiveIterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator(__DIR__ . '/Handlers/')
|
||||
);
|
||||
|
||||
foreach ($recursiveIterator as $file) {
|
||||
if ($file->isFile() && $file->getExtension() === 'php') {
|
||||
$relativePath = str_replace(__DIR__ . '/Handlers/', '', $file->getPathname());
|
||||
$className = self::HANDLER_NAMESPACE . str_replace('/', '\\', substr($relativePath, 0, -4));
|
||||
|
||||
|
||||
if (class_exists($className)) {
|
||||
$reflection = new \ReflectionClass($className);
|
||||
$attributes = $reflection->getAttributes(HandlesMessage::class);
|
||||
foreach ($attributes as $attribute) {
|
||||
$instance = $attribute->newInstance();
|
||||
$messageClass = $instance->getMessageClass();
|
||||
$this->handlers[$messageClass][] = $className;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $signal
|
||||
*/
|
||||
public static function handleSignal($signal): void
|
||||
{
|
||||
switch ($signal) {
|
||||
// Graceful
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
case SIGHUP:
|
||||
self::$shutDown = true;
|
||||
|
||||
break;
|
||||
|
||||
// Not Graceful
|
||||
case SIGKILL:
|
||||
exit(9);
|
||||
}
|
||||
}
|
||||
|
||||
private function shouldShutDown(): bool
|
||||
{
|
||||
return self::$shutDown;
|
||||
}
|
||||
|
||||
public function start(): void
|
||||
{
|
||||
if (!\function_exists('pcntl_signal')) {
|
||||
throw new \RuntimeException('The pcntl extension is required to handle signals.');
|
||||
}
|
||||
|
||||
Logger::info('Starting queue consumer...');
|
||||
|
||||
\pcntl_signal(SIGINT, [self::class, 'handleSignal']);
|
||||
\pcntl_signal(SIGTERM, [self::class, 'handleSignal']);
|
||||
\pcntl_signal(SIGHUP, [self::class, 'handleSignal']);
|
||||
|
||||
while (true) {
|
||||
if ($this->shouldShutDown()) {
|
||||
Logger::info('Shutting down queue consumer...');
|
||||
break;
|
||||
}
|
||||
|
||||
/** @var Queue $queue */
|
||||
foreach ($this->queues as $queue) {
|
||||
Logger::info('Listening to queue: ' . $queue->queueName());
|
||||
$message = $queue->pop();
|
||||
if ($message) {
|
||||
Logger::info('Processing message of type: ' . get_class($message));
|
||||
|
||||
$handlers = $this->getHandlerForMessage($message);
|
||||
|
||||
foreach ($handlers as $handler) {
|
||||
$handler($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
private function getHandlerForMessage($message): array
|
||||
{
|
||||
$callables = [];
|
||||
|
||||
$messageClass = get_class($message);
|
||||
if (isset($this->handlers[$messageClass])) {
|
||||
$handlerClasses = $this->handlers[$messageClass];
|
||||
|
||||
foreach ($handlerClasses as $handlerClass) {
|
||||
if (class_exists($handlerClass)) {
|
||||
$handlerInstance = new $handlerClass();
|
||||
|
||||
$callables[] = $handlerInstance;
|
||||
}
|
||||
}
|
||||
|
||||
return $callables;
|
||||
}
|
||||
|
||||
throw new \RuntimeException("No handler found for message class: $messageClass");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user