安装,
一般开发都是windows,所以用虚拟机或docker
使用
启动
php bin/hyperf.php start
如果出现端口被占用,下面的处理方法
- 查看9501端口那个进程在占用
netstat -anp|grep 9501
2. kill掉
kill 18
- 然后再启动即可
热更新
Watcher 组件除了解决上述启动问题,还提供了文件修改后立马重启的功能。
安装
composer require hyperf/watcher --dev
发布配置
php bin/hyperf.php vendor:publish hyperf/watcher
发布配置后在目录config/autoload/下自动生成watcher.php文件
配置 默认值 备注
driver | ScanFileDriver | 默认定时扫描文件驱动 |
---|---|---|
bin | PHP_BINARY | 用于启动服务的脚本 |
watch.dir | app, config | 监听目录 |
watch.file | .env | 监听文件 |
watch.interval | 2000 | 扫描间隔(毫秒) |
ext | .php, .env | 监听目录下的文件扩展名 |
启动
hyperf框架可以使用hyperf/watcher进行热更新操作,不用每次修改完代码都重启服务
注意:
使用php bin/hyperf.php server:watch这个命令后,php bin/hyperf.php start 这个命令将废弃
php bin/hyperf.php server:watch
路由
配置文件路由
<?php
use Hyperf\HttpServer\Router\Router;
// 此处代码示例为每个示例都提供了三种不同的绑定定义方式,实际配置时仅可采用一种且仅定义一次相同的路由
// 设置一个 GET 请求的路由,绑定访问地址 '/get' 到 App\Controller\IndexController 的 get 方法
Router::get('/get', 'App\Controller\IndexController::get');
Router::get('/get', 'App\Controller\IndexController@get');
Router::get('/get', [\App\Controller\IndexController::class, 'get']);
// 设置一个 POST 请求的路由,绑定访问地址 '/post' 到 App\Controller\IndexController 的 post 方法
Router::post('/post', 'App\Controller\IndexController::post');
Router::post('/post', 'App\Controller\IndexController@post');
Router::post('/post', [\App\Controller\IndexController::class, 'post']);
// 设置一个允许 GET、POST 和 HEAD 请求的路由,绑定访问地址 '/multi' 到 App\Controller\IndexController 的 multi 方法
Router::addRoute(['GET', 'POST', 'HEAD'], '/multi', 'App\Controller\IndexController::multi');
Router::addRoute(['GET', 'POST', 'HEAD'], '/multi', 'App\Controller\IndexController@multi');
Router::addRoute(['GET', 'POST', 'HEAD'], '/multi', [\App\Controller\IndexController::class, 'multi']);
路由组
Router::addGroup('/user',function (){
Router::get('/index', 'App\Controller\UserController::index');
Router::post('/create', 'App\Controller\UserController::createUser');
Router::put('/update', 'App\Controller\UserController::update');
Router::delete('/delete', 'App\Controller\UserController::delete');
});
必填参数
我们可以对 $uri
进行一些参数定义,通过 {}
来声明参数,如 /user/{id}
则声明了 id
值为一个必填参数。
可选参数
有时候您可能会希望这个参数是可选的,您可以通过[]
来声明中括号内的参数为一个可选参数,如 /user/[{id}]
。
校验参数
您也可以使用正则表达式对参数进行校验,以下是一些例子
use Hyperf\HttpServer\Router\Router;
// 可以匹配 /user/42, 但不能匹配 /user/xyz
Router::addRoute('GET', '/user/{id:\d+}', 'handler');
// 可以匹配 /user/foobar, 但不能匹配 /user/foo/bar
Router::addRoute('GET', '/user/{name}', 'handler');
// 也可以匹配 /user/foo/bar as well
Router::addRoute('GET', '/user/{name:.+}', 'handler');
// 这个路由
Router::addRoute('GET', '/user/{id:\d+}[/{name}]', 'handler');
// 等同于以下的两个路由
Router::addRoute('GET', '/user/{id:\d+}', 'handler');
Router::addRoute('GET', '/user/{id:\d+}/{name}', 'handler');
// 多个可选的嵌套也是允许的
Router::addRoute('GET', '/user[/{id:\d+}[/{name}]]', 'handler');
// 这是一条无效的路由, 因为可选部分只能出现在最后
Router::addRoute('GET', '/user[/{id:\d+}]/{name}', 'handler');
注解路由
例如:
<?php
/**
* OrderController.php
*
* Created on Orders-11:19
* Created by xxp 332410549@qq.com
*/
namespace App\Controller;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\GetMapping;
use Hyperf\HttpServer\Contract\RequestInterface;
/**
* @Controller(prefix="order")
*/
class OrderController extends AbstractController
{
/**
* @GetMapping(path="index")
*/
public function index(RequestInterface $request)
{
return 'OrderController'.$request->input('id');
}
}
访问地址
http://hyperf.demos.xp:9501/order/index
HTTP 异常
在路由匹配不到路由时,如 路由找不到(404)
、请求方法不允许(405)
等 HTTP 异常,Hyperf 会统一抛出一个 Hyperf\HttpMessage\Exception\HttpException
异常类的子类,
您需要通过 ExceptionHandler
机制来管理这些异常并做对应的响应处理,默认情况下可以直接使用组件提供的 Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler
来进行异常捕获处理,注意这个异常处理器需要您自行配置到 config/autoload/exceptions.php
配置文件中去,并保障多个异常处理器之间的顺序链路是正确的。
当您需要对 路由找不到(404)
、请求方法不允许(405)
等 HTTP 异常情况的响应进行自定义处理时,您可直接根据 HttpExceptionHandler
的代码实现您自己的异常处理器,并配置您自己的异常处理器。关于异常处理器的逻辑和使用说明,可具体查阅 异常处理
异常处理器
在 Hyperf 里,业务代码都运行在 Worker 进程 上,也就意味着一旦任意一个请求的业务存在没有捕获处理的异常的话,都会导致对应的 Worker 进程 被中断退出,这对服务而言也是不能接受的,捕获异常并输出合理的报错内容给客户端也是更加友好的。
我们可以通过对各个 server 定义不同的 异常处理器(ExceptionHandler),一旦业务流程存在没有捕获的异常,都会被传递到已注册的 异常处理器(ExceptionHandler) 去处理。
自定义一个异常处理
1. 通过配置文件注册异常处理器
config/autoload/exceptions.php 文件
<?php
// config/autoload/exceptions.php
return [
'handler' => [
'http' => [
Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler::class,
App\Exception\Handler\AppExceptionHandler::class,
App\Exception\Handler\FooExceptionHandler::class,
],
],
];
2. 定义异常处理器
app/Exception/Handler/FooExceptionHandler.php
<?php
/**
* FooExceptionHandler.php
*
* Created on ExceptionHandler -13:35
* Created by xxp 332410549@qq.com
*/
namespace App\Exception\Handler;
use App\Exception\FooException;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use Throwable;
class FooExceptionHandler extends ExceptionHandler
{
/**
* @inheritDoc
*/
public function handle(Throwable $throwable, ResponseInterface $response)
{
// 判断被捕获到的异常是希望被捕获的异常
if ($throwable instanceof FooException) {
// 格式化输出
$data = json_encode([
'code' => $throwable->getCode(),
'message' => $throwable->getMessage(),
], JSON_UNESCAPED_UNICODE);
// 阻止异常冒泡
$this->stopPropagation();
return $response->withStatus(500)->withBody(new SwooleStream($data));
}
// 交给下一个异常处理器
return $response;
}
/**
* @inheritDoc
*/
public function isValid(Throwable $throwable): bool
{
return true;
}
}
3. 定义异常类
app/Exception/FooException.php
<?php
/**
* FooException.php
*
* Created on FooException-13:40
* Created by xxp 332410549@qq.com
*/
namespace App\Exception;
use Hyperf\Server\Exception\ServerException;
class FooException extends ServerException
{
}
4. 触发异常
<?php
/**
* OrderController.php
*
* Created on Orders-11:19
* Created by xxp 332410549@qq.com
*/
namespace App\Controller;
use App\Exception\FooException;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\GetMapping;
use Hyperf\HttpServer\Contract\RequestInterface;
/**
* @Controller(prefix="order")
*/
class OrderController extends AbstractController
{
/**
* @GetMapping(path="index")
*/
public function index(RequestInterface $request)
{
try {
$data = [
'code' => 0,
'msg' => '获取成功',
'data' => [
'orders' => '订单列表'
]
];
if (1) { // 如果没有数据,返回空数组
throw new FooException('自定义异常');
}
return $this->response->json($data);
} catch (\Exception $e) {
return $this->response->json(['code' => 1,'msg' => $e->getMessage()]);
}
}
}
查看解决
总结:
在上面这个例子,我们先假设 FooException
是存在的一个异常,以及假设已经完成了该处理器的配置,那么当业务抛出一个没有被捕获处理的异常时,就会根据配置的顺序依次传递,整一个处理流程可以理解为一个管道,若前一个异常处理器调用 $this->stopPropagation()
则不再往后传递,若最后一个配置的异常处理器仍不对该异常进行捕获处理,那么就会交由 Hyperf 的默认异常处理器处理了。
Error 监听器
框架提供了 error_reporting()
错误级别的监听器 Hyperf\ExceptionHandler\Listener\ErrorExceptionHandler
。
配置
在 config/autoload/listeners.php 中添加监听器
<?php
return [
\Hyperf\ExceptionHandler\Listener\ErrorExceptionHandler::class
];
抓取错误
则当出现类似以下的代码时会抛出 \ErrorException 异常
public function index(RequestInterface $request)
{
try {
$a = [];
($a[1]);
return $this->response->json($data);
} catch (\Throwable $e) {
return $this->response->json(['code' => 1,'msg' => $e->getMessage() ."\n num:". $e->getLine()]);
}
}
结果
Ioc & Di
注入方式
简单注入
通过构造函数注入
Inject 注解
需要和下面 @var UserService 配置 UserService 类的类型
工厂对象注入
config/dependencies.php
php常用命令
查看php运行环境
可以看到php.ini文件所在的位置
php -i | grep ini
查看php某个扩展的详细详细信息
php --ri xxx
php --ri swoole
压测工具
ab -k -c 100 -n 10000 http:127.0.0.01:9501
选项 | 作用 |
---|---|
-n | 在测试会话中所执行的请求个数。默认时,仅执行一个请求。 |
-c | 一次产生的请求个数。默认是一次一个。 |
-t | 测试所进行的最大秒数。其内部隐含值是-n 50000,它可以使对服务器的测试限制在一个固定的总时间以内。默认时,没有时间限制。 |
-p | 包含了需要POST的数据的文件。 |
-P | 对一个中转代理提供BASIC认证信任。用户名和密码由一个:隔开,并以base64编码形式发送。无论服务器是否需要(即, 是否发送了401认证需求代码),此字符串都会被发送。 |
-T | POST数据所使用的Content-type头信息。 |
-v | 设置显示信息的详细程度-4或更大值会显示头信息,3或更大值可以显示响应代码(404,200等),2或更大值可以显示警告和其他信息。 |
-V | 显示版本号并退出。 |
-w | 以HTML表的格式输出结果。默认时,它是白色背景的两列宽度的一张表。 |
-i | 执行HEAD请求,而不是GET。 |
-x | 设置属性的字符串。
|
-X | 对请求使用代理服务器。 |
-y | 设置属性的字符串。 |
-z | 设置属性的字符串。 |
-C | 对请求附加一个Cookie:行。其典型形式是name=value的一个参数对,此参数可以重复。 |
-H | 对请求附加额外的头信息。此参数的典型形式是一个有效的头信息行,其中包含了以冒号分隔的字段和值的对(如,“Accept-Encoding:zip/zop;8bit”)。 |
-A | 对服务器提供BASIC认证信任。用户名和密码由一个:隔开,并以base64编码形式发送。无论服务器是否需要(即,是否发送了401认证需求代码),此字符串都会被发送。 |
-h | 显示使用方法。 |
-d | 不显示"percentage served within XX [ms] table"的消息(为以前的版本提供支持)。 |
-e | 产生一个以逗号分隔的(CSV)文件,其中包含了处理每个相应百分比的请求所需要(从1%到100%)的相应百分比的(以微妙为单位)时间。由于这种格式已经“二进制化”,所以比’gnuplot’格式更有用。 |
-g | 把所有测试结果写入一个’gnuplot’或者TSV(以Tab分隔的)文件。此文件可以方便地导入到Gnuplot,IDL,Mathematica,Igor甚至Excel中。其中的第一行为标题。 |
-i | 执行HEAD请求,而不是GET。 |
-k | 启用HTTP KeepAlive功能,即在一个HTTP会话中执行多个请求。默认时,不启用KeepAlive功能。 |
-q | 如果处理的请求数大于150,ab每处理大约10%或者100个请求时,会在stderr输出一个进度计数。此-q标记可以抑制这些信息。 |
-r | 在遇到socket接收错误后,不退出测试 |
yum 安装
如果没安装这个工具,可执行下面的命令
yum -y install httpd-tools #这样安装使其不能支持大于20000的并发数
yum -y install httpd-tools
源码安装
yum -y install gcc gcc-c++ automake apr apr-util pcre apr-devel apr-util-devel pcre-devel #安装httpd依赖
wget https://mirror.bit.edu.cn/apache//httpd/httpd-2.4.43.tar.gz #下载httpd包
tar -zxvf httpd-2.4.43.tar.gz -C /usr/src #解压httpd包
cd /usr/src/httpd-2.4.43 #切换目录到httpd包所在
./configure --prefix=/usr/local/httpd #编译
make && make install #安装
ln -s /usr/local/httpd/bin/* /usr/bin #做软链接
ulimit -n 65535 #修改同时最大打开的文件数,此为临时性的