mirror of
https://github.com/RaspbianFrance/raspisms.git
synced 2025-04-22 09:26:27 +02:00
Add support for Redis in addition to System V queues
This commit is contained in:
parent
5c697b5240
commit
36c5d7ec0c
11 changed files with 407 additions and 93 deletions
39
models/Queue.php
Normal file
39
models/Queue.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of RaspiSMS.
|
||||
*
|
||||
* (c) Pierre-Lin Bonnemaison <plebwebsas@gmail.com>
|
||||
*
|
||||
* This source file is subject to the GPL-3.0 license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace models;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
interface Queue
|
||||
{
|
||||
/**
|
||||
* A FIFO Queue to exchange messages, the backend mechanism can be whatever we want, but the queue take message, tag for routing is optionnal
|
||||
* @param string $id : A unique identifier for the queue
|
||||
*/
|
||||
public function __construct($id);
|
||||
|
||||
/**
|
||||
* Add a message to the queue
|
||||
*
|
||||
* @param string $message : The message to add to the queue, must be a string, for complex data just use json
|
||||
* @param ?string $tag : A tag to associate to the message for routing purposes, if not set will add to general queue
|
||||
*/
|
||||
public function push($message, ?string $tag = null);
|
||||
|
||||
/**
|
||||
* Read the older message in the queue (non-blocking)
|
||||
* @param ?string $tag : A tag to associate to the message for routing purposes, if not set will read from general queue
|
||||
* @return ?string $message : The oldest message or null if no message found, can be anything
|
||||
*/
|
||||
public function read(?string $tag = null);
|
||||
}
|
108
models/RedisQueue.php
Normal file
108
models/RedisQueue.php
Normal file
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of RaspiSMS.
|
||||
*
|
||||
* (c) Pierre-Lin Bonnemaison <plebwebsas@gmail.com>
|
||||
*
|
||||
* This source file is subject to the GPL-3.0 license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace models;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class RedisQueue implements Queue
|
||||
{
|
||||
private \Redis $redis;
|
||||
private $group;
|
||||
private $consumer;
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* A Redis queue to store and exchange messages using redis streams
|
||||
* routing is based on queue uniq id as stream name, combined with ':tag' if routing is needed, messages are stored as json
|
||||
* @param string $id : A unique identifier for the queue
|
||||
* @param array $redis_parameters : Parameters for the redis server, such as host, port, etc. Default to a basic local redis on port 6379
|
||||
* @param string $group : Name to use for the redis group that must read this queue, default to 'default'
|
||||
* @param string $consumer : Name to use for the redis consumer in the group that must read this queue, default to 'default'
|
||||
*/
|
||||
public function __construct($id, $redis_parameters = [], $group = 'default', $consumer = 'default')
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->redis = new \Redis();
|
||||
$success = $this->redis->connect($redis_parameters['host'], intval($redis_parameters['port']), 1, '', 0, 0, ['auth' => $redis_parameters['auth']]);
|
||||
|
||||
if (!$success)
|
||||
{
|
||||
throw new \Exception('Failed to connect to redis server !');
|
||||
}
|
||||
|
||||
$this->group = $group;
|
||||
$this->consumer = $consumer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a message to the queue
|
||||
*
|
||||
* @param string $message : The message to add to the queue
|
||||
* @param ?string $tag : A tag to associate to the message for routing purposes, if null will add to general queue
|
||||
*/
|
||||
public function push($message, ?string $tag = null)
|
||||
{
|
||||
$stream = $this->id . ($tag !== null ? ":$tag" : '');
|
||||
$success = $this->redis->xAdd($stream, '*', ['message' => $message]);
|
||||
|
||||
if (!$success)
|
||||
{
|
||||
throw new \Exception('Failed to push a message !');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the older message in the queue
|
||||
*
|
||||
* @return mixed $message : The oldest message or null if no message found, can be anything
|
||||
* @param ?string $tag : A tag to associate to the message for routing purposes, if null will read from general queue
|
||||
* @param mixed : The message to add to the queue, can be anything, the queue will have to treat it by itself
|
||||
*/
|
||||
public function read(?string $tag = null)
|
||||
{
|
||||
$stream = $this->id . ($tag !== null ? ":$tag" : '');
|
||||
|
||||
// Create the consumer group if it doesn't already exist
|
||||
try
|
||||
{
|
||||
$this->redis->xGroup('CREATE', $stream, $this->group, '$', true);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// Ignore error if the group already exists
|
||||
}
|
||||
|
||||
// Read a single message starting from the oldest (>)
|
||||
$messages = $this->redis->xReadGroup($this->group, $this->consumer, [$stream => '>'], 1);
|
||||
if (!count($messages))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the message, acknowledge it and return it
|
||||
foreach ($messages as $stream_name => $entries)
|
||||
{
|
||||
foreach ($entries as $message_id => $message)
|
||||
{
|
||||
$success = $this->redis->xAck($stream, $this->group, [$message_id]);
|
||||
return $message['message'];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
115
models/SystemVQueue.php
Normal file
115
models/SystemVQueue.php
Normal file
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of RaspiSMS.
|
||||
*
|
||||
* (c) Pierre-Lin Bonnemaison <plebwebsas@gmail.com>
|
||||
*
|
||||
* This source file is subject to the GPL-3.0 license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace models;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class SystemVQueue implements Queue
|
||||
{
|
||||
private $id;
|
||||
private $queue;
|
||||
|
||||
/**
|
||||
* A queue using System V message queues to store and exchange messages
|
||||
* routing is based on queue id and message type
|
||||
*
|
||||
* ** Attention : Instead of string, all ids and tags must be numbers, its the system v queues works, no reliable way arround it**
|
||||
* @param int $id : A unique identifier for the queue, *this must be generated with ftok*
|
||||
|
||||
*/
|
||||
public function __construct($id)
|
||||
{
|
||||
$this->id = (int) $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to close the system v queue on destruction
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if ($this->queue)
|
||||
{
|
||||
msg_remove_queue($this->queue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to get the message queue and ensure it is open, we should always call it during push/read just to
|
||||
* make sure another process didn't close the queue
|
||||
*/
|
||||
private function get_queue()
|
||||
{
|
||||
$this->queue = msg_get_queue($this->id);
|
||||
|
||||
if (!$this->queue)
|
||||
{
|
||||
throw new \Exception('Impossible to get a System V message queue for id ' . $this->id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a message to the queue
|
||||
*
|
||||
* @param string $message : The message to add to the queue
|
||||
* @param ?string $tag : A tag to associate to the message for routing purposes.
|
||||
* Though this is a string, we MUST pass a valid number, its the way System V queue works
|
||||
*/
|
||||
public function push($message, ?string $tag = '0')
|
||||
{
|
||||
$tag = (int) $tag;
|
||||
|
||||
$this->get_queue();
|
||||
$error_code = null;
|
||||
$success = msg_send($this->queue, $tag, $message, true, false, $error_code);
|
||||
if (!$success)
|
||||
{
|
||||
throw new \Exception('Impossible to send the message on system V queue, error code : ' . $error_code);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the older message in the queue
|
||||
*
|
||||
* @param ?string $tag : A tag to associate to the message for routing purposes
|
||||
* Though this is a string, we MUST pass a valid number, its the way System V queue works
|
||||
*
|
||||
* @return mixed $message : The oldest message or null if no message found, can be anything
|
||||
*/
|
||||
public function read(?string $tag = '0')
|
||||
{
|
||||
$tag = (int) $tag;
|
||||
|
||||
$msgtype = null;
|
||||
$maxsize = 409600;
|
||||
$message = null;
|
||||
|
||||
// Message type is forged from a prefix concat with the phone ID
|
||||
$error_code = null;
|
||||
$this->get_queue();
|
||||
$success = msg_receive($this->queue, $tag, $msgtype, $maxsize, $message, true, MSG_IPC_NOWAIT, $error_code); //MSG_IPC_NOWAIT == dont wait if no message found
|
||||
|
||||
if (!$success && MSG_ENOMSG !== $error_code)
|
||||
{
|
||||
throw new \Exception('Impossible to read messages on system V queue, error code : ' . $error_code);
|
||||
}
|
||||
|
||||
if (!$message)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue