Swover
基于Swoole的服务类库,提供基础的HTTP、TCP、Process服务能力,不包含任何业务代码,只需简单配置即可安全使用,对业务代码完全无侵入。
Install / Use
/learn @swover/SwoverREADME
Swover
Swover是一个基于Swoole扩展的服务类库,提供基础的HTTP、TCP、Process服务能力,不包含任何业务代码,只需简单配置即可安全使用,对业务代码完全无侵入。
依赖
- PHP 5.6 及之后版本
- Swoole Extension 1.9.5 及更新版本
- pcntl-extension
- posix-extension
虽然提供了对 PHP5 及 swoole 1.x 的支持,但仍建议升级到最新版。
安装
添加swover到composer.json文件,然后更新composer。
$ composer require swover/swover
$ composer update
配置项
| 配置 | 类型 | 场景 | 必填 | 默认 | 描述 |
| --------------- | :------: | :------- | :-- | :---- | ------------------------------------------------------------ |
| server_type | string | all |Y| | 服务类型,可选项:http,tcp,process |
| daemonize | bool | all | N | false | 服务是否以守护进程方式运行 |
| process_name | string | all | N | server | 服务的进程名,单节点内应唯一 |
| worker_num | int | all | N | 1 | worker 进程数 |
| task_worker_num | int | tcp,http | N | 0 | task-worker 进程数 |
| host | string | tcp,http | N | 0.0.0.0| 监听地址 |
| port | int | tcp,http | N | 0 | 监听端口,0表示随机获取一个可用端口 |
| max_request | int | all | N | 0 | 进程最大执行次数,超过时安全重启。0表示永不重启,为避免内存泄漏,建议设置 |
| entrance | string | all |Y| | 业务逻辑的入口,必须是可被调用的函数或方法,接收request返回response |
| async | bool | tcp,http | N | false | 是否异步执行,如果为true,接收到请求后,转发给task异步处理 |
| setting | array | all | N | | 配置选项 |
| events | array | all | N | | 事件注册,支持二维数组,数组下标无实际作用。事件 |
开始使用
服务类型
Swover提供了三种服务类型可用:
- Tcp:
Tcp类型的服务基于\Swoole\Server处理网络请求与响应 - Http:
Http类型的服务基于\Swoole\Http\Server处理网络请求与响应 - Process:通过
\Swoole\Process创建子进程,在进程内执行while(true)调用业务入口,需在入口内处理输入输出
通过服务启动时传入的server_type配置项决定服务类型。
启停服务
$config = [
'server_type' => 'tcp',
'daemonize' => false,
'process_name' => 'swover',
'worker_num' => 2, //会被setting内的同名配置覆盖
'task_worker_num' => 1,
'max_request' => 0,
'entrance' => '\\Entrance::process',
'host' => '127.0.0.1',
'port' => '9501',
'async' => false,
'setting' => [
'worker_num' => 3, //覆盖外层同名配置,最终服务启动时的 worker_num 为 3
'log_file' => '/tmp/swoole_tcp.log',
],
'events' => [ //事件注册可以为字符串、对象、闭包
'master_start' => [
'\MasterStartA',
new \MasterStartB(),
],
'worker_start' => '\WorkerStart',
function ($request) { },
]
];
$class = new \Swover\Server($config);
$class->start(); //启动服务
//$class->stop(); //安全的停止服务
//$class->force(); //强制停止
//$class->restart(); //安全的重启服务
//$class->reload(); //安全的重新加载服务
服务启动后,通过Request接收客户端请求,将Request对象作为参数传递给入口函数。
入口函数处理完业务逻辑后返回结果,建议返回Response对象、字符串或布尔值。
Request
服务接收到客户端数据后,构造\Swover\Utils\Request对象,构造函数接收参数为:
\Swoole\Http\Request:HTTP服务类型array:TCP服务类型
对象继承自\ArrayObject,可通过$request['get']、$request->get或$request->get()获取请求参数。
// curl http://127.0.0.1:9501/user/info?id=123
function entrance(\Swover\Utils\Request $request)
{
var_dump($request->path());
// string(10) "/user/info"
var_dump($request->get());
// array(1) {
// ["id"]=>
// string(3) "123"
// }
var_dump($request->get('name', 'ruesin'));
// string(6) "ruesin"
var_dump($request->get);
// array(1) {
// ["id"]=>
// string(3) "123"
// }
var_dump($request->get['id']);
// string(3) "123"
}
由于
Process服务只是通过while(true)调用入口函数,所以传递给入口函数的是一个空的Request对象。
Response
入口函数完成后返回数据,构造\Swover\Utils\Response对象,响应结果到客户端,数据类型:
\Swover\Utils\Response对象- 字符串:构造
Response对象,设置字符串为响应消息体; - 布尔值:构造
Response对象,如果false设置status为500,否则为200
Response 实现:
// \Swover\Server\Base::class
protected function entrance($request)
{
$result = call_user_func_array($this->entrance, [$request]);
if ($result instanceof \Swover\Contracts\Response) {
$response = $result;
} else {
$response = new Response();
}
if (is_string($result) || is_numeric($result)) {
$response->setBody($result);
}
if (is_bool($result) && $result === false) {
$response->setCode(500);
}
return $response;
}
业务入口:
// 返回字符串,将被设置为 response body
function entranceA(\Swover\Utils\Request $request)
{
return "This is response body.";
}
// 返回 false,将 http code 设置为500
function entranceB(\Swover\Utils\Request $request)
{
return false;
}
// 返回 response 对象
function entranceC(\Swover\Utils\Request $request)
{
$response = new \Swover\Utils\Response();
$response->setBody('{"status":-1,"msg":"Has Not Route!"}');
$response->setCode(404);
$response->setHeader('Content-Type', 'application/json');
return $response;
}
在
Process服务中,当status大于400时,判定终止此次循环,将重启worker进程。
Event
提供了事件机制,可以通过服务启动前传入配置 或 调用\Swover\Utils\Events注册事件。
此处提供的事件,主要是为了扩展Swoole的事件回调,事件按照注册顺序先后触发,可使用Event::before()方法将方法插入队头。
注册的回调方法:
- 自定义类:必须有表示事件类型的
EVENT_TYPE静态属性,时间触发的trigger(...$params)方法 - 闭包
function(...$params){}
除Request和Response两个事件为特殊定义,其余事件的参数均须与Swoole事件严格一致。
支持的事件类型定义在\Swover\Contracts\Events接口中,建议绑定事件时直接使用预定义常量,避免触发失败。
事件类型大小写不敏感,最终绑定均转为小写。
TCP的receive和HTTP的request事件统一为request事件,参数及使用定义在\Swover\Utils\Event\Request。
response事件参数及使用定义在\Swover\Utils\Event\Response。
$instance = \Swover\Utils\Event::getInstance();
// 绑定闭包
$instance->bindInstance(\Swover\Contracts\Events::START, 'master_start_alias_a', function (\Swoole\Server $server) {
echo "Swoole server is started\n";
});
// 绑定对象
$instance->bind(new WorkerStartEvent());
// 绑定类名,自动解析为对象
$instance->bind('WorkerStartEvent');
//自定义类,必须定义 EVENT_TYPE、trigger()
class WorkerStartEvent
{
const EVENT_TYPE = \Swover\Contracts\Events::WORKER_START;
public function trigger($server, $worker_id)
{
echo $worker_id . PHP_EOL;
}
}
// 实现了 Request 接口,接口已定义 EVENT_TYPE,只需实现 trigger()
class RequestEvent implements \Swover\Contracts\Events\Request
{
public function trigger($server, $request)
{
echo $request->path . PHP_EOL;
}
}
Worker
提供获取当前进程状态、主进程状态的方法。
父进程收到SIGCHLD信号后,子进程调用Worker::getStatus()时,会触发pcntl_signal_dispatch()注册的事件,将当前进程状态设置为false。
利用此特性,在业务内判断当前进程的状态是否需要退出。
while(true) {
if (\Swover\Worker::getStatus() == false) {
echo "Worker process is finish.";
break;
}
//do something
}
Worker::checkProcess($pid)用来检测指定进程ID是否正常存活,可用于在子进程中检测父进程的状态,当父进程不存在时退出当前进程。
while(true) {
if (\Swover\Worker::checkProcess(\Swover\Worker::getMasterPid()) == false) {
echo "Parent process has gone away.";
break;
}
//do something
}
示例
Related Skills
node-connect
354.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
112.2kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
354.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
354.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
