hyperf redis哨兵连接池 读写分离

23 篇文章 0 订阅

 这里使用的redis哨兵模式 , 并通过实例化不同的连接池达到读写分离的作用

 首先如图所示 , 创建这四个文件

RedisMaster

<?

namespace App\Pool;

use Hyperf\Contract\ConnectionInterface;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Pool\Exception\ConnectionException;
use Hyperf\Redis\Exception\InvalidRedisConnectionException;
use Hyperf\Redis\RedisConnection;

class RedisMaster extends RedisConnection implements ConnectionInterface
{

    public function reconnect(): bool
    {
        $auth = $this->config['auth'];
        $db = $this->config['db'];
        $cluster = $this->config['cluster']['enable'] ?? false;
        $sentinel = $this->config['sentinel']['enable'] ?? false;

        $redis = match (true) {
            $cluster => $this->createRedisCluster(),
            $sentinel => $this->createRedisSentinel(),
            default => $this->createRedis($this->config),
        };

        $options = $this->config['options'] ?? [];

        foreach ($options as $name => $value) {
            // The name is int, value is string.
            $redis->setOption($name, $value);
        }

        if ($redis instanceof \Redis && isset($auth) && $auth !== '') {
            $redis->auth($auth);
        }

        $database = $this->database ?? $db;
        if ($database > 0) {
            $redis->select($database);
        }

        $this->connection = $redis;
        $this->lastUseTime = microtime(true);

        return true;
    }


    protected function createRedisSentinel(): \Redis
    {
        try {
            $nodes = $this->config['sentinel']['nodes'] ?? [];
            $timeout = $this->config['timeout'] ?? 0;
            $persistent = $this->config['sentinel']['persistent'] ?? null;
            $retryInterval = $this->config['retry_interval'] ?? 0;
            $readTimeout = $this->config['sentinel']['read_timeout'] ?? 0;
            $masterName = $this->config['sentinel']['master_name'] ?? '';
            $auth = $this->config['sentinel']['auth'] ?? null;

            shuffle($nodes);

            $host = null;
            $port = null;
            foreach ($nodes as $node) {
                try {
                    [$sentinelHost, $sentinelPort] = explode(':', $node);
                    $sentinel = new \RedisSentinel(
                        $sentinelHost,
                        intval($sentinelPort),
                        $timeout,
                        $persistent,
                        $retryInterval,
                        $readTimeout
                    // $auth
                    );
                    $masterInfo = $sentinel->getMasterAddrByName($masterName);
                    if (is_array($masterInfo) && count($masterInfo) >= 2) {
                        [$host, $port] = $masterInfo;
                        break;
                    }
                    // echo $sentinelHost.$sentinelPort;
                } catch (\Throwable $exception) {
                    $logger = $this->container->get(StdoutLoggerInterface::class);
                    $logger->warning('Redis sentinel connection failed, caused by ' . $exception->getMessage());
                    continue;
                }
            }

            if ($host === null && $port === null) {
                throw new InvalidRedisConnectionException('Connect sentinel redis server failed.');
            }

            $redis = $this->createRedis([
                'host'           => $host,
                'port'           => $port,
                'timeout'        => $timeout,
                'retry_interval' => $retryInterval,
                'read_timeout'   => $readTimeout,
                'reserved'       => null
            ]);
        } catch (\Throwable $e) {
            throw new ConnectionException('Connection reconnect failed ' . $e->getMessage());
        }

        return $redis;
    }


    protected function createRedis(array $config): \Redis
    {
        $parameters = [
            $config['host'] ?? '',
            $config['port'] ?? 6379,
            $config['timeout'] ?? 0.0,
            $config['reserved'] ?? null,
            $config['retry_interval'] ?? 0,
            $config['read_timeout'] ?? 0.0,
        ];

        if (!empty($config['context'])) {
            $parameters[] = $config['context'];
        }

        $redis = new \Redis();
        $res = $redis->connect($config['host'], $config['port'], $config['timeout'], $config['reserved'], $config['retry_interval'], $config['read_timeout']);
        if (!$res) {
            throw new ConnectionException('Connection reconnect failed.');
        }

        return $redis;
    }

}

RedisMasterPool

<?php

namespace App\Pool;

use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\ConnectionInterface;
use Hyperf\Pool\Pool;
use Hyperf\Redis\Frequency;
use Hyperf\Utils\Arr;
use Psr\Container\ContainerInterface;

