cl.5fy.index.php,hyperf框架采坑

目标

上手,打通通用功能使用障碍,swoole相关错误调试。

1、课程学习

php知识点:

__invoke(): 类被函数式调用时触发执行

避免cli编程的内存泄漏

普通函数请求和响应对象:

public function index(RequestInterface $request, ResponseInterface $response){}

实际实例化的对象:

HyperfHttpServerRequest

-- Context::get(ServerRequestInterface::class)

这些对象存于协程上下文资源,即时释放。注意公共对象资源的修改!

a.依赖注入

简单注入

@AutoController 的路由信息注入

@inject 的简单对象注入 [ == 通过构造方法注入]

高级注入

# 抽象对象注入

由于 Hyperf\HttpServer\ConfigProvider.dependencies [

RequestInterface::class => Request::class

ResponseInterface::class => Response::class

]

在控制器接收方法里,直接使用抽象对象即依赖自实例对象

b.切片编程

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

c.协程

震惊:parallel():2个闭包函数的协程!!

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.php]

d.中间件

与切片同理,

/* 添加中间件多个、一个

* @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.'));

}

}

执行顺序是:先入、先执行、后出。

e.RPC调用

建立项目

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

f.事件监听

命令行生成模板文件:

php bin/hyperf.php list

php bin/hyperf.php gen:listener 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();

}

}

2、应用点

a.文件上传:GuzzleClient客户端

文件上传、接收,[这里文件类型判断需要启用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;

}

}

}

b.组件列表

路由,事件,日志,命令,数据库,依赖注入容器,服务,客户端,消息队列,配置中心,RPC,服务治理,定时任务,ID 生成器,文档生成,Graphql,热更新/热重载,Swoole,开发调试,权限认证,第三方 SDK

移步官网 组件列表

小结

hyperf功能全面,文档齐全、降低了上手门槛。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值