diff --git a/src/db/test/Cases/Mysql/BugTest.php b/src/db/test/Cases/Mysql/BugTest.php index 998b89c23..c7ea6f0cd 100644 --- a/src/db/test/Cases/Mysql/BugTest.php +++ b/src/db/test/Cases/Mysql/BugTest.php @@ -317,21 +317,22 @@ public function testListType(int $uid) /* @var User $user*/ $user = User::findById($uid)->getResult(); $userAry = $user->toArray(); - $this->assertTrue(is_int($userAry['age'])); $this->assertTrue(is_int($userAry['sex'])); $this->assertTrue(is_string($userAry['desc'])); + /** + * 通过 QueryBuilder 执行的查询将不再格式化结果属性的数据类型 + * @see https://github.com/swoft-cloud/swoft-component/pull/209 + */ $row = Query::table(User::class)->where('id', $uid)->one()->getResult(); - - $this->assertTrue(is_int($row['age'])); - $this->assertTrue(is_int($row['sex'])); + $this->assertTrue(is_string($row['age'])); + $this->assertTrue(is_string($row['sex'])); $this->assertTrue(is_string($row['description'])); - $rows = Query::table(User::class)->where('id', $uid)->get()->getResult(); foreach ($rows as $userRow){ - $this->assertTrue(is_int($userRow['age'])); - $this->assertTrue(is_int($userRow['sex'])); + $this->assertTrue(is_string($userRow['age'])); + $this->assertTrue(is_string($userRow['sex'])); $this->assertTrue(is_string($userRow['description'])); } } @@ -374,4 +375,4 @@ public function testNoInc() } -} \ No newline at end of file +} diff --git a/src/http-client/src/Adapter/CoroutineAdapter.php b/src/http-client/src/Adapter/CoroutineAdapter.php index 63e7b535e..1f529dc0e 100644 --- a/src/http-client/src/Adapter/CoroutineAdapter.php +++ b/src/http-client/src/Adapter/CoroutineAdapter.php @@ -6,6 +6,7 @@ use Swoft\App; use Swoft\Helper\JsonHelper; use Swoft\Http\Message\Uri\Uri; +use Swoft\HttpClient\Exception\RuntimeException; use Swoft\HttpClient\HttpCoResult; use Swoft\HttpClient\HttpResultInterface; use Swoole\Coroutine; @@ -30,7 +31,7 @@ class CoroutineAdapter implements AdapterInterface * @param array $options * @return HttpResultInterface * @throws \InvalidArgumentException - * @throws \RuntimeException + * @throws RuntimeException */ public function request(RequestInterface $request, array $options = []): HttpResultInterface { @@ -61,7 +62,7 @@ public function request(RequestInterface $request, array $options = []): HttpRes if (null !== $client->errCode && $client->errCode !== 0) { App::error(sprintf('HttpClient Request ERROR #%s url=%s', $client->errCode, $url)); - throw new \RuntimeException(\socket_strerror($client->errCode), $client->errCode); + throw new RuntimeException(\socket_strerror($client->errCode), $client->errCode); } $result = new HttpCoResult(null, $client, $profileKey); diff --git a/src/http-client/src/Adapter/CurlAdapter.php b/src/http-client/src/Adapter/CurlAdapter.php index a15a3314f..ce2561d01 100644 --- a/src/http-client/src/Adapter/CurlAdapter.php +++ b/src/http-client/src/Adapter/CurlAdapter.php @@ -5,6 +5,7 @@ use Psr\Http\Message\RequestInterface; use Swoft\App; use Swoft\Helper\JsonHelper; +use Swoft\HttpClient\Exception\RuntimeException; use Swoft\HttpClient\HttpResult; use Swoft\HttpClient\HttpResultInterface; @@ -25,7 +26,7 @@ class CurlAdapter implements AdapterInterface * @param array $options * @return HttpResultInterface * @throws \InvalidArgumentException - * @throws \RuntimeException + * @throws RuntimeException */ public function request(RequestInterface $request, array $options = []): HttpResultInterface { @@ -59,7 +60,7 @@ public function request(RequestInterface $request, array $options = []): HttpRes $errorString = curl_error($resource); if ($errorNo) { App::error(sprintf('HttpClient Request ERROR #%s url=%s', $errorNo, $url)); - throw new \RuntimeException($errorString, $errorNo); + throw new RuntimeException($errorString, $errorNo); } $result = new HttpResult($result); diff --git a/src/http-message/src/Server/Request.php b/src/http-message/src/Server/Request.php index ed1adf28d..cb37ae4e8 100644 --- a/src/http-message/src/Server/Request.php +++ b/src/http-message/src/Server/Request.php @@ -77,12 +77,13 @@ public static function loadFromSwooleRequest(\Swoole\Http\Request $swooleRequest $body = new SwooleStream($swooleRequest->rawContent()); $protocol = isset($server['server_protocol']) ? str_replace('HTTP/', '', $server['server_protocol']) : '1.1'; $request = new static($method, $uri, $headers, $body, $protocol); - return $request->withCookieParams($swooleRequest->cookie ?? []) - ->withQueryParams($swooleRequest->get ?? []) - ->withServerParams($server ?? []) - ->withParsedBody($swooleRequest->post ?? []) - ->withUploadedFiles(self::normalizeFiles($swooleRequest->files ?? [])) - ->setSwooleRequest($swooleRequest); + $request->cookieParams = ($swooleRequest->cookie ?? []); + $request->queryParams = ($swooleRequest->get ?? []); + $request->serverParams = ($server ?? []); + $request->parsedBody = ($swooleRequest->post ?? []); + $request->uploadedFiles = self::normalizeFiles($swooleRequest->files ?? []); + $request->swooleRequest = $swooleRequest; + return $request; } /** diff --git a/src/redis/.travis.yml b/src/redis/.travis.yml index fcc62ac8d..54bc18f78 100644 --- a/src/redis/.travis.yml +++ b/src/redis/.travis.yml @@ -11,7 +11,7 @@ services: install: - wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.gz && mkdir -p hiredis && tar -xf hiredis.tar.gz -C hiredis --strip-components=1 && cd hiredis && sudo make -j$(nproc) && sudo make install && sudo ldconfig && cd .. - echo 'no' | pecl install -f redis - - wget https://github.com/swoole/swoole-src/archive/v4.0.2.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure --enable-async-redis && make -j$(nproc) && make install && cd - + - wget https://github.com/swoole/swoole-src/archive/v4.2.9.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure --enable-async-redis && make -j$(nproc) && make install && cd - - echo "extension = swoole.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini before_script: diff --git a/src/redis/src/AbstractRedisConnection.php b/src/redis/src/AbstractRedisConnection.php index a9784f01c..2706fbd02 100644 --- a/src/redis/src/AbstractRedisConnection.php +++ b/src/redis/src/AbstractRedisConnection.php @@ -88,10 +88,6 @@ protected function initRedis() $error = sprintf('Redis connection authentication failed host=%s port=%d auth=%s', $host, (int)$port, (string)$config['auth']); throw new RedisException($error); } - if (isset($config['database']) && $config['database'] < 16 && false === $redis->select($config['database'])) { - $error = sprintf('Redis selection database failure host=%s port=%d database=%d', $host, (int)$port, (int)$config['database']); - throw new RedisException($error); - } return $redis; } diff --git a/src/redis/src/Operator/Hashes/HashGetAll.php b/src/redis/src/Operator/Hashes/HashGetAll.php index 8de686a4c..c9abbfd4b 100644 --- a/src/redis/src/Operator/Hashes/HashGetAll.php +++ b/src/redis/src/Operator/Hashes/HashGetAll.php @@ -21,8 +21,11 @@ public function getId() */ public function parseResponse($data) { - $result = array(); + if ($data === false) { + return false; + } + $result = []; for ($i = 0; $i < count($data); ++$i) { $result[$data[$i]] = $data[++$i]; } diff --git a/src/redis/src/Operator/Hashes/HashGetMultiple.php b/src/redis/src/Operator/Hashes/HashGetMultiple.php index 818933acc..b44c26318 100644 --- a/src/redis/src/Operator/Hashes/HashGetMultiple.php +++ b/src/redis/src/Operator/Hashes/HashGetMultiple.php @@ -21,10 +21,14 @@ public function getId() */ public function parseResponse($data) { + if ($data === false) { + return false; + } + $result = []; $hashKeys = $this->getArgument(1); foreach ($data as $key => $value) { - if (! isset($hashKeys[$key])) { + if (!isset($hashKeys[$key])) { continue; } diff --git a/src/redis/src/Operator/Processor/PrefixProcessor.php b/src/redis/src/Operator/Processor/PrefixProcessor.php index 57dd65cb5..e5b28675b 100644 --- a/src/redis/src/Operator/Processor/PrefixProcessor.php +++ b/src/redis/src/Operator/Processor/PrefixProcessor.php @@ -3,6 +3,7 @@ namespace Swoft\Redis\Operator\Processor; use Swoft\Bean\Annotation\Inject; +use Swoft\Helper\StringHelper; use Swoft\Redis\Operator\CommandInterface; use Swoft\Bean\Annotation\Bean; use Swoft\Redis\Pool\Config\RedisPoolConfig; @@ -95,6 +96,7 @@ public function init() 'ZADD' => 'static::first', 'ZINCRBY' => 'static::first', 'ZREM' => 'static::first', + 'ZDELETE' => 'static::first', 'ZRANGE' => 'static::first', 'ZREVRANGE' => 'static::first', 'ZRANGEBYSCORE' => 'static::first', @@ -149,6 +151,10 @@ public function init() 'EVALSHA' => 'static::evalKeys', 'ZRANGEBYLEX' => 'static::first', 'ZREVRANGEBYLEX' => 'static::first', + 'PFADD' => 'static::first', + 'PFCOUNT' => 'static::allarray', + 'PFMERGE' => 'static::allarray', + 'ZINTER' => 'static::allarray2', ]; } @@ -257,6 +263,33 @@ public static function all(CommandInterface $command, $prefix) } } + /** + * Applies the specified prefix to all the arguments. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function allarray(CommandInterface $command, $prefix, $len = 0) + { + if ($arguments = $command->getArguments()) { + $result = []; + foreach ($arguments as $index => $key) { + if ($len > 0 && $index >= $len) { + $result[] = $key; + } elseif (is_array($key)) { + foreach ($key as &$i) { + $i = "$prefix$i"; + } + $result[] = $key; + } else { + $result[] = "$prefix$key"; + } + } + + $command->setRawArguments($result); + } + } + /** * Applies the specified prefix only to even arguments in the list. * @@ -384,4 +417,15 @@ public static function zsetStore(CommandInterface $command, $prefix) $command->setRawArguments($arguments); } } + + public static function __callStatic($name, $arguments) + { + if (StringHelper::startsWith($name, 'allarray')) { + $len = (int)StringHelper::replaceFirst('allarray', '', $name); + $arguments[] = $len; + return static::allarray(...$arguments); + } + + throw new \Exception("class 'Swoft\Redis\Operator\Processor\PrefixProcessor' does not have a method '{$name}'"); + } } diff --git a/src/redis/src/Operator/Strings/StringPfAdd.php b/src/redis/src/Operator/Strings/StringPfAdd.php new file mode 100644 index 000000000..fc47cf14b --- /dev/null +++ b/src/redis/src/Operator/Strings/StringPfAdd.php @@ -0,0 +1,19 @@ +poolConfig->getDb(); - $redis->select($dbIndex); - return $redis; } } diff --git a/src/redis/src/Profile/RedisCommandProvider.php b/src/redis/src/Profile/RedisCommandProvider.php index bfceee4b1..1778af869 100644 --- a/src/redis/src/Profile/RedisCommandProvider.php +++ b/src/redis/src/Profile/RedisCommandProvider.php @@ -80,6 +80,10 @@ public function getSupportedCommands(): array 'INCRBYFLOAT' => '\Swoft\Redis\Operator\Strings\StringIncrementByFloat', 'BITOP' => '\Swoft\Redis\Operator\Strings\StringBitOp', 'BITCOUNT' => '\Swoft\Redis\Operator\Strings\StringBitCount', + /* ---------------- Redis 2.8 ---------------- */ + 'PFADD' => '\Swoft\Redis\Operator\Strings\StringPfAdd', + 'PFCOUNT' => '\Swoft\Redis\Operator\Strings\StringPfCount', + 'PFMERGE' => '\Swoft\Redis\Operator\Strings\StringPfMerge', /* commands operating on lists */ /* ---------------- Redis 1.2 ---------------- */ @@ -146,6 +150,7 @@ public function getSupportedCommands(): array 'ZREVRANK' => '\Swoft\Redis\Operator\ZSets\ZSetReverseRank', 'ZREMRANGEBYRANK' => '\Swoft\Redis\Operator\ZSets\ZSetRemoveRangeByRank', 'ZDELETERANGEBYRANK' => '\Swoft\Redis\Operator\ZSets\ZSetRemoveRangeByRank', + 'ZINTER' => '\Swoft\Redis\Operator\ZSets\ZSetInterStore', /* ---------------- Redis 2.2 ---------------- */ 'ZREVRANGEBYSCORE' => '\Swoft\Redis\Operator\ZSets\ZSetReverseRangeByScore', /* ---------------- Redis 2.8 ---------------- */ diff --git a/src/redis/src/RedisConnection.php b/src/redis/src/RedisConnection.php index a2a5aa466..e088de358 100644 --- a/src/redis/src/RedisConnection.php +++ b/src/redis/src/RedisConnection.php @@ -33,6 +33,10 @@ public function createConnection() { $redis = $this->initRedis(); $this->connection = $redis; + + /** @var RedisPoolConfig $config */ + $config = $this->getPool()->getPoolConfig(); + $redis->select($config->getDb()); } /** diff --git a/src/redis/src/SyncRedisConnection.php b/src/redis/src/SyncRedisConnection.php index 827db2cc9..90c529014 100644 --- a/src/redis/src/SyncRedisConnection.php +++ b/src/redis/src/SyncRedisConnection.php @@ -39,6 +39,10 @@ public function createConnection() $redis->setOption(\Redis::OPT_PREFIX, $prefix); } $this->connection = $redis; + + /** @var RedisPoolConfig $config */ + $config = $this->getPool()->getPoolConfig(); + $redis->select($config->getDb()); } /** diff --git a/src/redis/test/Cases/HashTest.php b/src/redis/test/Cases/HashTest.php index 62254ddb8..0f4d88141 100644 --- a/src/redis/test/Cases/HashTest.php +++ b/src/redis/test/Cases/HashTest.php @@ -26,6 +26,18 @@ public function testHmsetAndHmget() ]; $values = $this->redis->hMGet($key, ['NotExistKey', 'NotExistKey2']); $this->assertEquals($data, $values); + + $this->redis->set($key, 'xxxxx'); + $result = $this->redis->hMGet($key,['key']); + $this->assertFalse($result); + + $this->redis->delete($key); + $result = $this->redis->hMGet($key, ['key']); + $this->assertEquals(['key' => false], $result); + + $this->redis->sAdd($key, 'xxxxx'); + $result = $this->redis->hMGet($key, ['key']); + $this->assertFalse($result); } public function testHmsetAndHmgetByCo() @@ -43,6 +55,18 @@ public function testHGetAll() $result = $this->redis->hGetAll($key); $this->assertEquals(['key' => 'value', 'key2' => 'value2', 'key3' => 'value3'], $result); + + $this->redis->set($key, 'xxxxx'); + $result = $this->redis->hGetAll($key); + $this->assertFalse($result); + + $this->redis->delete($key); + $result = $this->redis->hGetAll($key); + $this->assertEquals([], $result); + + $this->redis->sAdd($key, 'xxxxx'); + $result = $this->redis->hGetAll($key); + $this->assertFalse($result); } public function testHGetAllByCo() diff --git a/src/redis/test/Cases/ListTest.php b/src/redis/test/Cases/ListTest.php index eab9bf495..cf4fcbb79 100644 --- a/src/redis/test/Cases/ListTest.php +++ b/src/redis/test/Cases/ListTest.php @@ -268,7 +268,7 @@ public function testblPop() }); go(function () use ($key, $expected) { - \co::sleep(3.0); + \co::sleep(2.0); $this->redis->lPush($key, $expected); }); }); diff --git a/src/redis/test/Cases/PoolTest.php b/src/redis/test/Cases/PoolTest.php index 6443d3cbf..3f9c0e834 100644 --- a/src/redis/test/Cases/PoolTest.php +++ b/src/redis/test/Cases/PoolTest.php @@ -4,6 +4,7 @@ use Swoft\App; use Swoft\Redis\Exception\RedisException; +use Swoft\Redis\Redis; use SwoftTest\Redis\Pool\RedisEnvPoolConfig; use SwoftTest\Redis\Pool\RedisPptPoolConfig; use SwoftTest\Redis\Testing\Clients\TimeoutRedis; @@ -73,4 +74,16 @@ public function testRedisTimeout() }); } + + public function testRedisReconnectSelectDb() + { + $redis = bean(Redis::class); + $redis->set('test_select_db', 1); + + $redis->reconnect(); + + $res = $redis->get('test_select_db'); + + $this->assertEquals(1, $res); + } } \ No newline at end of file diff --git a/src/redis/test/Cases/StringTest.php b/src/redis/test/Cases/StringTest.php index 072fa7da5..35a5b6258 100644 --- a/src/redis/test/Cases/StringTest.php +++ b/src/redis/test/Cases/StringTest.php @@ -3,6 +3,7 @@ namespace SwoftTest\Redis; use Swoft\App; +use Swoole\Coroutine\Redis; /** * StringTest @@ -12,7 +13,7 @@ class StringTest extends AbstractTestCase public function testSet() { $value = uniqid(); - $key = 'stringKey'; + $key = 'stringKey'; if (App::isCoContext()) { $key .= 'co'; } @@ -40,7 +41,7 @@ public function testSetByCo() public function testGet() { $default = 'defualtValue'; - $result = $this->redis->get("notKey" . uniqid(), $default); + $result = $this->redis->get("notKey" . uniqid(), $default); $this->assertSame($result, $default); } @@ -53,13 +54,13 @@ public function testGetByCo() public function testMsetAndMget() { - $key = uniqid(); - $key2 = uniqid(); - $value = 'value1'; + $key = uniqid(); + $key2 = uniqid(); + $value = 'value1'; $value2 = 'val2'; $result = $this->redis->mset([ - $key => $value, + $key => $value, $key2 => $value2, ]); @@ -76,4 +77,37 @@ public function testMsetAndMgetByCo() $this->testMsetAndMget(); }); } + + public function testHyperLoglog() + { + $this->redis->delete('pf:test'); + $this->redis->delete('pf:test2'); + $this->redis->delete('pf:test3'); + + $result = $this->redis->pfAdd('pf:test', [1, 2, 3]); + + $this->assertEquals(1, $result); + + $result = $this->redis->pfCount('pf:test'); + $this->assertEquals(3, $result); + + $result = $this->redis->pfAdd('pf:test2', [3, 4, 5]); + $this->assertEquals(1, $result); + + $result = $this->redis->pfMerge('pf:test3', ['pf:test', 'pf:test2']); + $this->assertTrue($result); + + $result = $this->redis->pfCount('pf:test3'); + $this->assertEquals(5, $result); + + $result = $this->redis->pfCount(['pf:test', 'pf:test2']); + $this->assertEquals(5, $result); + } + + public function testHyperLoglogByCo() + { + go(function () { + $this->testHyperLoglog(); + }); + } } \ No newline at end of file diff --git a/src/redis/test/Cases/ZsetTest.php b/src/redis/test/Cases/ZsetTest.php index 66ada5d50..e6c6e8d26 100644 --- a/src/redis/test/Cases/ZsetTest.php +++ b/src/redis/test/Cases/ZsetTest.php @@ -91,6 +91,33 @@ public function testZadd() $this->assertEquals(['key3' => 3.2, 'key2' => 1.3, 'key4' => 1.2], $rangeKeys); } + public function testZInter() + { + $key1 = 'test:zinter:1'; + $key2 = 'test:zinter:2'; + $output = 'test:zinter:output'; + + $this->redis->zAdd($key1, 0, 'i0'); + $this->redis->zAdd($key1, 1, 'i1'); + $this->redis->zAdd($key1, 1, 'i2'); + + $this->redis->zAdd($key2, 2, 'i2'); + $this->redis->zAdd($key2, 2, 'i3'); + + $res = $this->redis->zInter($output, [$key1, $key2], [1, 2]); + $this->assertEquals(1, $res); + + $res = $this->redis->zRange($output, 0, -1, true); + $this->assertEquals(['i2' => 5], $res); + } + + public function testZInterByCo() + { + go(function () { + $this->testZInter(); + }); + } + public function testZaddByCo() { go(function () { diff --git a/src/rpc-client/.travis.yml b/src/rpc-client/.travis.yml index eb2488cdc..8bfad31f9 100644 --- a/src/rpc-client/.travis.yml +++ b/src/rpc-client/.travis.yml @@ -8,13 +8,13 @@ php: install: - wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.gz && mkdir -p hiredis && tar -xf hiredis.tar.gz -C hiredis --strip-components=1 && cd hiredis && sudo make -j$(nproc) && sudo make install && sudo ldconfig && cd .. - echo 'no' | pecl install -f redis - - wget https://github.com/swoole/swoole-src/archive/v4.0.2.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure --enable-async-redis && make -j$(nproc) && make install && cd - + - wget https://github.com/swoole/swoole-src/archive/v4.0.3.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure --enable-async-redis && make -j$(nproc) && make install && cd - - echo "extension = swoole.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini before_script: - phpenv config-rm xdebug.ini - composer update - - php test/server.php & + - php test/server.php -d - sleep 10 script: composer test diff --git a/src/rpc-client/src/Exception/RpcClientException.php b/src/rpc-client/src/Exception/RpcClientException.php index 87b178b7e..485010001 100644 --- a/src/rpc-client/src/Exception/RpcClientException.php +++ b/src/rpc-client/src/Exception/RpcClientException.php @@ -15,5 +15,4 @@ */ class RpcClientException extends Exception { - } \ No newline at end of file diff --git a/src/rpc-client/src/Service.php b/src/rpc-client/src/Service.php index a8a094970..3131c50c7 100644 --- a/src/rpc-client/src/Service.php +++ b/src/rpc-client/src/Service.php @@ -81,7 +81,8 @@ class Service public function call(string $func, array $params) { $profileKey = $this->interface . '->' . $func; - $fallback = $this->getFallbackHandler($func); + $fallback = $this->getFallbackHandler($func); + $closeStatus = true; try { $connectPool = $this->getPool(); @@ -101,16 +102,48 @@ public function call(string $func, array $params) } App::profileStart($profileKey); - $result = $client->receive(); + try { + $result = $client->receive(); + } catch (\Throwable $ex) { + // Client is not connected to server + if ($ex instanceof RpcClientException && in_array($ex->getCode(), [0, 5001, 104])) { + // 0 Send failed, recv data is empty + // 104 Connection reset by peer + // 5001 SW_ERROR_CLIENT_NO_CONNECTION + App::warning(sprintf('%s call %s retried, data=%s, message=%s, code=%s', + $this->interface, + $func, + json_encode($data, JSON_UNESCAPED_UNICODE), + $ex->getMessage(), + $ex->getCode() + )); + $client->reconnect(); + $circuitBreaker->call([$client, 'send'], [$packData], $fallback); + $result = $client->receive(); + } else { + throw $ex; + } + } + App::profileEnd($profileKey); $client->release(true); App::debug(sprintf('%s call %s success, data=%s', $this->interface, $func, \json_encode($data, JSON_UNESCAPED_UNICODE))); + $result = $packer->unpack($result); - $data = $packer->checkData($result); + $closeStatus = false; + $data = $packer->checkData($result); } catch (\Throwable $throwable) { - if (isset($client) && $client instanceof AbstractServiceConnection) { + // If the client is normal, no need to close it. + if ($closeStatus && isset($client) && $client instanceof AbstractServiceConnection) { $client->close(); + App::error(sprintf('%s call %s failed, data=%s, message=%s, code=%s', + $this->interface, + $func, + json_encode($data, JSON_UNESCAPED_UNICODE), + $throwable->getMessage(), + $throwable->getCode() + )); } if (empty($fallback)) { throw $throwable; diff --git a/src/rpc-client/src/Service/ServiceConnection.php b/src/rpc-client/src/Service/ServiceConnection.php index cdce70522..7c039a5f3 100644 --- a/src/rpc-client/src/Service/ServiceConnection.php +++ b/src/rpc-client/src/Service/ServiceConnection.php @@ -79,7 +79,8 @@ public function recv(): string { $data = $this->connection->recv(); if (empty($data)) { - throw new RpcClientException('ServiceConnection::recv error, errCode=' . $this->connection->errCode); + $errCode = intval($this->connection->errCode); + throw new RpcClientException('ServiceConnection::recv error, errCode=' . $errCode, $errCode); } return $data; } @@ -100,6 +101,6 @@ public function getTcpClientSetting(): array */ public function close() { - return $this->connection->close(); + return $this->connection->close(true); } } diff --git a/src/rpc-client/test/Cases/AbstractTestCase.php b/src/rpc-client/test/Cases/AbstractTestCase.php index 30ed2e35f..9ac9d29bb 100644 --- a/src/rpc-client/test/Cases/AbstractTestCase.php +++ b/src/rpc-client/test/Cases/AbstractTestCase.php @@ -16,7 +16,7 @@ class AbstractTestCase extends TestCase protected function tearDown() { parent::tearDown(); - swoole_timer_after(6 * 1000, function () { + swoole_timer_after(10 * 1000, function () { swoole_event_exit(); }); } diff --git a/src/rpc-client/test/Cases/RpcTest.php b/src/rpc-client/test/Cases/RpcTest.php index ccc7f146d..fef801c52 100644 --- a/src/rpc-client/test/Cases/RpcTest.php +++ b/src/rpc-client/test/Cases/RpcTest.php @@ -4,6 +4,7 @@ use Swoft\Rpc\Client\Bean\Collector\ReferenceCollector; use Swoft\Rpc\Client\Service\ServiceProxy; +use SwoftTest\Rpc\Testing\Clients\Demo8098ServiceClient; use SwoftTest\Rpc\Testing\Clients\DemoServiceClient; use SwoftTest\Rpc\Testing\Lib\DemoServiceInterface; use SwoftTest\Rpc\Testing\Pool\Config\DemoServicePoolConfig; @@ -15,15 +16,19 @@ public function testDemo() $this->assertTrue(true); } - public function testVersion() + public function testRpc() { $client = bean(DemoServiceClient::class); $this->assertEquals('1.0.0', $client->version()); - } - public function testLongMessageByCo() - { + // SyncServiceConnection not timeout + $client = bean(DemoServiceClient::class); + $id = rand(1000, 9999); + $res = $client->get($id); + $this->assertEquals($id, $res); + go(function () { + // Test Long Message $client = bean(DemoServiceClient::class); $string = 'Hi, Agnes! '; $str = $client->longMessage($string); @@ -32,19 +37,8 @@ public function testLongMessageByCo() $expect .= $string; } $this->assertEquals($expect, $str); - }); - } - - public function testRpcServiceTimeout() - { - go(function () { - $client = bean(DemoServiceClient::class); - $id = rand(1000, 9999); - $res = $client->get($id); - $this->assertEquals('', $res); - - \co::sleep(1); + // Test Tcp Timeout go(function () { $client = bean(DemoServiceClient::class); $id = rand(1000, 9999); @@ -52,14 +46,16 @@ public function testRpcServiceTimeout() $this->assertEquals('', $res); }); + \co::sleep(2); + + // Test Rpc Server Restart + $cmd = 'php ' . alias('@root') . '/server.php -d'; + \co::exec($cmd); \co::sleep(1); - go(function () { - $client = bean(DemoServiceClient::class); - $id = rand(1000, 9999); - $res = $client->get($id); - $this->assertEquals('', $res); - }); + $client = bean(DemoServiceClient::class); + $res = $client->version(); + $this->assertEquals('1.0.0', $res); }); } } \ No newline at end of file diff --git a/src/rpc-client/test/Testing/Services/DemoService.php b/src/rpc-client/test/Testing/Services/DemoService.php index 901ee00f2..35b7ebb53 100644 --- a/src/rpc-client/test/Testing/Services/DemoService.php +++ b/src/rpc-client/test/Testing/Services/DemoService.php @@ -31,7 +31,7 @@ public function longMessage($string) public function get($id) { - sleep(2); + \co::sleep(2); return $id; } } \ No newline at end of file diff --git a/src/rpc-client/test/config/beans/log.php b/src/rpc-client/test/config/beans/log.php index d0dde4936..efe39842c 100644 --- a/src/rpc-client/test/config/beans/log.php +++ b/src/rpc-client/test/config/beans/log.php @@ -20,12 +20,14 @@ \Swoft\Log\Logger::WARNING ] ], - 'logger' => [ - 'class' => \Swoft\Log\Logger::class, - 'name' => APP_NAME, - 'flushInterval' => 100, - 'flushRequest' => true, - 'handlers' => [ + + "logger" => [ + "class" => \Swoft\Log\Logger::class, + "name" => APP_NAME, + "enable" => true, + "flushInterval" => 1, + "flushRequest" => true, + "handlers" => [ '${noticeHandler}', '${applicationHandler}' ] diff --git a/src/rpc-client/test/server.php b/src/rpc-client/test/server.php index 191974a20..dcf0601d8 100644 --- a/src/rpc-client/test/server.php +++ b/src/rpc-client/test/server.php @@ -6,4 +6,7 @@ $command = bean(RpcCommand::class); -$command->start(); \ No newline at end of file +$dir = alias('@runtime/logs'); +@mkdir($dir, 0777, true); + +$command->restart(); \ No newline at end of file diff --git a/src/session/src/SessionManager.php b/src/session/src/SessionManager.php index 7968e44f9..343ef40ff 100644 --- a/src/session/src/SessionManager.php +++ b/src/session/src/SessionManager.php @@ -5,6 +5,7 @@ use Swoft\App; use Swoft\Bean\Annotation\Bean; use Swoft\Bean\Annotation\Scope; +use Swoft\Core\RequestContext; use Swoft\Session\Handler\FileSessionHandler; use Swoft\Session\Handler\LifetimeInterface; use Swoft\Session\Handler\RedisSessionHandler; @@ -14,6 +15,8 @@ */ class SessionManager { + const SESSION_KEY = 'session'; + /** * The session handlers * @@ -29,11 +32,6 @@ class SessionManager */ protected $config = []; - /** - * @var SessionInterface - */ - protected $session; - /** * Create a handler by config * @@ -99,10 +97,7 @@ public function setConfig(array $config): self */ public function getSession(): SessionInterface { - if (!$this->session instanceof SessionInterface) { - throw new \RuntimeException('Did you maybe forget to configure session middleware ?'); - } - return $this->session; + return RequestContext::getContextDataByKey(self::SESSION_KEY); } /** @@ -111,8 +106,7 @@ public function getSession(): SessionInterface */ public function setSession($session): self { - $this->session = $session; + RequestContext::setContextDataByKey(self::SESSION_KEY, $session); return $this; } - } diff --git a/src/session/test/Cases/AbstractTestCase.php b/src/session/test/Cases/AbstractTestCase.php index 78d75d902..2cd3f237a 100644 --- a/src/session/test/Cases/AbstractTestCase.php +++ b/src/session/test/Cases/AbstractTestCase.php @@ -13,5 +13,11 @@ */ class AbstractTestCase extends TestCase { - + protected function tearDown() + { + parent::tearDown(); + swoole_timer_after(6 * 1000, function () { + swoole_event_exit(); + }); + } } \ No newline at end of file diff --git a/src/session/test/Cases/SessionTest.php b/src/session/test/Cases/SessionTest.php new file mode 100644 index 000000000..425e0b913 --- /dev/null +++ b/src/session/test/Cases/SessionTest.php @@ -0,0 +1,46 @@ +createHandlerByConfig(); + return new SessionStore($name, $handler, $id); + } + + public function testSetGetSession() + { + /** @var SessionManager $manager */ + $manager = bean('sessionManager'); + + $session = $this->getSession(); + $manager->setSession($session); + $this->assertEquals($session, $manager->getSession()); + + go(function () use ($manager) { + $session = $this->getSession(); + $manager->setSession($session); + \co::sleep(0.5); + $this->assertEquals($session, $manager->getSession()); + }); + + go(function () use ($manager) { + $session = $this->getSession(); + $manager->setSession($session); + \co::sleep(0.5); + $this->assertEquals($session, $manager->getSession()); + }); + } +} \ No newline at end of file diff --git a/src/session/test/config/properties/app.php b/src/session/test/config/properties/app.php new file mode 100644 index 000000000..13caa1261 --- /dev/null +++ b/src/session/test/config/properties/app.php @@ -0,0 +1,18 @@ + '1.0', + 'autoInitBean' => true, + 'components' => [ + 'custom' => [ + 'Swoft\\Session' => BASE_PATH . '/../src', + ], + ], +];