class RedisMasterPool extends Pool
{
    protected array $config;

    public function __construct(ContainerInterface $container, protected string $name = 'default')
    {
        $config = $container->get(ConfigInterface::class);
        $key = sprintf('redis.%s', $this->name);
        if (!$config->has($key)) {
            throw new \InvalidArgumentException(sprintf('config[%s] is not exist!', $key));
        }

        $this->config = $config->get($key);
        $options = Arr::get($this->config, 'pool', []);

        $this->frequency = make(Frequency::class);

        parent::__construct($container, $options);
    }

    public function getName(): string
    {
        return $this->name;
    }

    protected function createConnection(): ConnectionInterface
    {
        return new RedisMaster($this->container, $this, $this->config);
    }
}

RedisSlave

<?

namespace App\Pool;

use Hyperf\Contract\ConnectionInterface;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Pool\Exception\ConnectionException;
use Hyperf\Redis\Exception\InvalidRedisConnectionException;
use Hyperf\Redis\RedisConnection;

class RedisSlave extends RedisConnection implements ConnectionInterface
{


    public function reconnect(): bool
    {
        $auth = $this->config['auth'];
        $db = $this->config['db'];
        $cluster = $this->config['cluster']['enable'] ?? false;
        $sentinel = $this->config['sentinel']['enable'] ?? false;

        $redis = match (true) {
            $cluster => $this->createRedisCluster(),
            $sentinel => $this->createRedisSentinel(),
            default => $this->createRedis($this->config),
        };

        $options = $this->config['options'] ?? [];

        foreach ($options as $name => $value) {
            // The name is int, value is string.
            $redis->setOption($name, $value);
        }

        if ($redis instanceof \Redis && isset($auth) && $auth !== '') {
            $redis->auth($auth);
        }

        $database = $this->database ?? $db;
        if ($database > 0) {
            $redis->select($database);
        }

        $this->connection = $redis;
        $this->lastUseTime = microtime(true);

        return true;
    }


    protected function createRedisSentinel(): \Redis
    {
        try {
            $nodes = $this->config['sentinel']['nodes'] ?? [];
            $timeout = $this->config['timeout'] ?? 0;
            $persistent = $this->config['sentinel']['persistent'] ?? null;
            $retryInterval = $this->config['retry_interval'] ?? 0;
            $readTimeout = $this->config['sentinel']['read_timeout'] ?? 0;
            $masterName = $this->config['sentinel']['master_name'] ?? '';
            $auth = $this->config['sentinel']['auth'] ?? null;

            shuffle($nodes);

            $host = null;
            $port = null;


            $slaveInfos = $this->getSlaveRedis();
            if (count($slaveInfos) > 0) {
                shuffle($slaveInfos);
                foreach ($slaveInfos as $slaveInfo) {
                    try {
                        if (is_array($slaveInfo) && count($slaveInfo) >= 3) {
                            [$addr, $host, $port] = $slaveInfo;
                            break;
                        }
                    } catch (\Throwable $exception) {
                        $logger = $this->container->get(StdoutLoggerInterface::class);
                        $logger->warning('Redis slaves connection failed, caused by ' . $exception->getMessage());
                        continue;
                    }
                }
            } else {
                foreach ($nodes as $node) {
                    try {
                        [$sentinelHost, $sentinelPort] = explode(':', $node);
                        $sentinel = new \RedisSentinel(
                            $sentinelHost,
                            intval($sentinelPort),
                            $timeout,
                            $persistent,
                            $retryInterval,
                            $readTimeout
                        // $auth
                        );
                        $masterInfo = $sentinel->getMasterAddrByName($masterName);
                        if (is_array($masterInfo) && count($masterInfo) >= 2) {
                            [$host, $port] = $masterInfo;
                            break;
                        }
                    } catch (\Throwable $exception) {
                        $logger = $this->container->get(StdoutLoggerInterface::class);
                        $logger->warning('Redis sentinel connection failed, caused by ' . $exception->getMessage());
                        continue;
                    }
                }
            }

            if ($host === null && $port === null) {
                throw new InvalidRedisConnectionException('Connect sentinel redis server failed.');
            }

            $redis = $this->createRedis([
                'host'           => $host,
                'port'           => $port,
                'timeout'        => $timeout,
                'retry_interval' => $retryInterval,
                'read_timeout'   => $readTimeout,
                'reserved'       => null
            ]);

        } catch (\Throwable $e) {
            throw new ConnectionException('Connection reconnect failed ' . $e->getMessage());
        }

        return $redis;
    }


