在使用 Hyperf1.1
的小夥伴們,通常都會碰到這麼一個問題,那就是協程上下文資料拷貝的問題。
比如我實現了一個 Listener
,在監聽 SQL
的同時,會把當前請求的路由資料記錄下來。當出現慢查的時候,就可以精確定位到是哪個路由。
但當你以以下方式執行 SQL
時,可能就會出現這個錯誤 TypeError:Return value of Hyperf\HttpServer\Request::getRequest() must implement interface Psr\Http\Message\ServerRequestInterface, null returned
<?php
go(function(){
Db::select("SELECT * FROM users;");
})
當然,你可以自己實現 go
方法,然後在 composer
載入之前提前載入這個檔案。例如
// 包含 go 方法的實現
require BASE_PATH . '/config/bootstrap.php';
require BASE_PATH . '/vendor/autoload.php';
但框架中直接使用 Coroutine::create
來建立協程就沒有辦法了,比如 Parallel
和 Concurrent
等等
ClassMap 功能
而在 Hyperf2.0
版本中,框架實現了基於 composer class map
的替換功能,你可以自己實現 Hyperf\Utils\Coroutine
,而框架會自動幫你替換掉原來的 Hyperf\Utils\Coroutine
。
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Utils;
use App\Kernel\Context\Coroutine as BCoroutine;
use Swoole\Coroutine as SwooleCoroutine;
/**
* @method static void defer(callable $callable)
*/
class Coroutine
{
public static function __callStatic($name, $arguments)
{
if (! method_exists(SwooleCoroutine::class, $name)) {
throw new \BadMethodCallException(sprintf('Call to undefined method %s.', $name));
}
return SwooleCoroutine::$name(...$arguments);
}
/**
* Returns the current coroutine ID.
* Returns -1 when running in non-coroutine context.
*/
public static function id(): int
{
return SwooleCoroutine::getCid();
}
/**
* Returns the parent coroutine ID.
* Returns -1 when running in the top level coroutine.
* Returns null when running in non-coroutine context.
*
* @see https://github.com/swoole/swoole-src/pull/2669/files#diff-3bdf726b0ac53be7e274b60d59e6ec80R940
*/
public static function parentId(): ?int
{
$cid = SwooleCoroutine::getPcid();
if ($cid === false) {
return null;
}
return $cid;
}
/**
* @return int Returns the coroutine ID of the coroutine just created.
* Returns -1 when coroutine create failed.
*/
public static function create(callable $callable): int
{
return di()->get(BCoroutine::class)->create($callable);
}
public static function inCoroutine(): bool
{
return Coroutine::id() > 0;
}
}
App\Kernel\Context\Coroutine
是實現了協程上下文拷貝功能的類。
測試
讓我們寫一段程式碼,進行測試。
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace App\Controller;
class IndexController extends Controller
{
public function index()
{
$user = $this->request->input('user', 'Hyperf');
$method = $this->request->getMethod();
go(function () {
var_dump($this->request->input('user'));
});
return $this->response->success([
'user' => $user,
'method' => $method,
'message' => 'Hello Hyperf.',
]);
}
}
當我們不使用 ClassMap
功能時,呼叫介面,會丟擲以下錯誤。
[WARNING] TypeError:Return value of Hyperf\HttpServer\Request::getRequest() must implement interface Psr\Http\Message\ServerRequestInterface, null returned(0) in /Users/limx/Applications/GitHub/hyperf/biz-skeleton/vendor/hyperf/http-server/src/Request.php:620
Stack trace:
#0 /Users/limx/Applications/GitHub/hyperf/biz-skeleton/vendor/hyperf/http-server/src/Request.php(579): Hyperf\HttpServer\Request->getRequest()
#1 /Users/limx/Applications/GitHub/hyperf/biz-skeleton/vendor/hyperf/utils/src/Functions.php(268): Hyperf\HttpServer\Request->Hyperf\HttpServer\{closure}()
#2 /Users/limx/Applications/GitHub/hyperf/biz-skeleton/vendor/hyperf/http-server/src/Request.php(593): call(Object(Closure))
#3 /Users/limx/Applications/GitHub/hyperf/biz-skeleton/vendor/hyperf/http-server/src/Request.php(587): Hyperf\HttpServer\Request->storeParsedData(Object(Closure))
#4 /Users/limx/Applications/GitHub/hyperf/biz-skeleton/vendor/hyperf/http-server/src/Request.php(97): Hyperf\HttpServer\Request->getInputData()
#5 /Users/limx/Applications/GitHub/hyperf/biz-skeleton/app/Controller/IndexController.php(21): Hyperf\HttpServer\Request->input('user')
#6 /Users/limx/Applications/GitHub/hyperf/biz-skeleton/vendor/hyperf/utils/src/Functions.php(268): App\Controller\IndexController->App\Controller\{closure}()
#7 /Users/limx/Applications/GitHub/hyperf/biz-skeleton/vendor/hyperf/utils/src/Coroutine.php(67): call(Object(Closure))
#8 {main}
當我們配置了 ClassMap
後,結果就正常了。
$ curl http://127.0.0.1:9501/\?user\=Hyperf
{"code":0,"data":{"user":"Hyperf","method":"GET","message":"Hello Hyperf."}}
string(6) "Hyperf"
可見 App\Kernel\Context\Coroutine
已被正確替換。
大家可以考慮一下,哪些場景,通過這個辦法可以輕鬆的解決呢?
寫在最後
Hyperf 是基於 Swoole 4.4+ 實現的高效能、高靈活性的 PHP 協程框架,內建協程伺服器及大量常用的元件,效能較傳統基於 PHP-FPM 的框架有質的提升,提供超高效能的同時,也保持著極其靈活的可擴充套件性,標準元件均基於 PSR 標準 實現,基於強大的依賴注入設計,保證了絕大部分元件或類都是 可替換 與 可複用 的。
框架元件庫除了常見的協程版的 MySQL 客戶端、Redis 客戶端,還為您準備了協程版的 Eloquent ORM、WebSocket 服務端及客戶端、JSON RPC 服務端及客戶端、GRPC 服務端及客戶端、Zipkin/Jaeger (OpenTracing) 客戶端、Guzzle HTTP 客戶端、Elasticsearch 客戶端、Consul 客戶端、ETCD 客戶端、AMQP 元件、Apollo 配置中心、阿里雲 ACM 應用配置管理、ETCD 配置中心、基於令牌桶演算法的限流器、通用連線池、熔斷器、Swagger 文件生成、Swoole Tracker、Blade 和 Smarty 檢視引擎、Snowflake 全域性ID生成器 等元件,省去了自己實現對應協程版本的麻煩。
Hyperf 還提供了 基於 PSR-11 的依賴注入容器、註解、AOP 面向切面程式設計、基於 PSR-15 的中介軟體、自定義程式、基於 PSR-14 的事件管理器、Redis/RabbitMQ 訊息佇列、自動模型快取、基於 PSR-16 的快取、Crontab 秒級定時任務、Translation 國際化、Validation 驗證器 等非常便捷的功能,滿足豐富的技術場景和業務場景,開箱即用。%
本作品採用《CC 協議》,轉載必須註明作者和本文連結