Skip to content

Commit 1c1fc94

Browse files
committed
Merge branch 'master' into pr/846
2 parents 82baf0a + fea3e69 commit 1c1fc94

File tree

10 files changed

+183
-15
lines changed

10 files changed

+183
-15
lines changed

publish/redis.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
'timeout' => 0.0,
2020
'reserved' => null,
2121
'retry_interval' => 0,
22+
'cluster' => [
23+
'enable' => (bool) env('REDIS_CLUSTER_ENABLE', false),
24+
'name' => null,
25+
'seeds' => [],
26+
],
2227
'pool' => [
2328
'min_connections' => 1,
2429
'max_connections' => 10,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* This file is part of Hyperf.
6+
*
7+
* @link https://www.hyperf.io
8+
* @document https://doc.hyperf.io
9+
* @contact [email protected]
10+
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
11+
*/
12+
13+
namespace Hyperf\Redis\Exception;
14+
15+
use RuntimeException;
16+
17+
class InvalidRedisConnectionException extends RuntimeException
18+
{
19+
}

src/Redis.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
namespace Hyperf\Redis;
1414

15+
use Hyperf\Redis\Exception\InvalidRedisConnectionException;
1516
use Hyperf\Redis\Pool\PoolFactory;
1617
use Hyperf\Utils\Context;
1718

@@ -41,6 +42,7 @@ public function __call($name, $arguments)
4142
$connection = $this->getConnection($hasContextConnection);
4243

4344
try {
45+
$connection = $connection->getConnection();
4446
// Execute the command with the arguments.
4547
$result = $connection->{$name}(...$arguments);
4648
} finally {
@@ -90,7 +92,10 @@ private function getConnection($hasContextConnection): RedisConnection
9092
}
9193
if (! $connection instanceof RedisConnection) {
9294
$pool = $this->factory->getPool($this->poolName);
93-
$connection = $pool->get()->getConnection();
95+
$connection = $pool->get();
96+
}
97+
if (! $connection instanceof RedisConnection) {
98+
throw new InvalidRedisConnectionException('The connection is not a valid RedisConnection.');
9499
}
95100
return $connection;
96101
}

src/RedisConnection.php

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@
1313
namespace Hyperf\Redis;
1414

1515
use Hyperf\Contract\ConnectionInterface;
16+
use Hyperf\Contract\StdoutLoggerInterface;
1617
use Hyperf\Pool\Connection as BaseConnection;
1718
use Hyperf\Pool\Exception\ConnectionException;
1819
use Hyperf\Pool\Pool;
1920
use Psr\Container\ContainerInterface;
2021

22+
/**
23+
* @method bool select(int $db)
24+
*/
2125
class RedisConnection extends BaseConnection implements ConnectionInterface
2226
{
2327
use ScanCaller;
@@ -36,6 +40,11 @@ class RedisConnection extends BaseConnection implements ConnectionInterface
3640
'auth' => null,
3741
'db' => 0,
3842
'timeout' => 0.0,
43+
'cluster' => [
44+
'enable' => false,
45+
'name' => null,
46+
'seeds' => [],
47+
],
3948
'options' => [],
4049
];
4150

@@ -55,7 +64,13 @@ public function __construct(ContainerInterface $container, Pool $pool, array $co
5564

5665
public function __call($name, $arguments)
5766
{
58-
return $this->connection->{$name}(...$arguments);
67+
try {
68+
$result = $this->connection->{$name}(...$arguments);
69+
} catch (\Throwable $exception) {
70+
$result = $this->retry($name, $arguments, $exception);
71+
}
72+
73+
return $result;
5974
}
6075

6176
public function getActiveConnection()
@@ -78,10 +93,16 @@ public function reconnect(): bool
7893
$auth = $this->config['auth'];
7994
$db = $this->config['db'];
8095
$timeout = $this->config['timeout'];
81-
82-
$redis = new \Redis();
83-
if (! $redis->connect($host, $port, $timeout)) {
84-
throw new ConnectionException('Connection reconnect failed.');
96+
$cluster = $this->config['cluster']['enable'] ?? false;
97+
98+
$redis = null;
99+
if ($cluster !== true) {
100+
$redis = new \Redis();
101+
if (! $redis->connect($host, $port, $timeout)) {
102+
throw new ConnectionException('Connection reconnect failed.');
103+
}
104+
} else {
105+
$redis = $this->createRedisCluster();
85106
}
86107

87108
$options = $this->config['options'] ?? [];
@@ -127,4 +148,35 @@ public function setDatabase(?int $database): void
127148
{
128149
$this->database = $database;
129150
}
151+
152+
protected function createRedisCluster()
153+
{
154+
try {
155+
$seeds = $this->config['cluster']['seeds'] ?? [];
156+
$name = $this->config['cluster']['name'] ?? null;
157+
$timeout = $this->config['timeout'] ?? null;
158+
159+
$redis = new \RedisCluster($name, $seeds, $timeout);
160+
} catch (\Throwable $e) {
161+
throw new ConnectionException('Connection reconnect failed. ' . $e->getMessage());
162+
}
163+
164+
return $redis;
165+
}
166+
167+
protected function retry($name, $arguments, \Throwable $exception)
168+
{
169+
$logger = $this->container->get(StdoutLoggerInterface::class);
170+
$logger->warning(sprintf('Redis::__call failed, bacause ' . $exception->getMessage()));
171+
172+
try {
173+
$this->reconnect();
174+
$result = $this->connection->{$name}(...$arguments);
175+
} catch (\Throwable $exception) {
176+
$this->lastUseTime = 0.0;
177+
throw $exception;
178+
}
179+
180+
return $result;
181+
}
130182
}

src/RedisFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public function __construct(ConfigInterface $config)
3232
}
3333