    protected function getSlaveRedis()
    {
        $redisPool = $this->container->get(\App\Pool\RedisMasterPool::class);
        $masterRedisConn = $redisPool->get();
        $infoReplication = $masterRedisConn->info('replication');
        $masterRedisConn->release();
        $slavesCount = $infoReplication['connected_slaves'];
        $slaves = [];
        for ($i = 0; $i < $slavesCount; $i ++) {
            preg_match('/ip=(.*?),port=(\d+)/', $infoReplication['slave' . $i], $slaves[]);
        }
        return $slaves;
    }


    protected function createRedis(array $config): \Redis
    {
        $parameters = [
            $config['host'] ?? '',
            $config['port'] ?? 6379,
            $config['timeout'] ?? 0.0,
            $config['reserved'] ?? null,
            $config['retry_interval'] ?? 0,
            $config['read_timeout'] ?? 0.0,
        ];

        if (!empty($config['context'])) {
            $parameters[] = $config['context'];
        }

        $redis = new \Redis();
        $res = $redis->connect($config['host'], $config['port'], $config['timeout'], $config['reserved'], $config['retry_interval'], $config['read_timeout']);

        if (!$res) {
            throw new ConnectionException('Connection reconnect failed.');
        }
        return $redis;
    }

}

RedisSlavePool

<?php

namespace App\Pool;

use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\ConnectionInterface;
use Hyperf\Pool\Pool;
use Hyperf\Redis\Frequency;
use Hyperf\Utils\Arr;
use Psr\Container\ContainerInterface;

class RedisSlavePool extends Pool
{
    protected array $config;

    public function __construct(ContainerInterface $container, protected string $name = 'default')
    {
        $config = $container->get(ConfigInterface::class);
        $key = sprintf('redis.%s', $this->name);
        if (!$config->has($key)) {
            throw new \InvalidArgumentException(sprintf('config[%s] is not exist!', $key));
        }

        $this->config = $config->get($key);
        $options = Arr::get($this->config, 'pool', []);

        $this->frequency = make(Frequency::class);

        parent::__construct($container, $options);
    }

    public function getName(): string
    {
        return $this->name;
    }

    protected function createConnection(): ConnectionInterface
    {
        return new RedisSlave($this->container, $this, $this->config);
    }
}

Config/autoload/redis

<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */
return [
    'default' => [
        'host' => env('REDIS_HOST', 'localhost'),
        'auth' => env('REDIS_AUTH', null),
        'port' => (int) env('REDIS_PORT', 6379),
        'db' => (int) env('REDIS_DB', 0),
        'timeout' => 0.0,
        'reserved' => null,
        'retry_interval' => 0,
        'read_timeout' => 0.0,
        'cluster' => [
            'enable' => (bool) env('REDIS_CLUSTER_ENABLE', false),
            'name' => null,
            'seeds' => [],
        ],
        'sentinel' => [
            'enable' => (bool) env('REDIS_SENTINEL_ENABLE', true),
            'master_name' => env('REDIS_MASTER_NAME', 'mymaster'),
            'nodes' => explode(';', env('REDIS_SENTINEL_NODE', '')),
            'persistent' => '',
            'read_timeout' => 100,
            'auth' => 'ygyc',
        ],
        'pool' => [
            'min_connections' => 1,
            'max_connections' => 10,
            'connect_timeout' => 0,
            'wait_timeout' => 3.0,
            'heartbeat' => -1,
            'max_idle_time' => (float) env('REDIS_MAX_IDLE_TIME', 0),
        ],
        
    ],
];

env文件自行修改

使用

<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */

namespace App\Controller;

use Hyperf\Di\Annotation\Inject;
use Hyperf\Utils\ApplicationContext;

class IndexController extends AbstractController
{

    public function index()
    {


        $container = ApplicationContext::getContainer();

        $redisMaster = $container->get(\App\Pool\RedisMasterPool::class)->get();
        $redisMaster->set('times', '2022-10-26');
        $redisMaster->release();


        $redisSlave = $container->get(\App\Pool\RedisSlavePool::class)->get();
        $time = $redisSlave->get('time');
        $redisSlave->release();


        return ['redis' => $time];
    }
}

这里使用完redis后需要release释放连接到连接池中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苗先生的PHP记录

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值