#117067 Доработка редис на yii проектах RedisSentinel

This commit is contained in:
2025-09-12 13:45:10 +03:00
parent c20f00cc2a
commit a7bb060bbd
5 changed files with 685 additions and 72 deletions

148
SentinelConnection.php Normal file
View File

@@ -0,0 +1,148 @@
<?php
namespace dominion\cache;
use Yii;
use yii\helpers\Inflector;
use Redis;
use RedisException;
use RedisSentinel;
class SentinelConnection extends Connection
{
/**
* Команды достапные на любой ноде master или slave
* @var array
*/
public $redisSlaveCommands = [
'GET',
'HGET',
//нужно перечислить все, но пока так
];
public $slaves = [];
public function executeCommand($name, $params = [])
{
$this->open();
Yii::debug("Executing Redis Command: {$name} " . var_export($params, true), __METHOD__);
$redis = $this->redis; //мастер
$redisCommand = strtoupper(Inflector::camel2words($name, false));
if (in_array($redisCommand, $this->redisSlaveCommands))
{
if(!empty($this->slaves))
{
//читаем из произвольного раба
$redis = $this->slaves[array_rand($this->slaves)];
}
}
return $redis->{$name}(...$params);
}
protected function isValidServer(mixed $server): bool
{
return is_array($server) && isset($server['ip']) && isset($server['port']);
}
public function createClient(array $config): Redis
{
$service = $config['sentinel_service'] ?? 'mymaster';
$hosts = is_array($config['sentinel_host']) ? $config['sentinel_host'] : [$config['sentinel_host']];
foreach ($hosts as $host)
{
$newConfig = $config;
$newConfig['sentinel_host'] = $host;
$sentinel = $this->connectToSentinel($newConfig);
if ($sentinel->ping())
{
break;
}
}
$master = $sentinel->master($service);
if (!$this->isValidServer($master))
{
throw new RedisException(sprintf("No master found for service '%s'.", $service));
}
$slaves = $sentinel->slaves($service);
foreach ($slaves as $slave)
{
if ($this->isValidServer($slave))
{
//могут быть проблемв из-за большого количества подключений
$this->slaves[] = parent::createClient(array_merge($config, [
'host' => $slave['ip'],
'port' => $slave['port'],
]));
}
}
return parent::createClient(array_merge($config, [
'host' => $master['ip'],
'port' => $master['port'],
]));
}
private function connectToSentinel(array $config): RedisSentinel
{
$host = $config['sentinel_host'] ?? '';
$port = $config['sentinel_port'] ?? 26379;
$timeout = $config['sentinel_timeout'] ?? 0.2;
$persistent = $config['sentinel_persistent'] ?? null;
$retryInterval = $config['sentinel_retry_interval'] ?? 0;
$readTimeout = $config['sentinel_read_timeout'] ?? 0;
$username = $config['sentinel_username'] ?? '';
$password = $config['sentinel_password'] ?? '';
$ssl = $config['sentinel_ssl'] ?? null;
if (strlen(trim($host)) === 0)
{
throw new ConfigurationException('No host has been specified for the Redis Sentinel connection.');
}
$auth = null;
if (strlen(trim($username)) !== 0 && strlen(trim($password)) !== 0)
{
$auth = [$username, $password];
}
elseif (strlen(trim($password)) !== 0)
{
$auth = $password;
}
if (version_compare(phpversion('redis'), '6.0', '>='))
{
$options = [
'host' => $host,
'port' => $port,
'connectTimeout' => $timeout,
'persistent' => $persistent,
'retryInterval' => $retryInterval,
'readTimeout' => $readTimeout,
];
if ($auth !== null)
{
$options['auth'] = $auth;
}
if (version_compare(phpversion('redis'), '6.1', '>=') && $ssl !== null)
{
$options['ssl'] = $ssl;
}
return new RedisSentinel($options);
}
if ($auth !== null)
{
/** @noinspection PhpMethodParametersCountMismatchInspection */
return new RedisSentinel($host, $port, $timeout, $persistent, $retryInterval, $readTimeout, $auth);
}
return new RedisSentinel($host, $port, $timeout, $persistent, $retryInterval, $readTimeout);
}
}