From 004bb3365209e5ba3a794ae2201cb7acdc6cb6cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Tue, 23 Jul 2019 14:53:27 +0800 Subject: [PATCH 01/36] Added CoroutineMemory Driver. --- src/cache/src/Collector/CoroutineMemory.php | 38 ++++++ .../src/Collector/CoroutineMemoryKey.php | 21 ++++ .../src/Driver/CoroutineMemoryDriver.php | 115 ++++++++++++++++++ 3 files changed, 174 insertions(+) create mode 100644 src/cache/src/Collector/CoroutineMemory.php create mode 100644 src/cache/src/Collector/CoroutineMemoryKey.php create mode 100644 src/cache/src/Driver/CoroutineMemoryDriver.php diff --git a/src/cache/src/Collector/CoroutineMemory.php b/src/cache/src/Collector/CoroutineMemory.php new file mode 100644 index 000000000..67840f9a7 --- /dev/null +++ b/src/cache/src/Collector/CoroutineMemory.php @@ -0,0 +1,38 @@ +items = []; + } + + public function clearPrefix(string $prefix) + { + foreach ($this->items as $key => $item) { + if (Str::startsWith($prefix, $key)) { + unset($this->items[$key]); + } + } + + return true; + } +} diff --git a/src/cache/src/Collector/CoroutineMemoryKey.php b/src/cache/src/Collector/CoroutineMemoryKey.php new file mode 100644 index 000000000..fce8ed2bc --- /dev/null +++ b/src/cache/src/Collector/CoroutineMemoryKey.php @@ -0,0 +1,21 @@ +getCollection()->get($key, $default); + } + + public function set($key, $value, $ttl = null) + { + return $this->getCollection()->offsetSet($key, $value); + } + + public function delete($key) + { + return $this->getCollection()->offsetUnset($key); + } + + public function clear() + { + return $this->getCollection()->clear(); + } + + public function getMultiple($keys, $default = null) + { + $result = []; + foreach ($keys as $key) { + $result[$key] = $this->get($key, $default); + } + + return $result; + } + + public function setMultiple($values, $ttl = null) + { + foreach ($values as $key => $value) { + $this->set($key, $values, $ttl); + } + } + + public function deleteMultiple($keys) + { + foreach ($keys as $key) { + $this->delete($key); + } + } + + public function has($key) + { + return $this->getCollection()->has($key); + } + + public function fetch(string $key, $default = null): array + { + if (! $this->has($key)) { + return [false, $default]; + } + + return [true, $this->get($key)]; + } + + public function clearPrefix(string $prefix): bool + { + return $this->getCollection()->clearPrefix($prefix); + } + + public function addKey(string $collector, string $key): bool + { + $instance = CoroutineMemoryKey::instance(); + $data = $instance->get($collector, []); + $data[] = $key; + $instance->put($collector, $data); + + return true; + } + + public function keys(string $collector): array + { + return CoroutineMemoryKey::instance()->get($collector, []); + } + + public function delKey(string $collector, ...$key): bool + { + $instance = CoroutineMemoryKey::instance(); + $result = []; + $data = $instance->get($collector, []); + foreach ($data as $item) { + if (! in_array($item, $key)) { + $result[] = $item; + } + } + $instance->put($collector, $result); + } + + protected function getCollection() + { + return CoroutineMemory::instance(); + } +} From efbd5d14ac586d753c91b1fd35ee44b48d8bdc67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Tue, 23 Jul 2019 15:03:25 +0800 Subject: [PATCH 02/36] Added testing. --- composer.json | 3 +- phpunit.xml | 1 + .../tests/Cases/CoroutineMemoryDriverTest.php | 48 +++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/cache/tests/Cases/CoroutineMemoryDriverTest.php diff --git a/composer.json b/composer.json index 8e93b43b0..5a818ee0c 100644 --- a/composer.json +++ b/composer.json @@ -150,6 +150,7 @@ "autoload-dev": { "psr-4": { "HyperfTest\\AsyncQueue\\": "./src/async-queue/tests/", + "HyperfTest\\Cache\\": "./src/cache/tests/", "HyperfTest\\ConfigApollo\\": "./src/config-apollo/tests/", "HyperfTest\\Constants\\": "./src/constants/tests/", "HyperfTest\\Consul\\": "./src/consul/tests/", @@ -182,8 +183,8 @@ "config": [ "Hyperf\\Amqp\\ConfigProvider", "Hyperf\\AsyncQueue\\ConfigProvider", - "Hyperf\\CircuitBreaker\\ConfigProvider", "Hyperf\\Cache\\ConfigProvider", + "Hyperf\\CircuitBreaker\\ConfigProvider", "Hyperf\\Config\\ConfigProvider", "Hyperf\\ConfigApollo\\ConfigProvider", "Hyperf\\Devtool\\ConfigProvider", diff --git a/phpunit.xml b/phpunit.xml index 33c56e412..f1418a040 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -11,6 +11,7 @@ ./src/async-queue/tests + ./src/cache/tests ./src/constants/tests ./src/consul/tests ./src/database/tests diff --git a/src/cache/tests/Cases/CoroutineMemoryDriverTest.php b/src/cache/tests/Cases/CoroutineMemoryDriverTest.php new file mode 100644 index 000000000..4d7eee74a --- /dev/null +++ b/src/cache/tests/Cases/CoroutineMemoryDriverTest.php @@ -0,0 +1,48 @@ +shouldReceive('get')->with(PhpSerializerPacker::class)->andReturn(new PhpSerializerPacker()); + + $driver = new CoroutineMemoryDriver($container, []); + $this->assertSame(null, $driver->get('test', null)); + $driver->set('test', 'xxx'); + $this->assertSame('xxx', $driver->get('test', null)); + + parallel([function () use ($driver) { + $this->assertSame(null, $driver->get('test', null)); + $driver->set('test', 'xxx2'); + $this->assertSame('xxx2', $driver->get('test', null)); + }]); + } +} From 5bb7aca285effeeba60a682c6d4362ab4e817638 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Tue, 23 Jul 2019 16:55:08 +0800 Subject: [PATCH 03/36] Add support for array value of RequestMapping::$methods --- .../src/Annotation/RequestMapping.php | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/http-server/src/Annotation/RequestMapping.php b/src/http-server/src/Annotation/RequestMapping.php index 99bb53bf0..17da7155c 100644 --- a/src/http-server/src/Annotation/RequestMapping.php +++ b/src/http-server/src/Annotation/RequestMapping.php @@ -20,6 +20,21 @@ use Hyperf\Utils\Str; */ class RequestMapping extends Mapping { + + public const GET = 'GET'; + + public const POST = 'POST'; + + public const PUT = 'PUT'; + + public const PATCH = 'PATCH'; + + public const DELETE = 'DELETE'; + + public const HEADER = 'HEADER'; + + public const OPTIONS = 'OPTIONS'; + /** * @var array */ @@ -29,8 +44,12 @@ class RequestMapping extends Mapping { parent::__construct($value); if (isset($value['methods'])) { - // Explode a string to a array - $this->methods = explode(',', Str::upper(str_replace(' ', '', $value['methods']))); + if (is_string($value['methods'])) { + // Explode a string to a array + $this->methods = explode(',', Str::upper(str_replace(' ', '', $value['methods']))); + } else { + $this->methods = $value['methods']; + } } } } From 106f458f859697c4e8e7806ff0e3ad635e6f8464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Tue, 23 Jul 2019 16:59:09 +0800 Subject: [PATCH 04/36] Fixed render not implement interface. --- src/view/src/Render.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/src/Render.php b/src/view/src/Render.php index e7a97f273..3760b1188 100644 --- a/src/view/src/Render.php +++ b/src/view/src/Render.php @@ -20,7 +20,7 @@ use Hyperf\View\Engine\SmartyEngine; use Hyperf\View\Exception\EngineNotFindException; use Psr\Container\ContainerInterface; -class Render +class Render implements RenderInterface { /** * @var ContainerInterface From 536ef17c1937c44bbba886ba1ccee79ffb00b1ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Tue, 23 Jul 2019 17:00:36 +0800 Subject: [PATCH 05/36] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28daefe7b..7f2431fd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - [#235](https://github.com/hyperf-cloud/hyperf/pull/235) Added default exception handler for `grpc-server` and optimized code. - [#240](https://github.com/hyperf-cloud/hyperf/pull/240) Fixed OnPipeMessage event will be dispatch by another listener. +- [#252](https://github.com/hyperf-cloud/hyperf/pull/252) Fixed view render not implement interface. # v1.0.5 - 2019-07-07 From 4654db3304e14519f2f79ea7217b080c9c4342e8 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Tue, 23 Jul 2019 17:21:44 +0800 Subject: [PATCH 06/36] Optimized --- src/di/src/Annotation/AbstractAnnotation.php | 2 +- src/http-server/src/Annotation/RequestMapping.php | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/di/src/Annotation/AbstractAnnotation.php b/src/di/src/Annotation/AbstractAnnotation.php index 2e9cd0ad7..5d9c1f99b 100644 --- a/src/di/src/Annotation/AbstractAnnotation.php +++ b/src/di/src/Annotation/AbstractAnnotation.php @@ -54,7 +54,7 @@ abstract class AbstractAnnotation implements AnnotationInterface, Arrayable AnnotationCollector::collectProperty($className, $target, static::class, $this); } - protected function bindMainProperty(string $key, array $value) + protected function bindMainProperty(string $key, ?array $value) { if (isset($value['value'])) { $this->{$key} = $value['value']; diff --git a/src/http-server/src/Annotation/RequestMapping.php b/src/http-server/src/Annotation/RequestMapping.php index 17da7155c..3c93b15b6 100644 --- a/src/http-server/src/Annotation/RequestMapping.php +++ b/src/http-server/src/Annotation/RequestMapping.php @@ -48,7 +48,11 @@ class RequestMapping extends Mapping // Explode a string to a array $this->methods = explode(',', Str::upper(str_replace(' ', '', $value['methods']))); } else { - $this->methods = $value['methods']; + $methods = []; + foreach ($value['methods'] as $method) { + $methods[] = Str::upper(str_replace(' ', '', $method)); + } + $this->methods = $methods; } } } From d52126b4bb5dc15a2b399a54fd106e4d4b416f21 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Tue, 23 Jul 2019 17:21:49 +0800 Subject: [PATCH 07/36] Create MappingAnnotationTest.php --- .../tests/MappingAnnotationTest.php | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/http-server/tests/MappingAnnotationTest.php diff --git a/src/http-server/tests/MappingAnnotationTest.php b/src/http-server/tests/MappingAnnotationTest.php new file mode 100644 index 000000000..fd87e6c8d --- /dev/null +++ b/src/http-server/tests/MappingAnnotationTest.php @@ -0,0 +1,54 @@ +assertSame(['GET', 'POST'], $mapping->methods); + $this->assertNull($mapping->path); + + // Normal case + $mapping = new RequestMapping([ + 'methods' => 'get,post,put', + 'path' => $path = '/foo', + ]); + $this->assertSame(['GET', 'POST', 'PUT'], $mapping->methods); + $this->assertSame($path, $mapping->path); + + // The methods have space + $mapping = new RequestMapping([ + 'methods' => 'get, post, put', + 'path' => $path, + ]); + $this->assertSame(['GET', 'POST', 'PUT'], $mapping->methods); + $this->assertSame($path, $mapping->path); + } + + public function testRequestMappingWithArrayMethods() + { + $mapping = new RequestMapping([ + 'methods' => [ + 'GET', 'POST ', 'put' + ], + 'path' => $path = '/foo', + ]); + $this->assertSame(['GET', 'POST', 'PUT'], $mapping->methods); + $this->assertSame($path, $mapping->path); + } + + public function testRequestMappingBindMainProperty() + { + $mapping = new RequestMapping(['value' => '/foo']); + $this->assertSame(['GET', 'POST'], $mapping->methods); + $this->assertSame('/foo', $mapping->path); + } +} \ No newline at end of file From 734ff3308fb116315b05e39cf3f3c5b392809cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Tue, 23 Jul 2019 17:32:25 +0800 Subject: [PATCH 08/36] Update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f2431fd3..28daefe7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,6 @@ - [#235](https://github.com/hyperf-cloud/hyperf/pull/235) Added default exception handler for `grpc-server` and optimized code. - [#240](https://github.com/hyperf-cloud/hyperf/pull/240) Fixed OnPipeMessage event will be dispatch by another listener. -- [#252](https://github.com/hyperf-cloud/hyperf/pull/252) Fixed view render not implement interface. # v1.0.5 - 2019-07-07 From 7970bb97f7d59452f6aeed4f7f013ab423f7df24 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Tue, 23 Jul 2019 17:32:31 +0800 Subject: [PATCH 09/36] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28daefe7b..2a13f9ceb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Added - [#245](https://github.com/hyperf-cloud/hyperf/pull/245) Added TaskWorkerStrategy and WorkerStrategy crontab strategies. +- [#254](https://github.com/hyperf-cloud/hyperf/pull/254) Added support for array value of `RequestMapping::$methods`, `@RequestMapping(methods={"GET"})` and `@RequestMapping(methods={RequestMapping::GET})` is available now. ## Changed From 1bae450336674e2f30784f6ee8c49741fc797c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Tue, 23 Jul 2019 19:13:59 +0800 Subject: [PATCH 10/36] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a13f9ceb..b7b2dc81a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Added - [#245](https://github.com/hyperf-cloud/hyperf/pull/245) Added TaskWorkerStrategy and WorkerStrategy crontab strategies. -- [#254](https://github.com/hyperf-cloud/hyperf/pull/254) Added support for array value of `RequestMapping::$methods`, `@RequestMapping(methods={"GET"})` and `@RequestMapping(methods={RequestMapping::GET})` is available now. +- [#254](https://github.com/hyperf-cloud/hyperf/pull/254) Added support for array value of `RequestMapping::$methods`, `@RequestMapping(methods={"GET"})` and `@RequestMapping(methods={RequestMapping::GET})` are available now. ## Changed From cb33983ef10ae3bc10ea318b1141f6ac7c3c1384 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Tue, 23 Jul 2019 19:42:46 +0800 Subject: [PATCH 11/36] Update config.md --- doc/zh/config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/zh/config.md b/doc/zh/config.md index 34c88b61f..b93e04b55 100644 --- a/doc/zh/config.md +++ b/doc/zh/config.md @@ -1,6 +1,6 @@ # 配置 -当您使用的是 [hyperf-cloud/hyperf-skeleton](https://github.com/hyperf-cloud/hyperf-skeleton) 项目创建的项目时,或基于 [hyperf-cloud/installer](https://github.com/hyperf-cloud/installer) 创建的项目,Hyperf 的所有配置文件均处于根目录下的 `config` 文件夹内,每个选项都有说明,您可以随时查看并熟悉有哪些选项可以使用。 +当您使用的是 [hyperf-cloud/hyperf-skeleton](https://github.com/hyperf-cloud/hyperf-skeleton) 项目创建的项目时,Hyperf 的所有配置文件均处于根目录下的 `config` 文件夹内,每个选项都有说明,您可以随时查看并熟悉有哪些选项可以使用。 # 安装 From de17fc6afb90d0a03246c10b2b38a7034ad724ff Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Tue, 23 Jul 2019 20:10:10 +0800 Subject: [PATCH 12/36] Transfer Arrayable to Response, and add text/plain content-type header for string response --- src/http-server/src/CoreMiddleware.php | 11 ++-- src/http-server/tests/CoreMiddlewareTest.php | 60 +++++++++++++++++++ .../tests/Stub/CoreMiddlewareStub.php | 8 +++ 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/http-server/src/CoreMiddleware.php b/src/http-server/src/CoreMiddleware.php index 336c11842..0a1ce5640 100644 --- a/src/http-server/src/CoreMiddleware.php +++ b/src/http-server/src/CoreMiddleware.php @@ -146,15 +146,18 @@ class CoreMiddleware implements MiddlewareInterface /** * Transfer the non-standard response content to a standard response object. * - * @param array|string $response + * @param array|string|Jsonable|Arrayable $response */ protected function transferToResponse($response, ServerRequestInterface $request): ResponseInterface { if (is_string($response)) { - return $this->response()->withBody(new SwooleStream($response)); + return $this->response()->withAddedHeader('content-type', 'text/plain')->withBody(new SwooleStream($response)); } - if (is_array($response)) { + if (is_array($response) || $response instanceof Arrayable) { + if ($response instanceof Arrayable) { + $response = $response->toArray(); + } return $this->response() ->withAddedHeader('content-type', 'application/json') ->withBody(new SwooleStream(json_encode($response, JSON_UNESCAPED_UNICODE))); @@ -166,7 +169,7 @@ class CoreMiddleware implements MiddlewareInterface ->withBody(new SwooleStream((string) $response)); } - return $this->response()->withBody(new SwooleStream((string) $response)); + return $this->response()->withAddedHeader('content-type', 'text/plain')->withBody(new SwooleStream((string) $response)); } /** diff --git a/src/http-server/tests/CoreMiddlewareTest.php b/src/http-server/tests/CoreMiddlewareTest.php index 15aa157ed..96568d087 100644 --- a/src/http-server/tests/CoreMiddlewareTest.php +++ b/src/http-server/tests/CoreMiddlewareTest.php @@ -12,12 +12,18 @@ declare(strict_types=1); namespace HyperfTest\HttpServer; +use Hyperf\HttpServer\CoreMiddleware; use Hyperf\HttpServer\Router\DispatcherFactory; +use Hyperf\Utils\Contracts\Arrayable; +use Hyperf\Utils\Contracts\Jsonable; use HyperfTest\HttpServer\Stub\CoreMiddlewareStub; use HyperfTest\HttpServer\Stub\DemoController; use Mockery; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use ReflectionMethod; /** * @internal @@ -35,6 +41,60 @@ class CoreMiddlewareTest extends TestCase $this->assertSame([$id, 'Hyperf', []], $params); } + public function testTransferToResponse() + { + $middleware = new CoreMiddlewareStub($container = $this->getContainer(), 'http'); + $reflectionMethod = new ReflectionMethod(CoreMiddleware::class, 'transferToResponse'); + $reflectionMethod->setAccessible(true); + $request = Mockery::mock(ServerRequestInterface::class); + /** @var ResponseInterface $response */ + + // String + $response = $reflectionMethod->invoke($middleware, $body = 'foo', $request); + $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertSame($body, $response->getBody()->getContents()); + $this->assertSame('text/plain', $response->getHeaderLine('content-type')); + + // Array + $response = $reflectionMethod->invoke($middleware, $body = ['foo' => 'bar'], $request); + $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertSame(json_encode($body), $response->getBody()->getContents()); + $this->assertSame('application/json', $response->getHeaderLine('content-type')); + + // Arrayable + $response = $reflectionMethod->invoke($middleware, new class() implements Arrayable { + public function toArray(): array + { + return ['foo' => 'bar']; + } + }, $request); + $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertSame(json_encode(['foo' => 'bar']), $response->getBody()->getContents()); + $this->assertSame('application/json', $response->getHeaderLine('content-type')); + + // Jsonable + $response = $reflectionMethod->invoke($middleware, new class() implements Jsonable { + public function __toString(): string + { + return json_encode(['foo' => 'bar'], JSON_UNESCAPED_UNICODE); + } + }, $request); + $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertSame(json_encode(['foo' => 'bar']), $response->getBody()->getContents()); + $this->assertSame('application/json', $response->getHeaderLine('content-type')); + + // __toString + $response = $reflectionMethod->invoke($middleware, new class() { + public function __toString(): string + { + return 'This is a string'; + } + }, $request); + $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertSame('This is a string', $response->getBody()->getContents()); + $this->assertSame('text/plain', $response->getHeaderLine('content-type')); + } + protected function getContainer() { $container = Mockery::mock(ContainerInterface::class); diff --git a/src/http-server/tests/Stub/CoreMiddlewareStub.php b/src/http-server/tests/Stub/CoreMiddlewareStub.php index 6b339701c..ebf51c424 100644 --- a/src/http-server/tests/Stub/CoreMiddlewareStub.php +++ b/src/http-server/tests/Stub/CoreMiddlewareStub.php @@ -12,7 +12,9 @@ declare(strict_types=1); namespace HyperfTest\HttpServer\Stub; +use Hyperf\HttpMessage\Server\Response; use Hyperf\HttpServer\CoreMiddleware; +use Psr\Http\Message\ResponseInterface; class CoreMiddlewareStub extends CoreMiddleware { @@ -20,4 +22,10 @@ class CoreMiddlewareStub extends CoreMiddleware { return parent::parseParameters($controller, $action, $arguments); } + + protected function response(): ResponseInterface + { + return new Response(); + } + } From e5ec67d687dd6b58de15445ac2feee2bcf68b588 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Tue, 23 Jul 2019 20:10:16 +0800 Subject: [PATCH 13/36] Optimized --- src/http-server/src/Server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http-server/src/Server.php b/src/http-server/src/Server.php index e2312f62e..49f25abcc 100644 --- a/src/http-server/src/Server.php +++ b/src/http-server/src/Server.php @@ -105,7 +105,7 @@ class Server implements OnRequestInterface, MiddlewareInitializerInterface $psr7Response = $exceptionHandlerDispatcher->dispatch($throwable, $this->exceptionHandlers); } finally { // Send the Response to client. - if (! $psr7Response || ! $psr7Response instanceof Psr7Response) { + if (! isset($psr7Response) || ! $psr7Response instanceof Psr7Response) { return; } $psr7Response->send(); From bee227b21fea0cc807ef94b4cb4c45cf2603a579 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Tue, 23 Jul 2019 20:13:21 +0800 Subject: [PATCH 14/36] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7b2dc81a..41038128f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [#245](https://github.com/hyperf-cloud/hyperf/pull/245) Added TaskWorkerStrategy and WorkerStrategy crontab strategies. - [#254](https://github.com/hyperf-cloud/hyperf/pull/254) Added support for array value of `RequestMapping::$methods`, `@RequestMapping(methods={"GET"})` and `@RequestMapping(methods={RequestMapping::GET})` are available now. +- [#255](https://github.com/hyperf-cloud/hyperf/pull/255) Transfer `Hyperf\Utils\Contracts\Arrayable` result of Request to Response automatically, and added `text/plain` content-type header for string Response. ## Changed From 937b0a8189fb5f78f854d3483ab5fa095c80af4f Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 24 Jul 2019 00:08:38 +0800 Subject: [PATCH 15/36] Create IdGeneratorInterface.php --- src/contract/src/IdGeneratorInterface.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/contract/src/IdGeneratorInterface.php diff --git a/src/contract/src/IdGeneratorInterface.php b/src/contract/src/IdGeneratorInterface.php new file mode 100644 index 000000000..50c9d1cb3 --- /dev/null +++ b/src/contract/src/IdGeneratorInterface.php @@ -0,0 +1,18 @@ + Date: Wed, 24 Jul 2019 00:08:57 +0800 Subject: [PATCH 16/36] Remove useless code --- src/rpc-server/src/CoreMiddleware.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/rpc-server/src/CoreMiddleware.php b/src/rpc-server/src/CoreMiddleware.php index a62c5c680..ad415baad 100644 --- a/src/rpc-server/src/CoreMiddleware.php +++ b/src/rpc-server/src/CoreMiddleware.php @@ -32,11 +32,6 @@ class CoreMiddleware extends \Hyperf\HttpServer\CoreMiddleware $this->dispatcher = $factory->getDispatcher($serverName); } - public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface - { - return parent::process($request, $handler); - } - protected function handleFound(array $routes, ServerRequestInterface $request) { if ($routes[1] instanceof Closure) { From d5b54c3f8be026621c1f558de518be64c897525c Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 24 Jul 2019 00:11:36 +0800 Subject: [PATCH 17/36] Optimized error handling --- src/json-rpc/src/CoreMiddleware.php | 48 +++++++--- src/json-rpc/src/DataFormatter.php | 19 +++- src/json-rpc/src/HttpCoreMiddleware.php | 4 + src/json-rpc/src/HttpServer.php | 16 +++- src/json-rpc/src/ResponseBuilder.php | 89 +++++++++++++++++++ src/rpc-client/src/AbstractServiceClient.php | 31 +++++-- .../src/Contract/DataFormatterInterface.php | 2 + 7 files changed, 188 insertions(+), 21 deletions(-) create mode 100644 src/json-rpc/src/ResponseBuilder.php diff --git a/src/json-rpc/src/CoreMiddleware.php b/src/json-rpc/src/CoreMiddleware.php index cc325685c..ccca01bd3 100644 --- a/src/json-rpc/src/CoreMiddleware.php +++ b/src/json-rpc/src/CoreMiddleware.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Hyperf\JsonRpc; -use Hyperf\HttpMessage\Stream\SwooleStream; +use Closure; use Hyperf\Rpc\ProtocolManager; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; @@ -38,6 +38,11 @@ class CoreMiddleware extends \Hyperf\RpcServer\CoreMiddleware */ protected $packer; + /** + * @var \Hyperf\JsonRpc\ResponseBuilder + */ + protected $responseBuilder; + public function __construct(ContainerInterface $container, string $serverName) { parent::__construct($container, $serverName); @@ -45,18 +50,41 @@ class CoreMiddleware extends \Hyperf\RpcServer\CoreMiddleware $protocolName = 'jsonrpc'; $this->dataFormatter = $container->get($this->protocolManager->getDataFormatter($protocolName)); $this->packer = $container->get($this->protocolManager->getPacker($protocolName)); + $this->responseBuilder = make(ResponseBuilder::class, [ + 'dataFormatter' => $this->dataFormatter, + 'packer' => $this->packer, + ]); + } + + protected function handleFound(array $routes, ServerRequestInterface $request) + { + if ($routes[1] instanceof Closure) { + $response = call($routes[1]); + } else { + [$controller, $action] = $this->prepareHandler($routes[1]); + $controllerInstance = $this->container->get($controller); + if (! method_exists($controller, $action)) { + // Route found, but the handler does not exist. + return $this->responseBuilder->buildErrorResponse($request, -32603); + } + $parameters = $this->parseParameters($controller, $action, $request->getParsedBody()); + $response = $controllerInstance->{$action}(...$parameters); + } + return $response; + } + + protected function handleNotFound(ServerRequestInterface $request) + { + return $this->responseBuilder->buildErrorResponse($request, -32601); + } + + protected function handleMethodNotAllowed(array $routes, ServerRequestInterface $request) + { + return $this->handleNotFound($request); } protected function transferToResponse($response, ServerRequestInterface $request): ResponseInterface { - return $this->response() - ->withAddedHeader('content-type', 'application/json') - ->withBody(new SwooleStream($this->format($response, $request))); - } - - protected function format($response, ServerRequestInterface $request): string - { - $response = $this->dataFormatter->formatResponse([$request->getAttribute('request_id') ?? '', $response]); - return $this->packer->pack($response); + return $this->responseBuilder->buildResponse($request, $response); } } diff --git a/src/json-rpc/src/DataFormatter.php b/src/json-rpc/src/DataFormatter.php index dd72fc60c..b52e72827 100644 --- a/src/json-rpc/src/DataFormatter.php +++ b/src/json-rpc/src/DataFormatter.php @@ -18,11 +18,12 @@ class DataFormatter implements DataFormatterInterface { public function formatRequest($data) { - [$path, $params] = $data; + [$path, $params, $id] = $data; return [ 'jsonrpc' => '2.0', 'method' => $path, 'params' => $params, + 'id' => $id, ]; } @@ -35,4 +36,20 @@ class DataFormatter implements DataFormatterInterface 'result' => $result, ]; } + + public function formatErrorResponse($data) + { + [$id, $code, $message, $data] = $data; + return [ + 'jsonrpc' => '2.0', + 'id' => $id ?? null, + 'error' => [ + 'code' => $code, + 'message' => $message, + 'data' => $data, + ], + ]; + } + + } diff --git a/src/json-rpc/src/HttpCoreMiddleware.php b/src/json-rpc/src/HttpCoreMiddleware.php index 374ff6894..d64a3f6fa 100644 --- a/src/json-rpc/src/HttpCoreMiddleware.php +++ b/src/json-rpc/src/HttpCoreMiddleware.php @@ -28,6 +28,10 @@ class HttpCoreMiddleware extends CoreMiddleware $protocolName = 'jsonrpc-http'; $this->dataFormatter = $container->get($this->protocolManager->getDataFormatter($protocolName)); $this->packer = $container->get($this->protocolManager->getPacker($protocolName)); + $this->responseBuilder = make(ResponseBuilder::class, [ + 'dataFormatter' => $this->dataFormatter, + 'packer' => $this->packer, + ]); } protected function handleNotFound(ServerRequestInterface $request) diff --git a/src/json-rpc/src/HttpServer.php b/src/json-rpc/src/HttpServer.php index 3e7ab0813..28cf7ee9c 100644 --- a/src/json-rpc/src/HttpServer.php +++ b/src/json-rpc/src/HttpServer.php @@ -37,6 +37,11 @@ class HttpServer extends Server */ protected $packer; + /** + * @var \Hyperf\JsonRpc\ResponseBuilder + */ + protected $responseBuilder; + public function __construct( string $serverName, string $coreHandler, @@ -46,8 +51,13 @@ class HttpServer extends Server ) { parent::__construct($serverName, $coreHandler, $container, $dispatcher); $this->protocolManager = $protocolManager; - $packerClass = $this->protocolManager->getPacker('jsonrpc-http'); + $protocolName = 'jsonrpc-http'; + $packerClass = $this->protocolManager->getPacker($protocolName); $this->packer = $this->container->get($packerClass); + $this->responseBuilder = make(ResponseBuilder::class, [ + 'dataFormatter' => $container->get($this->protocolManager->getDataFormatter($protocolName)), + 'packer' => $this->packer, + ]); } protected function initRequestAndResponse(SwooleRequest $request, SwooleResponse $response): array @@ -56,11 +66,11 @@ class HttpServer extends Server $psr7Request = Psr7Request::loadFromSwooleRequest($request); if (! $this->isHealthCheck($psr7Request)) { if (strpos($psr7Request->getHeaderLine('content-type'), 'application/json') === false) { - throw new InvalidArgumentException('Invalid Json RPC request.'); + $this->responseBuilder->buildErrorResponse($request, -32700); } $content = $this->packer->unpack($psr7Request->getBody()->getContents()); if (! isset($content['jsonrpc'], $content['method'], $content['params'])) { - throw new InvalidArgumentException('Invalid Json RPC request.'); + $this->responseBuilder->buildErrorResponse($request, -32600); } } $psr7Request = $psr7Request->withUri($psr7Request->getUri()->withPath($content['method'] ?? '/')) diff --git a/src/json-rpc/src/ResponseBuilder.php b/src/json-rpc/src/ResponseBuilder.php new file mode 100644 index 000000000..8d7994b28 --- /dev/null +++ b/src/json-rpc/src/ResponseBuilder.php @@ -0,0 +1,89 @@ +dataFormatter = $dataFormatter; + $this->packer = $packer; + } + + public function buildErrorResponse(ServerRequestInterface $request, int $code): ResponseInterface + { + $body = new SwooleStream($this->formatErrorResponse($request, $code)); + return $this->response()->withAddedHeader('content-type', 'application/json')->withBody($body); + } + + public function buildResponse(ServerRequestInterface $request, $response): ResponseInterface + { + $body = new SwooleStream($this->formatResponse($response, $request)); + return $this->response() + ->withAddedHeader('content-type', 'application/json') + ->withBody($body); + } + + protected function formatResponse($response, ServerRequestInterface $request): string + { + $response = $this->dataFormatter->formatResponse([$request->getAttribute('request_id') ?? '', $response]); + return $this->packer->pack($response); + } + + protected function formatErrorResponse(ServerRequestInterface $request, int $code): string + { + [$code, $message] = $this->error($code); + $response = $this->dataFormatter->formatErrorResponse([$request->getAttribute('request_id') ?? '', $code, $message, null]); + return $this->packer->pack($response); + } + + protected function error(int $code, ?string $message = null): array + { + $mapping = [ + -32700 => 'Parse error.', + -32600 => 'Invalid request.', + -32601 => 'Method not found.', + -32602 => 'Invalid params.', + -32603 => 'Internal error.', + ]; + if (isset($mapping[$code])) { + return [$code, $mapping[$code]]; + } + return [$code, $message ?? '']; + } + + /** + * Get response instance from context. + */ + protected function response(): ResponseInterface + { + return Context::get(ResponseInterface::class); + } +} diff --git a/src/rpc-client/src/AbstractServiceClient.php b/src/rpc-client/src/AbstractServiceClient.php index 762bc0916..11cdb23cf 100644 --- a/src/rpc-client/src/AbstractServiceClient.php +++ b/src/rpc-client/src/AbstractServiceClient.php @@ -16,6 +16,7 @@ use Hyperf\Consul\Agent; use Hyperf\Consul\Health; use Hyperf\Consul\HealthInterface; use Hyperf\Contract\ConfigInterface; +use Hyperf\Contract\IdGeneratorInterface; use Hyperf\Contract\PackerInterface; use Hyperf\Guzzle\ClientFactory; use Hyperf\LoadBalancer\LoadBalancerInterface; @@ -44,7 +45,7 @@ abstract class AbstractServiceClient * * @var string */ - protected $protocol = 'jsonrpc'; + protected $protocol = 'jsonrpc-http'; /** * The load balancer of the client, this name of the load balancer @@ -89,6 +90,11 @@ abstract class AbstractServiceClient */ protected $config; + /** + * @var null|\Hyperf\Contract\IdGeneratorInterface + */ + protected $idGenerator; + public function __construct(ContainerInterface $container) { $this->container = $container; @@ -101,13 +107,24 @@ abstract class AbstractServiceClient $this->client = make(Client::class) ->setPacker($this->createPacker()) ->setTransporter($transporter); + if ($container->has(IdGeneratorInterface::class)) { + $this->idGenerator = $container->get(IdGeneratorInterface::class); + } } - protected function __request(string $method, array $params) + protected function __request(string $method, array $params, ?string $id = null) { - $response = $this->client->send($this->__generateData($method, $params)); - if (is_array($response) && isset($response['result'])) { - return $response['result']; + if ($this->idGenerator instanceof IdGeneratorInterface && ! $id) { + $id = $this->idGenerator->generate(); + } + $response = $this->client->send($this->__generateData($method, $params, $id)); + if (is_array($response)) { + if (isset($response['result'])) { + return $response['result']; + } + if (isset($response['error'])) { + return $response['error']; + } } throw new RuntimeException('Invalid response.'); } @@ -120,9 +137,9 @@ abstract class AbstractServiceClient return $this->pathGenerator->generate($this->serviceName, $methodName); } - protected function __generateData(string $methodName, array $params) + protected function __generateData(string $methodName, array $params, ?string $id) { - return $this->dataFormatter->formatRequest([$this->__generateRpcPath($methodName), $params]); + return $this->dataFormatter->formatRequest([$this->__generateRpcPath($methodName), $params, $id]); } protected function createLoadBalancer(array $nodes, callable $refresh = null): LoadBalancerInterface diff --git a/src/rpc/src/Contract/DataFormatterInterface.php b/src/rpc/src/Contract/DataFormatterInterface.php index 21bace04a..0f174e483 100644 --- a/src/rpc/src/Contract/DataFormatterInterface.php +++ b/src/rpc/src/Contract/DataFormatterInterface.php @@ -17,4 +17,6 @@ interface DataFormatterInterface public function formatRequest($data); public function formatResponse($data); + + public function formatErrorResponse($data); } From 552ecf7324f5dff7e7f32a8fc6e8ab1e303ffbd1 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 24 Jul 2019 01:52:09 +0800 Subject: [PATCH 18/36] Optimized and adapte request_id --- src/json-rpc/src/HttpServer.php | 6 ++++-- src/json-rpc/src/TcpServer.php | 27 +++++++++++++++++++-------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/json-rpc/src/HttpServer.php b/src/json-rpc/src/HttpServer.php index 28cf7ee9c..88eaa7cd9 100644 --- a/src/json-rpc/src/HttpServer.php +++ b/src/json-rpc/src/HttpServer.php @@ -68,6 +68,7 @@ class HttpServer extends Server if (strpos($psr7Request->getHeaderLine('content-type'), 'application/json') === false) { $this->responseBuilder->buildErrorResponse($request, -32700); } + // @TODO Optimize the error handling of encode. $content = $this->packer->unpack($psr7Request->getBody()->getContents()); if (! isset($content['jsonrpc'], $content['method'], $content['params'])) { $this->responseBuilder->buildErrorResponse($request, -32600); @@ -75,13 +76,14 @@ class HttpServer extends Server } $psr7Request = $psr7Request->withUri($psr7Request->getUri()->withPath($content['method'] ?? '/')) ->withParsedBody($content['params'] ?? null) - ->withAttribute('data', $content ?? []); + ->withAttribute('data', $content ?? []) + ->withAttribute('request_id', $content['id'] ?? null); Context::set(ServerRequestInterface::class, $psr7Request); Context::set(ResponseInterface::class, $psr7Response = new Psr7Response($response)); return [$psr7Request, $psr7Response]; } - protected function isHealthCheck(RequestInterface $request) + protected function isHealthCheck(RequestInterface $request): bool { return $request->getHeaderLine('user-agent') === 'Consul Health Check'; } diff --git a/src/json-rpc/src/TcpServer.php b/src/json-rpc/src/TcpServer.php index 0e7c1d20d..df8a78e0f 100644 --- a/src/json-rpc/src/TcpServer.php +++ b/src/json-rpc/src/TcpServer.php @@ -18,7 +18,6 @@ use Hyperf\HttpMessage\Uri\Uri; use Hyperf\Rpc\ProtocolManager; use Hyperf\RpcServer\Server; use Hyperf\Server\ServerManager; -use InvalidArgumentException; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -37,6 +36,11 @@ class TcpServer extends Server */ protected $packer; + /** + * @var \Hyperf\JsonRpc\ResponseBuilder + */ + protected $responseBuilder; + public function __construct( string $serverName, string $coreHandler, @@ -47,8 +51,13 @@ class TcpServer extends Server ) { parent::__construct($serverName, $coreHandler, $container, $dispatcher, $logger); $this->protocolManager = $protocolManager; - $packerClass = $this->protocolManager->getPacker('jsonrpc'); + $protocolName = 'jsonrpc'; + $packerClass = $this->protocolManager->getPacker($protocolName); $this->packer = $this->container->get($packerClass); + $this->responseBuilder = make(ResponseBuilder::class, [ + 'dataFormatter' => $container->get($this->protocolManager->getDataFormatter($protocolName)), + 'packer' => $this->packer, + ]); } protected function buildResponse(int $fd, SwooleServer $server): ResponseInterface @@ -62,10 +71,7 @@ class TcpServer extends Server $class = $this->protocolManager->getPacker('jsonrpc'); $packer = $this->container->get($class); $data = $this->packer->unpack($data); - if (isset($data['jsonrpc'])) { - return $this->buildJsonRpcRequest($fd, $fromId, $data); - } - throw new InvalidArgumentException('Doesn\'t match JSON RPC protocol.'); + return $this->buildJsonRpcRequest($fd, $fromId, $data); } protected function buildJsonRpcRequest(int $fd, int $fromId, array $data) @@ -80,9 +86,14 @@ class TcpServer extends Server [$type, $port] = ServerManager::get($this->serverName); $uri = (new Uri())->withPath($data['method'])->withHost($port->host)->withPort($port->port); - return (new Psr7Request('POST', $uri))->withAttribute('fd', $fd) + $request = (new Psr7Request('POST', $uri))->withAttribute('fd', $fd) ->withAttribute('fromId', $fromId) ->withAttribute('data', $data) - ->withParsedBody($data['params']); + ->withAttribute('request_id', $data['id'] ?? null) + ->withParsedBody($data['params'] ?? ''); + if (! isset($data['jsonrpc'])) { + return $this->responseBuilder->buildErrorResponse($request, -32600); + } + return $request; } } From 5a7f83e59fe4d0992b8282934cfecfc1bd08974d Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 24 Jul 2019 01:52:28 +0800 Subject: [PATCH 19/36] Optimized --- src/rpc-client/src/Client.php | 2 +- src/rpc-server/src/Server.php | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rpc-client/src/Client.php b/src/rpc-client/src/Client.php index 2a8154686..9020a57ae 100644 --- a/src/rpc-client/src/Client.php +++ b/src/rpc-client/src/Client.php @@ -39,7 +39,7 @@ class Client $packer = $this->getPacker(); $packedData = $packer->pack($data); $response = $this->getTransporter()->send($packedData); - return $packer->unpack($response); + return $packer->unpack((string) $response); } public function getPacker(): PackerInterface diff --git a/src/rpc-server/src/Server.php b/src/rpc-server/src/Server.php index 6006b7c06..891b7b2ad 100644 --- a/src/rpc-server/src/Server.php +++ b/src/rpc-server/src/Server.php @@ -118,9 +118,7 @@ abstract class Server implements OnReceiveInterface, MiddlewareInitializerInterf if (! $response || ! $response instanceof ResponseInterface) { $response = $this->transferToResponse($response); } - if (! $response) { - $this->logger->debug(sprintf('No content to response at fd[%d]', $fd)); - } else { + if ($response) { $server->send($fd, (string) $response); } } From 9b5202a325d803c68de5975c83f349a541feb3ee Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 24 Jul 2019 01:52:40 +0800 Subject: [PATCH 20/36] Use lower case header name --- src/http-message/src/Base/Request.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/http-message/src/Base/Request.php b/src/http-message/src/Base/Request.php index 92ffd7f6a..02b30f69c 100755 --- a/src/http-message/src/Base/Request.php +++ b/src/http-message/src/Base/Request.php @@ -239,11 +239,11 @@ class Request implements RequestInterface $host .= ':' . $port; } + $header = 'host'; if ($this->hasHeader('host')) { - $header = $this->getHeaderLine('host'); + $host = $this->getHeaderLine('host'); } else { - $header = 'Host'; - $this->headerNames['host'] = 'Host'; + $this->headerNames['host'] = 'host'; } // Ensure Host is the first header. $this->headers = [$header => [$host]] + $this->headers; From cc198ca67fca1c461d65a58a128dc08251bda225 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 24 Jul 2019 01:52:51 +0800 Subject: [PATCH 21/36] Adapte TCP health check --- .../src/Listener/RegisterServiceListener.php | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/service-governance/src/Listener/RegisterServiceListener.php b/src/service-governance/src/Listener/RegisterServiceListener.php index 343ae4352..666a60518 100644 --- a/src/service-governance/src/Listener/RegisterServiceListener.php +++ b/src/service-governance/src/Listener/RegisterServiceListener.php @@ -74,6 +74,9 @@ class RegisterServiceListener implements ListenerInterface */ public function process(object $event) { + foreach ($this->consulAgent->services()->json() as $service) { + $this->consulAgent->deregisterService($service['ID']); + } $services = $this->serviceManager->all(); $servers = $this->getServers(); foreach ($services as $serviceName => $paths) { @@ -94,7 +97,7 @@ class RegisterServiceListener implements ListenerInterface } else { $nextId = $this->generateId($this->getLastServiceId($serviceName)); } - $response = $this->consulAgent->registerService([ + $requestBody = [ 'Name' => $serviceName, 'ID' => $nextId, 'Address' => $address, @@ -102,12 +105,22 @@ class RegisterServiceListener implements ListenerInterface 'Meta' => [ 'Protocol' => $service['protocol'], ], - 'Check' => [ + ]; + if ($service['protocol'] === 'jsonrpc-http') { + $requestBody['Check'] = [ 'DeregisterCriticalServiceAfter' => '90m', 'HTTP' => "http://{$address}:{$port}/", 'Interval' => '1s', - ], - ]); + ]; + } + if ($service['protocol'] === 'jsonrpc') { + $requestBody['Check'] = [ + 'DeregisterCriticalServiceAfter' => '90m', + 'TCP' => "{$address}:{$port}", + 'Interval' => '1s', + ]; + } + $response = $this->consulAgent->registerService($requestBody); if ($response->getStatusCode() === 200) { $this->logger->info(sprintf('Service %s[%s]:%s register to the consul successfully.', $serviceName, $path, $nextId), $this->defaultLoggerContext); } else { From 7453c0f120e55aa1dab4229aa0f9c036d7a329d9 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 24 Jul 2019 02:01:48 +0800 Subject: [PATCH 22/36] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41038128f..5f080a8c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,12 @@ - [#245](https://github.com/hyperf-cloud/hyperf/pull/245) Added TaskWorkerStrategy and WorkerStrategy crontab strategies. - [#254](https://github.com/hyperf-cloud/hyperf/pull/254) Added support for array value of `RequestMapping::$methods`, `@RequestMapping(methods={"GET"})` and `@RequestMapping(methods={RequestMapping::GET})` are available now. - [#255](https://github.com/hyperf-cloud/hyperf/pull/255) Transfer `Hyperf\Utils\Contracts\Arrayable` result of Request to Response automatically, and added `text/plain` content-type header for string Response. +- [#256](https://github.com/hyperf-cloud/hyperf/pull/256) If `Hyperf\Contract\IdGeneratorInterface` exist, the `json-rpc` client will generate a Request ID via IdGenerator automatically, and stored in Request attibute. Also added support for service register and health checks of `jsonrpc` TCP protocol. ## Changed - [#247](https://github.com/hyperf-cloud/hyperf/pull/247) Use Use `WorkerStrategy` as the default crontab strategy. +- [#256](https://github.com/hyperf-cloud/hyperf/pull/256) Optimized error handling of json-rpc, and will response a standard json-rpc error object when the rpc method does not exist. ## Fixed From 19969bb9249da9fe231a679524704deb9eb43f73 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 24 Jul 2019 02:11:29 +0800 Subject: [PATCH 23/36] Optimized --- doc/zh/json-rpc.md | 11 ++++++----- doc/zh/service-register.md | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/zh/json-rpc.md b/doc/zh/json-rpc.md index d847e2d1d..ee66a1893 100644 --- a/doc/zh/json-rpc.md +++ b/doc/zh/json-rpc.md @@ -39,6 +39,7 @@ namespace App\JsonRpc; use Hyperf\RpcServer\Annotation\RpcService; /** + * 注意,如希望通过服务中心来管理服务,需在注解内增加 publishTo 属性 * @RpcService(name="CalculatorService", protocol="jsonrpc-http", server="jsonrpc-http") */ class CalculatorService implements CalculatorServiceInterface @@ -131,11 +132,11 @@ return [ 配置完成后,在启动服务时,Hyperf 会自动地将 `@RpcService` 定义了 `publishTo` 属性为 `consul` 的服务注册到服务中心去。 -> 目前仅支持 `jsonrpc-http` 协议发布到服务中心去,其它协议的健康检查尚未实现 +> 目前仅支持 `jsonrpc` 和 `jsonrpc-http` 协议发布到服务中心去,其它协议尚未实现服务注册 ## 定义服务消费者 -一个 `服务消费者(ServiceConsumer)` 可以理解为就是一个客户端类,但在 Hyperf 里您无需处理连接和请求相关的事情,只需要定义一个类及相关属性即可。(v1.1会提供动态代理实现的客户端,使之更加简单便捷) +一个 `服务消费者(ServiceConsumer)` 可以理解为就是一个客户端类,但在 Hyperf 里您无需处理连接和请求相关的事情,只需要定义一个类及相关属性即可。(后续版本迭代会提供动态代理实现的客户端,使之更加简单便捷) ```php [ - App\JsonRpc\CalculatorServiceInterface::class => App\JsonRpc\CalculatorService::class, + App\JsonRpc\CalculatorServiceInterface::class => App\JsonRpc\CalculatorServiceConsumer::class, ], ]; ``` diff --git a/doc/zh/service-register.md b/doc/zh/service-register.md index bf1043b9a..6fdbd4c9c 100644 --- a/doc/zh/service-register.md +++ b/doc/zh/service-register.md @@ -39,4 +39,4 @@ class CalculatorService implements CalculatorServiceInterface `server` 属性为绑定该服务类发布所要承载的 `Server`,默认值为 `jsonrpc-http`,该属性对应 `config/autoload/server.php` 文件内 `servers` 下所对应的 `name`,这里也就意味着我们需要定义一个对应的 `Server`,我们下一章节具体阐述这里应该怎样去处理; `publishTo` 属性为定义该服务所要发布的服务中心,目前仅支持 `consul` 或为空,为空时代表不发布该服务到服务中心去,但也就意味着您需要手动处理服务发现的问题,当值为 `consul` 时需要对应配置好 [hyperf/consul](./consul.md) 组件的相关配置,要使用此功能需安装 [hyperf/service-governance](https://github.com/hyperf-cloud/service-governance) 组件; -> 使用 `@RpcService` 注解需 use Hyperf\RpcServer\Annotation\RpcService; 命名空间。 +> 使用 `@RpcService` 注解需 `use Hyperf\RpcServer\Annotation\RpcService;` 命名空间。 From 88d1c03bdbace0161c572918b8aeb51fa76d98c3 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 24 Jul 2019 02:15:44 +0800 Subject: [PATCH 24/36] Optimized the getInternalIp method --- .../src/Listener/RegisterServiceListener.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/service-governance/src/Listener/RegisterServiceListener.php b/src/service-governance/src/Listener/RegisterServiceListener.php index 666a60518..f03bb911a 100644 --- a/src/service-governance/src/Listener/RegisterServiceListener.php +++ b/src/service-governance/src/Listener/RegisterServiceListener.php @@ -222,6 +222,14 @@ class RegisterServiceListener implements ListenerInterface private function getInternalIp(): string { - return gethostbyname(gethostname()); + $ips = swoole_get_local_ip(); + if (is_array($ips)) { + return current($ips); + } + $ip = gethostbyname(gethostname()); + if (is_string($ip)) { + return $ip; + } + throw new \RuntimeException('Can not get the internal IP.'); } } From ac3924bb6080e30b787ad647be7e8459ddc27ff3 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 24 Jul 2019 02:18:18 +0800 Subject: [PATCH 25/36] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f080a8c9..ac4856181 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - [#235](https://github.com/hyperf-cloud/hyperf/pull/235) Added default exception handler for `grpc-server` and optimized code. - [#240](https://github.com/hyperf-cloud/hyperf/pull/240) Fixed OnPipeMessage event will be dispatch by another listener. +- [#257](https://github.com/hyperf-cloud/hyperf/pull/257) Fixed cannot get the internal ip in some special environment. # v1.0.5 - 2019-07-07 From b1a36417e743a43246583f087c198bee977727be Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 24 Jul 2019 02:28:13 +0800 Subject: [PATCH 26/36] Update CHANGELOG.md --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac4856181..d4d617835 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Added +- [#203](https://github.com/hyperf-cloud/hyperf/pull/203) [#236](https://github.com/hyperf-cloud/hyperf/pull/236) [#247](https://github.com/hyperf-cloud/hyperf/pull/247) [#252](https://github.com/hyperf-cloud/hyperf/pull/252) Added View component, support for Blade engine and Smarty engine. +- [#203](https://github.com/hyperf-cloud/hyperf/pull/203) Added support for Swoole Task mechanism. - [#245](https://github.com/hyperf-cloud/hyperf/pull/245) Added TaskWorkerStrategy and WorkerStrategy crontab strategies. - [#254](https://github.com/hyperf-cloud/hyperf/pull/254) Added support for array value of `RequestMapping::$methods`, `@RequestMapping(methods={"GET"})` and `@RequestMapping(methods={RequestMapping::GET})` are available now. - [#255](https://github.com/hyperf-cloud/hyperf/pull/255) Transfer `Hyperf\Utils\Contracts\Arrayable` result of Request to Response automatically, and added `text/plain` content-type header for string Response. @@ -9,14 +11,14 @@ ## Changed -- [#247](https://github.com/hyperf-cloud/hyperf/pull/247) Use Use `WorkerStrategy` as the default crontab strategy. -- [#256](https://github.com/hyperf-cloud/hyperf/pull/256) Optimized error handling of json-rpc, and will response a standard json-rpc error object when the rpc method does not exist. +- [#247](https://github.com/hyperf-cloud/hyperf/pull/247) Use `WorkerStrategy` as the default crontab strategy. +- [#256](https://github.com/hyperf-cloud/hyperf/pull/256) Optimized error handling of json-rpc, server will response a standard json-rpc error object when the rpc method does not exist. ## Fixed - [#235](https://github.com/hyperf-cloud/hyperf/pull/235) Added default exception handler for `grpc-server` and optimized code. - [#240](https://github.com/hyperf-cloud/hyperf/pull/240) Fixed OnPipeMessage event will be dispatch by another listener. -- [#257](https://github.com/hyperf-cloud/hyperf/pull/257) Fixed cannot get the internal ip in some special environment. +- [#257](https://github.com/hyperf-cloud/hyperf/pull/257) Fixed cannot get the Internal IP in some special environment. # v1.0.5 - 2019-07-07 From 4b317ae0080b47a947cf8cb4d5d912cb0db903e6 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 24 Jul 2019 02:31:36 +0800 Subject: [PATCH 27/36] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4d617835..9213e7fcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# v1.0.6 - TBD +# v1.0.6 - 2019-07-24 ## Added @@ -20,7 +20,7 @@ - [#240](https://github.com/hyperf-cloud/hyperf/pull/240) Fixed OnPipeMessage event will be dispatch by another listener. - [#257](https://github.com/hyperf-cloud/hyperf/pull/257) Fixed cannot get the Internal IP in some special environment. -# v1.0.5 - 2019-07-07 +# v1.0.5 - 2019-07-17 ## Added From e7c2781a3f11d651c2cd92e8f26a49b5bb08d0bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Wed, 24 Jul 2019 10:07:56 +0800 Subject: [PATCH 28/36] Update doc. --- CHANGELOG.md | 1 + doc/zh/cache.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4d617835..ccd98dc84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - [#203](https://github.com/hyperf-cloud/hyperf/pull/203) [#236](https://github.com/hyperf-cloud/hyperf/pull/236) [#247](https://github.com/hyperf-cloud/hyperf/pull/247) [#252](https://github.com/hyperf-cloud/hyperf/pull/252) Added View component, support for Blade engine and Smarty engine. - [#203](https://github.com/hyperf-cloud/hyperf/pull/203) Added support for Swoole Task mechanism. - [#245](https://github.com/hyperf-cloud/hyperf/pull/245) Added TaskWorkerStrategy and WorkerStrategy crontab strategies. +- [#251](https://github.com/hyperf-cloud/hyperf/pull/251) Added coroutine memory driver for cache. - [#254](https://github.com/hyperf-cloud/hyperf/pull/254) Added support for array value of `RequestMapping::$methods`, `@RequestMapping(methods={"GET"})` and `@RequestMapping(methods={RequestMapping::GET})` are available now. - [#255](https://github.com/hyperf-cloud/hyperf/pull/255) Transfer `Hyperf\Utils\Contracts\Arrayable` result of Request to Response automatically, and added `text/plain` content-type header for string Response. - [#256](https://github.com/hyperf-cloud/hyperf/pull/256) If `Hyperf\Contract\IdGeneratorInterface` exist, the `json-rpc` client will generate a Request ID via IdGenerator automatically, and stored in Request attibute. Also added support for service register and health checks of `jsonrpc` TCP protocol. diff --git a/doc/zh/cache.md b/doc/zh/cache.md index 78c065249..d50f32867 100644 --- a/doc/zh/cache.md +++ b/doc/zh/cache.md @@ -184,3 +184,51 @@ public function updateUserBook(int $id) } ``` +## 缓存驱动 + +### Redis驱动 + +`Hyperf\Cache\Driver\RedisDriver` 会把缓存数据存放到 `Redis` 中,需要用户配置相应的 `Redis配置`。此方式为默认方式。 + +### 协程内存驱动 + +> 本驱动乃Beta版本,请谨慎使用。 + +如果您需要将数据缓存到 `Context` 中,可以尝试次驱动。例如以下应用场景: + +```php +getArray($userId)[$id] ?? 0; + } + + /** + * @Cacheable(prefix="test", group="co") + * @param int $userId + * @return array + */ + public function getArray($userId) + { + return $this->redis->hGetAll($userId); + } +} +``` + +对应配置如下: + +```php + [ + 'driver' => Hyperf\Cache\Driver\CoroutineMemoryDriver::class, + 'packer' => Hyperf\Utils\Packer\PhpSerializerPacker::class, + ], +]; +``` + From d214e29b366472b0f9db2a01981b1b52e6601468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Wed, 24 Jul 2019 10:09:59 +0800 Subject: [PATCH 29/36] Update cache.md --- doc/zh/cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/zh/cache.md b/doc/zh/cache.md index d50f32867..dfce3dcfb 100644 --- a/doc/zh/cache.md +++ b/doc/zh/cache.md @@ -194,7 +194,7 @@ public function updateUserBook(int $id) > 本驱动乃Beta版本,请谨慎使用。 -如果您需要将数据缓存到 `Context` 中,可以尝试次驱动。例如以下应用场景: +如果您需要将数据缓存到 `Context` 中,可以尝试次驱动。例如以下应用场景 `Demo::get` 会在多个地方调用多次,但是又不想每次都到 `Redis` 中进行查询。 ```php Date: Wed, 24 Jul 2019 10:48:35 +0800 Subject: [PATCH 30/36] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b429ea19..6b3dc70ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +# v1.0.7 - TBD + # v1.0.6 - 2019-07-24 ## Added From 5b614b6e3c5332bbebba23792dba88b471ab21dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Wed, 24 Jul 2019 13:42:59 +0800 Subject: [PATCH 31/36] Added how to use mongo in task. --- doc/zh/task.md | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/doc/zh/task.md b/doc/zh/task.md index 667a16494..f4c86ec16 100644 --- a/doc/zh/task.md +++ b/doc/zh/task.md @@ -35,6 +35,7 @@ return [ ]; ``` + ## 使用 Task 组件提供了 `主动方法投递` 和 `注解投递` 两种使用方法。 @@ -66,6 +67,7 @@ $exec = $container->get(TaskExecutor::class); $result = $exec->execute(new Task([MethodTask::class, 'handle'], Coroutine::id())); ``` + ### 使用注解 通过 `主动方法投递` 时,并不是特别直观,这里我们实现了对应的 `@Task` 注解,并通过 `AOP` 重写了方法调用。当在 `Worker` 进程时,自动投递到 `Task` 进程,并协程等待 数据返回。 @@ -111,3 +113,86 @@ Swoole 暂时没有协程化的函数列表 - pdo_odbc - pdo_firebird +### MongoDB + +因为 `MongoDB` 没有办法被 `hook`,所以我们可以通过 `Task` 来调用,下面就简单介绍一下如何通过注解方便使用。 + +以下我们实现两个方法 `insert` 和 `query`,其中需要注意的是 `manager` 方法不能使用 `Task`, +因为 `Task` 会在对应的 `Task进程` 中处理,所以从 `Task进程` 返回到 `Worker进程` 的数据最好是不涉及任何 `IO` 的数据。 + +```php +insert($document); + + $result = $this->manager()->executeBulkWrite($namespace, $bulk, $writeConcern); + return $result->getUpsertedCount(); + } + + /** + * @Task + * @param mixed $namespace + * @param mixed $filter + * @param mixed $options + */ + public function query($namespace, $filter = [], $options = []) + { + $query = new Query($filter, $options); + $cursor = $this->manager()->executeQuery($namespace, $query); + return $cursor->toArray(); + } + + protected function manager() + { + if ($this->manager instanceof Manager) { + return $this->manager; + } + $uri = 'mongodb://127.0.0.1:27017'; + return $this->manager = new Manager($uri, []); + } +} + +``` + +使用如下 + +```php +get(MongoTask::class); +$client->insert('hyperf.test', ['id' => rand(0, 99999999)]); + +$result = $client->query('hyperf.test', [], [ + 'sort' => ['id' => -1], + 'limit' => 5, +]); +``` + From 2ae2b452825a7ed9a0224ba39ca882dc6ea30d28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Wed, 24 Jul 2019 13:48:17 +0800 Subject: [PATCH 32/36] Update task.md --- doc/zh/task.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/doc/zh/task.md b/doc/zh/task.md index f4c86ec16..7bd199e0e 100644 --- a/doc/zh/task.md +++ b/doc/zh/task.md @@ -115,10 +115,11 @@ Swoole 暂时没有协程化的函数列表 ### MongoDB -因为 `MongoDB` 没有办法被 `hook`,所以我们可以通过 `Task` 来调用,下面就简单介绍一下如何通过注解方便使用。 +> 因为 `MongoDB` 没有办法被 `hook`,所以我们可以通过 `Task` 来调用,下面就简单介绍一下如何通过注解方式调用 `MongoDB`。 以下我们实现两个方法 `insert` 和 `query`,其中需要注意的是 `manager` 方法不能使用 `Task`, -因为 `Task` 会在对应的 `Task进程` 中处理,所以从 `Task进程` 返回到 `Worker进程` 的数据最好是不涉及任何 `IO` 的数据。 +因为 `Task` 会在对应的 `Task进程` 中处理,然后将数据从 `Task进程` 返回到 `Worker进程` 。 +所以 `Task方法` 的入参和出参最好不要携带任何 `IO`,比如返回一个实例化后的 `Redis` 等等。 ```php Date: Wed, 24 Jul 2019 14:20:51 +0800 Subject: [PATCH 33/36] Update OnPipeMessageListener.php --- src/config-apollo/src/Listener/OnPipeMessageListener.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config-apollo/src/Listener/OnPipeMessageListener.php b/src/config-apollo/src/Listener/OnPipeMessageListener.php index 4a95f0069..2ab88e7fc 100644 --- a/src/config-apollo/src/Listener/OnPipeMessageListener.php +++ b/src/config-apollo/src/Listener/OnPipeMessageListener.php @@ -86,7 +86,7 @@ class OnPipeMessageListener implements ListenerInterface $this->config->set($key, $value); $this->logger->debug(sprintf('Config [%s] is updated', $key)); } - ReleaseKey::set($cacheKey, $data['releaseKey']); + ReleaseKey::set($cacheKey, $data->releaseKey); } } } From 483dee69cb15c3bc9cbd6ba29ab25a5e899db03f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Wed, 24 Jul 2019 15:24:11 +0800 Subject: [PATCH 34/36] Fixed typo. --- doc/zh/cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/zh/cache.md b/doc/zh/cache.md index dfce3dcfb..732ae96df 100644 --- a/doc/zh/cache.md +++ b/doc/zh/cache.md @@ -194,7 +194,7 @@ public function updateUserBook(int $id) > 本驱动乃Beta版本,请谨慎使用。 -如果您需要将数据缓存到 `Context` 中,可以尝试次驱动。例如以下应用场景 `Demo::get` 会在多个地方调用多次,但是又不想每次都到 `Redis` 中进行查询。 +如果您需要将数据缓存到 `Context` 中,可以尝试此驱动。例如以下应用场景 `Demo::get` 会在多个地方调用多次,但是又不想每次都到 `Redis` 中进行查询。 ```php Date: Wed, 24 Jul 2019 17:16:20 +0800 Subject: [PATCH 35/36] Fixed timeout when produce a amqp message. --- src/amqp/src/Connection.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/amqp/src/Connection.php b/src/amqp/src/Connection.php index d558e0f8d..a8b163038 100644 --- a/src/amqp/src/Connection.php +++ b/src/amqp/src/Connection.php @@ -139,7 +139,7 @@ class Connection extends BaseConnection implements ConnectionInterface $class = AMQPSwooleConnection::class; } - $this->lastHeartbeatTime = 0; + $this->lastHeartbeatTime = microtime(true); return new $class($this->config['host'] ?? 'localhost', $this->config['port'] ?? 5672, $this->config['user'] ?? 'guest', $this->config['password'] ?? 'guest', $this->config['vhost'] ?? '/', $this->params->isInsist(), $this->params->getLoginMethod(), $this->params->getLoginResponse(), $this->params->getLocale(), $this->params->getConnectionTimeout(), $this->params->getReadWriteTimeout(), $this->params->getContext(), $this->params->isKeepalive(), $this->params->getHeartbeat()); } @@ -149,13 +149,8 @@ class Connection extends BaseConnection implements ConnectionInterface return false; } - $lastHeartbeatTime = $this->lastHeartbeatTime; - $currentTime = microtime(true); - - if ($lastHeartbeatTime && $lastHeartbeatTime > 0) { - if ($currentTime - $lastHeartbeatTime > $this->params->getHeartbeat()) { - return true; - } + if (microtime(true) - $this->lastHeartbeatTime > $this->params->getHeartbeat()) { + return true; } return false; From c0f121fc72f1c5d9706f9e327d077b6ed59b21b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Wed, 24 Jul 2019 17:17:32 +0800 Subject: [PATCH 36/36] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b3dc70ec..7147980e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # v1.0.7 - TBD +## Fixed + +- [#266](https://github.com/hyperf-cloud/hyperf/pull/266) Fixed timeout when produce a amqp message. + # v1.0.6 - 2019-07-24 ## Added