上手,打统统用功能使用障碍,swoole相关错误调试。php
《黄朝晖:Hyperf从入门到精通系列》html
__invoke(): 类被函数式调用时触发执行node
普通函数请求和响应对象:git
public function index(RequestInterface $request, ResponseInterface $response){}
实际实例化的对象:github
HyperfHttpServerRequest
-- Context::get(ServerRequestInterface::class)
这些对象存于协程上下文资源,即时释放。注意公共对象资源的修改!数据库
@AutoController 的路由信息注入 @inject 的简单对象注入 [ == 经过构造方法注入]
# 抽象对象注入 因为 Hyperf\HttpServer\ConfigProvider.dependencies [ RequestInterface::class => Request::class ResponseInterface::class => Response::class ] 在控制器接收方法里,直接使用抽象对象即依赖自实例对象
AOP(Aspect Oriented Programming):面向切片编程编程
App\Annotation\FooAnnotation.php /** * @Annotation * @Target({"CLASS","METHOD","PROPERTY"}) */ class FooAnnotation extends AbstractAnnotation{} -- 添加到注解树 @Annotation -把类路径、方法、属性添加到注解树 注解是添做使用类(当前类)的一部分
App\Aspect\FooSpect.php /** * @Aspect */ class FooSpect extends AbstractAspect{ //定义切入类 public $classes = []; /** 注解引入、重写切片“环绕”处理方法 * @param ProceedingJoinPoint $proceedingJoinPoint * @return mixed|void */ public function process(ProceedingJoinPoint $proceedingJoinPoint){} } -- 添加切片类、方法 [注解方式] 到切面树 -重写定义切入类 $classes -ProceedingJoinPoint $proceedingJoinPoint
App\Controller\FooController.php /** * @AutoController() * @FooAnnotation(bar="123", calc=11) */ class FooController{} --修改注解属性 @FooAnnotation(bar="123", calc=11) curl -v http://127.0.0.1:9501/foo/index 或 test
震惊:parallel():2个闭包函数的协程!!json
namespace App\Controller; use Hyperf\Di\Annotation\Inject; use Hyperf\Guzzle\ClientFactory; use Hyperf\HttpServer\Annotation\AutoController; use Hyperf\HttpServer\Contract\RequestInterface; class CoroutineController { /** * @Inject() * @var ClientFactory */ private $clientFactory; public function sleep(RequestInterface $request) { $sec = $request->query('second',1); sleep($sec); return $sec; } /** Parallel 特性 * 便捷版 WaitGroup * @return array */ public function testCo33() { $time = (float) time() + floatval(microtime()); $result = parallel([ function () { $client = $this->clientFactory->create(); $client->get('127.0.0.1:9501/Coroutine/sleep?second=1'); return '123: '. \Hyperf\Utils\Coroutine::id(); }, function () { $client = $this->clientFactory->create(); $client->get('127.0.0.1:9501/Coroutine/sleep?second=2'); return '321: '. \Hyperf\Utils\Coroutine::id(); } ]); return [__FUNCTION__.' ok: '. round((float) time() + floatval(microtime()) - $time,4), $result]; } /** Concurrent 协程运行控制 * 高级控制版 WaitGroup * @return array */ public function testCo9() { $time = (float) time() + floatval(microtime()); $concurrent = new \Hyperf\Utils\Coroutine\Concurrent(5); $result = []; for ($i = 0; $i < 15; ++$i) { $concurrent->create(function () use ($i, &$result) { $client = $this->clientFactory->create(); $client->get('127.0.0.1:9501/Coroutine/sleep?second='. ($i%5+1)); $result[] = [$i. ': '. \Hyperf\Utils\Coroutine::id()]; return 1; }); } //因为并行机制 句柄移交的缘由, 这里result结果输出数是 15-5=12 个 return [__FUNCTION__.' ok: '. round((float) time() + floatval(microtime()) - $time,4), $result]; } }
本文实例:CoroutineController.phpswoole
与切片同理,闭包
/* 添加中间件多个、一个 * @Middlewares({ * @Middleware(Test1MIddleware::class), * @Middleware(Test2Middleware::class) * }) */ class MidController{}
修改传值注意:保存到 Context会话对象
。
Hyperf\Utils\Context::set(ServerRequestInterface::class, $request->withAttribute()...) App\Controller\MidController.php /** curl -v http://127.0.0.1:9501/mid/index * b. 方法中间件 * @Middleware(FooMiddleware::class) */ public function index(RequestInterface $request){} App\Middleware\FooMiddleware.php class FooMiddleware implements MiddlewareInterface { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { // $request 和 $response 为修改后的对象 $request = Context::set(ServerRequestInterface::class, $request->withAttribute('foo', 'test2 set into value a...')); $response = $handler->handle($request); $body = $response->getBody()->getContents(); echo __CLASS__ .__LINE__.PHP_EOL; return $response->withBody(new SwooleStream($body. PHP_EOL. ' in func see Foo deal.')); } }
执行顺序是:先入、先执行、后出。
composer create-project hyperf/hyperf-skeleton rpc-server cp rpc-server rpc-client -r
[文件夹 rpc-server]
App\Rpc\CalculatorService.php: use Hyperf\RpcServer\Annotation\RpcService; /** * @RpcService(name="CalculatorService", protocol="jsonrpc-http", server="rpc", publishTo="consul") */ class CalculatorService implements CalculatorServiceInterface { public function add(int $a, int $b): int { return $a + $b; } public function minus(int $a, int $b): int { return $a - $b; } }
@RpcService 引用RpcService类
这里protocol="jsonrpc-http/tcp", server配置在config/autoload/server.php的servers中,publishTo="consul"注册到consul。
在编辑器中右键,Refactor导出接口CalculatorServiceInterface.php[本例到相同目录]。
server.php: "servers"=> #添加或替换 [ 'name' => 'rpc', //与CalculatorService.php:server="rpc"相同 'type' => Server::SERVER_HTTP, 'host' => '0.0.0.0', 'port' => 9600, 'sock_type' => SWOOLE_SOCK_TCP, 'callbacks' => [ SwooleEvent::ON_REQUEST => [\Hyperf\JsonRpc\HttpServer::class, 'onRequest'], ], ],
添加依赖后,接口的对象自动实例化
config/autoload/dependencies.php: /** * 接口的实体化依赖 */ App\Rpc\CalculatorServiceInterface::class => App\Rpc\CalculatorService::class
[文件夹 rpc-client]
接口文件一样放到 appRpc 下
class CalcController extends AbstractController { /** * @Inject() * @var CalculatorServiceInterface */ private $calcService; public function add(){ return $this->calcService->add(12, 56); } public function minus(){ return $this->calcService->minus(23, 78); } }
添加 config/autoload/services.php 参考官方添加:
'consumers'=>[ 'name' => 'CalculatorService', 'service' => \App\Rpc\CalculatorServiceInterface::class, 'registry' => [ 'protocol' => 'consul', 'address' => 'http://172.10.1.11:8500', //服务群端主节点 ], 'nodes' => [ ['host' => '172.10.1.22', 'port' => 8500], //备用:客户群群端节点 ], ]
php rpc-server/bin/hyperf.php start [9600,发布到8500] php rpc-client/bin/hyperf.php start [9501,注册到8500] curl -v http://127.0.0.1:9501/calc/add //和minus
命令行生成模板文件:
php bin/hyperf.php list php bin/hyperf.php gen:SendSmsListener
/** * @Inject() * @var EventDispatcherInterface */ private $eventDispatcher; //引入事件监听分发类 //用户注册以前 $beforeRegister = new BeforeRegister(); $this->eventDispatcher->dispatch($beforeRegister); if($beforeRegister->shouldRegister){ //注册用户 $userId = rand(1,99999); } //注册成功后 if($userId){ $this->eventDispatcher->dispatch(new UserRegistered($userId)); }
Listener:
/** 权重默认是1 * @Listener(priority=2) */ class SendSmsListener implements ListenerInterface { public function listen(): array { return [ UserRegistered::class ]; } /** * @param UserRegistered $event */ public function process(object $event) { echo '发送短信给'. $event->userId .PHP_EOL; } } class VaildRegisterListener implements ListenerInterface { public function listen(): array { return [ BeforeRegister::class ]; } /** * @param BeforeRegister $event */ public function process(object $event) { $event->shouldRegister = (bool) rand(0,2); echo '注册身份验证'. ($event->shouldRegister ? '经过' : '失败') .PHP_EOL; } }
Event:
class UserRegistered { public $userId; public function __construct(int $userId) { $this->userId = $userId; } } class BeforeRegister { public $shouldRegister = false; }
Controller:
class ListenController { /** * @Inject() * @var UserService */ public $userService; public function test() { return $this->userService->register(); } }
实例代码上传:
https://github.com/cffycls/cl...
文件上传、接收,[这里文件类型判断须要启用fileinfo扩展]:
客户端 $body = [ 'multipart' => [ [ 'name' => 'data', 'contents' => '{"field_1":"Test","field_2":"Test","field_3":"Test"}', 'headers' => [ 'Content-Type' => 'application/json', ], ], [ 'name' => 'file', 'filename' => 'README.md', 'Mime-Type' => 'application/text', 'contents' => file_get_contents('./README.md'), ] ] ]; $res = (new GuzzleHttp\Client())->request('POST', 'http://127.0.0.1:9501/guzzle_client/write', $body); 服务端: $files = $this->request->getUploadedFiles(); //var_dump($files); foreach ($files as $f => $fileObj){ //2者等效,同一对象 $file = $this->request->file($f); var_dump($file->getMimeType()); $fileInfo = $file->toArray(); var_dump($fileInfo); echo '如下是接收的文件内容: '. PHP_EOL; var_dump( file_get_contents($fileInfo['tmp_file']) ); if(file_exists('/tmp/README.md.tmp')){ echo '文件已存在: '. PHP_EOL; }else{ $file->moveTo('/tmp/README.md.tmp'); //保存文件 // 经过 isMoved(): bool 方法判断方法是否已移动 if ($file->isMoved()) { echo $fileInfo['name'] .'文件已上传 '. PHP_EOL; unlink('/tmp/README.md.tmp'); return $fileInfo; } } }
路由,事件,日志,命令,数据库,依赖注入容器,服务,客户端,消息队列,配置中心,RPC,服务治理,定时任务,ID 生成器,文档生成,Graphql,热更新/热重载,Swoole,开发调试,权限认证,第三方 SDK
移步官网 组件列表