3434
/**
35-
* @return \Redis
35+
* @return \Redis|RedisProxy
3636
*/
3737
public function get(string $poolName)
3838
{

tests/RedisConnectionTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ public function testRedisConnectionConfig()
4747
'auth' => 'redis',
4848
'db' => 0,
4949
'timeout' => 0.0,
50+
'cluster' => [
51+
'enable' => false,
52+
'name' => null,
53+
'seeds' => [],
54+
],
5055
'options' => [],
5156
'pool' => [
5257
'min_connections' => 1,

tests/RedisTest.php

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@
1515
use Hyperf\Config\Config;
1616
use Hyperf\Contract\ConfigInterface;
1717
use Hyperf\Di\Container;
18+
use Hyperf\Pool\Channel;
19+
use Hyperf\Pool\PoolOption;
20+
use Hyperf\Redis\Frequency;
1821
use Hyperf\Redis\Pool\PoolFactory;
1922
use Hyperf\Redis\Pool\RedisPool;
2023
use Hyperf\Redis\Redis;
2124
use Hyperf\Utils\ApplicationContext;
25+
use Hyperf\Utils\Context;
26+
use HyperfTest\Redis\Stub\RedisPoolFailedStub;
2227
use HyperfTest\Redis\Stub\RedisPoolStub;
2328
use Mockery;
2429
use PHPUnit\Framework\TestCase;
@@ -32,6 +37,7 @@ class RedisTest extends TestCase
3237
public function tearDown()
3338
{
3439
Mockery::close();
40+
Context::set('redis.connection.default', null);
3541
}
3642

3743
public function testRedisConnect()
@@ -68,9 +74,38 @@ public function testRedisSelect()
6874
$this->assertSame('db:0 name:get argument:xxxx', $res[0]);
6975
}
7076

77+
public function testRedisReuseAfterThrowable()
78+
{
79+
$container = $this->getContainer();
80+
$pool = new RedisPoolFailedStub($container, 'default');
81+
$container->shouldReceive('make')->once()->with(RedisPool::class, ['name' => 'default'])->andReturn($pool);
82+
$factory = new PoolFactory($container);
83+
$redis = new Redis($factory);
84+
try {
85+
$redis->set('xxxx', 'yyyy');
86+
} catch (\Throwable $exception) {
87+
$this->assertSame('Get connection failed.', $exception->getMessage());
88+
}
89+
90+
$this->assertSame(1, $pool->getConnectionsInChannel());
91+
$this->assertSame(1, $pool->getCurrentConnections());
92+
}
93+
7194
private function getRedis()
95+
{
96+
$container = $this->getContainer();
97+
$pool = new RedisPoolStub($container, 'default');
98+
$container->shouldReceive('make')->once()->with(RedisPool::class, ['name' => 'default'])->andReturn($pool);
99+
$factory = new PoolFactory($container);
100+
101+
return new Redis($factory);
102+
}
103+
104+
private function getContainer()
72105
{
73106
$container = Mockery::mock(Container::class);
107+
ApplicationContext::setContainer($container);
108+
74109
$container->shouldReceive('get')->once()->with(ConfigInterface::class)->andReturn(new Config([
75110
'redis' => [
76111
'default' => [
@@ -89,13 +124,13 @@ private function getRedis()
89124
],
90125
],
91126
]));
92-
$pool = new RedisPoolStub($container, 'default');
93-
$container->shouldReceive('make')->once()->with(RedisPool::class, ['name' => 'default'])->andReturn($pool);
94-
95-
ApplicationContext::setContainer($container);
96-
97-
$factory = new PoolFactory($container);
98-
99-
return new Redis($factory);
127+
$container->shouldReceive('make')->with(Frequency::class, Mockery::any())->andReturn(new Frequency());
128+
$container->shouldReceive('make')->with(PoolOption::class, Mockery::any())->andReturnUsing(function ($class, $args) {
129+
return new PoolOption(...array_values($args));
130+
});
131+
$container->shouldReceive('make')->with(Channel::class, Mockery::any())->andReturnUsing(function ($class, $args) {
132+
return new Channel($args['size']);
133+
});
134+
return $container;
100135
}
101136
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* This file is part of Hyperf.
6+
*
7+
* @link https://www.hyperf.io
8+
* @document https://doc.hyperf.io
9+
* @contact [email protected]
10+
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
11+
*/
12+
13+
namespace HyperfTest\Redis\Stub;
14+
15+
class RedisConnectionFailedStub extends RedisConnectionStub
16+
{
17+
public function getConnection()
18+
{
19+
throw new \Exception('Get connection failed.');
20+
}
21+
}

tests/Stub/RedisConnectionStub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ public function reconnect(): bool
3939
$this->db = $this->config['db'];
4040
$this->timeout = $this->config['timeout'];
4141

42+
$this->lastUseTime = microtime(true);
43+
4244
return true;
4345
}
4446

tests/Stub/RedisPoolFailedStub.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* This file is part of Hyperf.
6+
*
7+
* @link https://www.hyperf.io
8+
* @document https://doc.hyperf.io
9+
* @contact [email protected]
10+
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
11+
*/
12+
13+
namespace HyperfTest\Redis\Stub;
14+
15+
use Hyperf\Contract\ConnectionInterface;
16+
use Hyperf\Redis\Pool\RedisPool;
17+
18+
class RedisPoolFailedStub extends RedisPool
19+
{
20+
protected function createConnection(): ConnectionInterface
21+
{
22+
return new RedisConnectionFailedStub($this->container, $this, $this->config);
23+
}
24+
}

0 commit comments

Comments
 (0)