kuvalda-cache/SentinelConnection.php

149 lines
4.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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