149 lines
4.6 KiB
PHP
149 lines
4.6 KiB
PHP
<?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);
|
||
}
|
||
}
|