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