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);
|
|
}
|
|
}
|