swover/swover

A Server-Framework based on Swoole extension

v2.0.0 2019-10-10 03:23 UTC

This package is auto-updated.

Last update: 2024-03-29 03:51:22 UTC


README

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){}

RequestResponse两个事件为特殊定义,其余事件的参数均须与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
}

示例