Merge pull request #873 from tai1030/master

Support redis cluster
This commit is contained in:
李铭昕 2019-11-07 11:29:19 +08:00 committed by GitHub
commit c711676321
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 145 additions and 22 deletions

View File

@ -38,6 +38,6 @@ before_script:
- composer config -g process-timeout 900 && composer update - composer config -g process-timeout 900 && composer update
script: script:
- composer analyse src/di src/json-rpc src/tracer src/metric src/nats - composer analyse src/di src/json-rpc src/tracer src/metric src/redis src/nats
- composer test -- --exclude-group NonCoroutine - composer test -- --exclude-group NonCoroutine
- vendor/bin/phpunit --group NonCoroutine - vendor/bin/phpunit --group NonCoroutine

View File

@ -6,8 +6,9 @@
- [#820](https://github.com/hyperf/hyperf/pull/820) Added nats component. - [#820](https://github.com/hyperf/hyperf/pull/820) Added nats component.
- [#832](https://github.com/hyperf/hyperf/pull/832) Added `Hyperf\Utils\Codec\Json`. - [#832](https://github.com/hyperf/hyperf/pull/832) Added `Hyperf\Utils\Codec\Json`.
- [#833](https://github.com/hyperf/hyperf/pull/833) Added `Hyperf\Utils\Backoff`. - [#833](https://github.com/hyperf/hyperf/pull/833) Added `Hyperf\Utils\Backoff`.
- [#852](https://github.com/hyperf/hyperf/pull/852) Added a `clear()` method for `Hyperf\Utils\Parallel` to clear adde callbacks. - [#852](https://github.com/hyperf/hyperf/pull/852) Added a `clear()` method for `Hyperf\Utils\Parallel` to clear added callbacks.
- [#854](https://github.com/hyperf/hyperf/pull/854) Added `GraphQLMiddleware`. - [#854](https://github.com/hyperf/hyperf/pull/854) Added `GraphQLMiddleware`.
- [#873](https://github.com/hyperf/hyperf/pull/873) Added redis cluster.
## Fixed ## Fixed

View File

@ -8,12 +8,15 @@ composer require hyperf/redis
## 配置 ## 配置
| 配置项 | 类型 | 默认值 | 备注 | | 配置项 | 类型 | 默认值 | 备注 |
|:------:|:-------:|:-----------:|:---------:| |:--------------:|:-------:|:-----------:|:------------------------------:|
| host | string | 'localhost' | Redis地址 | | host | string | 'localhost' | Redis地址 |
| auth | string | 无 | 密码 | | auth | string | 无 | 密码 |
| port | integer | 6379 | 端口 | | port | integer | 6379 | 端口 |
| db | integer | 0 | DB | | db | integer | 0 | DB |
| cluster.enable | boolean | false | 是否集群模式 |
| cluster.name | string | null | 集群名 |
| cluster.seeds | array | [] | 集群连接地址数组 ['host:port'] |
```php ```php
<?php <?php
@ -23,6 +26,11 @@ return [
'auth' => env('REDIS_AUTH', ''), 'auth' => env('REDIS_AUTH', ''),
'port' => (int) env('REDIS_PORT', 6379), 'port' => (int) env('REDIS_PORT', 6379),
'db' => (int) env('REDIS_DB', 0), 'db' => (int) env('REDIS_DB', 0),
'cluster' => [
'enable' => (bool) env('REDIS_CLUSTER_ENABLE', false),
'name' => null,
'seeds' => [],
],
'pool' => [ 'pool' => [
'min_connections' => 1, 'min_connections' => 1,
'max_connections' => 10, 'max_connections' => 10,
@ -63,6 +71,11 @@ return [
'auth' => env('REDIS_AUTH', ''), 'auth' => env('REDIS_AUTH', ''),
'port' => (int) env('REDIS_PORT', 6379), 'port' => (int) env('REDIS_PORT', 6379),
'db' => (int) env('REDIS_DB', 0), 'db' => (int) env('REDIS_DB', 0),
'cluster' => [
'enable' => (bool) env('REDIS_CLUSTER_ENABLE', false),
'name' => null,
'seeds' => [],
],
'pool' => [ 'pool' => [
'min_connections' => 1, 'min_connections' => 1,
'max_connections' => 10, 'max_connections' => 10,
@ -128,3 +141,59 @@ $redis = $container->get(RedisFactory::class)->get('foo');
$result = $redis->keys('*'); $result = $redis->keys('*');
``` ```
## 集群模式
### 使用 `name`
配置 `cluster`,修改修改 `redis.ini`,也可以修改 `Dockerfile` 如下
```
# - config PHP
&& { \
echo "upload_max_filesize=100M"; \
echo "post_max_size=108M"; \
echo "memory_limit=1024M"; \
echo "date.timezone=${TIMEZONE}"; \
echo "redis.clusters.seeds = \"mycluster[]=localhost:7000&mycluster[]=localhost:7001\""; \
echo "redis.clusters.timeout = \"mycluster=5\""; \
echo "redis.clusters.read_timeout = \"mycluster=10\""; \
echo "redis.clusters.auth = \"mycluster=password\"";
} | tee conf.d/99-overrides.ini \
```
对应 PHP 配置如下
```php
<?php
// 省略其他配置
return [
'default' => [
'cluster' => [
'enable' => true,
'name' => 'mycluster',
'seeds' => [],
],
],
];
```
### 使用 seeds
当然不配置 name 直接使用 seeds 也是可以的。如下
```php
<?php
// 省略其他配置
return [
'default' => [
'cluster' => [
'enable' => true,
'name' => null,
'seeds' => [
'192.168.1.110:6379',
'192.168.1.111:6379',
],
],
],
];
```

View File

@ -19,6 +19,11 @@ return [
'timeout' => 0.0, 'timeout' => 0.0,
'reserved' => null, 'reserved' => null,
'retry_interval' => 0, 'retry_interval' => 0,
'cluster' => [
'enable' => (bool) env('REDIS_CLUSTER_ENABLE', false),
'name' => null,
'seeds' => [],
],
'pool' => [ 'pool' => [
'min_connections' => 1, 'min_connections' => 1,
'max_connections' => 10, 'max_connections' => 10,

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Redis\Exception;
use RuntimeException;
class InvalidRedisConnectionException extends RuntimeException
{
}

View File

@ -49,9 +49,6 @@ class RedisPool extends Pool
parent::__construct($container, $options); parent::__construct($container, $options);
} }
/**
* @return string
*/
public function getName(): string public function getName(): string
{ {
return $this->name; return $this->name;

View File

@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Hyperf\Redis; namespace Hyperf\Redis;
use Hyperf\Redis\Exception\InvalidRedisConnectionException;
use Hyperf\Redis\Pool\PoolFactory; use Hyperf\Redis\Pool\PoolFactory;
use Hyperf\Utils\Context; use Hyperf\Utils\Context;
@ -89,7 +90,10 @@ class Redis
} }
if (! $connection instanceof RedisConnection) { if (! $connection instanceof RedisConnection) {
$pool = $this->factory->getPool($this->poolName); $pool = $this->factory->getPool($this->poolName);
return $pool->get(); $connection = $pool->get();
}
if (! $connection instanceof RedisConnection) {
throw new InvalidRedisConnectionException('The connection is not a valid RedisConnection.');
} }
return $connection; return $connection;
} }

View File

@ -19,6 +19,9 @@ use Hyperf\Pool\Exception\ConnectionException;
use Hyperf\Pool\Pool; use Hyperf\Pool\Pool;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
/**
* @method bool select(int $db)
*/
class RedisConnection extends BaseConnection implements ConnectionInterface class RedisConnection extends BaseConnection implements ConnectionInterface
{ {
/** /**
@ -35,6 +38,11 @@ class RedisConnection extends BaseConnection implements ConnectionInterface
'auth' => null, 'auth' => null,
'db' => 0, 'db' => 0,
'timeout' => 0.0, 'timeout' => 0.0,
'cluster' => [
'enable' => false,
'name' => null,
'seeds' => [],
],
'options' => [], 'options' => [],
]; ];
@ -83,10 +91,16 @@ class RedisConnection extends BaseConnection implements ConnectionInterface
$auth = $this->config['auth']; $auth = $this->config['auth'];
$db = $this->config['db']; $db = $this->config['db'];
$timeout = $this->config['timeout']; $timeout = $this->config['timeout'];
$cluster = $this->config['cluster']['enable'] ?? false;
$redis = new \Redis(); $redis = null;
if (! $redis->connect($host, $port, $timeout)) { if ($cluster !== true) {
throw new ConnectionException('Connection reconnect failed.'); $redis = new \Redis();
if (! $redis->connect($host, $port, $timeout)) {
throw new ConnectionException('Connection reconnect failed.');
}
} else {
$redis = $this->createRedisCluster();
} }
$options = $this->config['options'] ?? []; $options = $this->config['options'] ?? [];
@ -133,6 +147,21 @@ class RedisConnection extends BaseConnection implements ConnectionInterface
$this->database = $database; $this->database = $database;
} }
protected function createRedisCluster()
{
try {
$seeds = $this->config['cluster']['seeds'] ?? [];
$name = $this->config['cluster']['name'] ?? null;
$timeout = $this->config['timeout'] ?? null;
$redis = new \RedisCluster($name, $seeds, $timeout);
} catch (\Throwable $e) {
throw new ConnectionException('Connection reconnect failed. ' . $e->getMessage());
}
return $redis;
}
protected function retry($name, $arguments, \Throwable $exception) protected function retry($name, $arguments, \Throwable $exception)
{ {
$logger = $this->container->get(StdoutLoggerInterface::class); $logger = $this->container->get(StdoutLoggerInterface::class);

View File

@ -32,7 +32,7 @@ class RedisFactory
} }
/** /**
* @return \Redis * @return \Redis|RedisProxy
*/ */
public function get(string $poolName) public function get(string $poolName)
{ {

View File

@ -47,6 +47,11 @@ class RedisConnectionTest extends TestCase
'auth' => 'redis', 'auth' => 'redis',
'db' => 0, 'db' => 0,
'timeout' => 0.0, 'timeout' => 0.0,
'cluster' => [
'enable' => false,
'name' => null,
'seeds' => [],
],
'options' => [], 'options' => [],
'pool' => [ 'pool' => [
'min_connections' => 1, 'min_connections' => 1,

View File

@ -49,17 +49,11 @@ class RedisConnectionStub extends RedisConnection
$this->db = $db; $this->db = $db;
} }
/**
* @return array
*/
public function getConfig(): array public function getConfig(): array
{ {
return $this->config; return $this->config;
} }
/**
* @return null|int
*/
public function getDatabase(): ?int public function getDatabase(): ?int
{ {
return $this->database; return $this->database;