swoole知识点 常见问题

    本文地址:http://tongxinmao.com/Article/Detail/id/159

    server启动成功后会创建worker_num+2个进程。主进程+Manager进程+worker_num个Worker进程。

    主进程内有多个Reactor线程,基于epoll/kqueue进行网络事件轮询。收到数据后转发到worker进程去处理


    Manager进程

    对所有worker进程进行管理,worker进程生命周期结束或者发生异常时自动回收,并创建新的worker进程

    worker进程

    对收到的数据进行处理,包括协议解析和响应请求

    Task进程

    • 接受由Worker进程通过swoole_server->task/taskwait方法投递的任务

    • 处理任务,并将结果数据返回给Worker进程

    • 完全是同步阻塞模式

    • Task以多进程的方式运行

    Server就是一个工厂,那reactor就是销售,帮你接项目订单。而worker就是工人,当销售接到订单后,worker去工作生产出客户要的东西。而task_worker可以理解为行政人员,可以帮助worker干些杂事,让worker专心工作。

    进程隔离

    进程隔离也是很多新手经常遇到的问题。修改了全局变量的值,为什么不生效,原因就是全局变量在不同的进程,内存空间是隔离的,所以无效。所以使用swoole开发Server程序需要了解进程隔离问题。

    • 不同的进程中PHP变量不是共享,即使是全局变量,在A进程内修改了它的值,在B进程内是无效的

    • 如果需要在不同的Worker进程内共享数据,可以用RedisMySQL文件Swoole\TableAPCushmget等工具实现

    • 不同进程的文件句柄是隔离的,所以在A进程创建的Socket连接或打开的文件,在B进程内是无效,即使是将它的fd发送到B进程也是不可用的



    是否可以共用1个redis或mysql连接[编辑本页]

    绝对不可以。必须每个进程单独创建redis/mysql连接,其他的存储客户端同样也是如此。原因是如果共用1个连接,那么返回的结果无法保证被哪个进程处理。持有连接的进程理论上都可以对这个连接进行读写,这样数据就发生错乱了。

    所以在多个进程之间,一定不能共用连接

    • 在swoole_server中,应当在onWorkerStart中创建连接对象

    • 在swoole_process中,应当在swoole_process->start后,子进程回调函数中创建连接对象

    • 本页面所述信息对使用pcntl_fork的程序同样有效

    示例

    $serv = new swoole_server("0.0.0.0", 9502);//必须在onWorkerStart回调中创建redis/mysql连接
    $serv->on('workerstart', function($serv, $id) {    
    $redis = new redis;    $redis->connect('127.0.0.1', 6379);    $serv->redis = $redis;
    });
    $serv->on('receive', function (swoole_server $serv, $fd, $from_id, $data) { 
        $value = $serv->redis->get("key");    $serv->send($fd, "Swoole: ".$value);
    });
    $serv->start();


    由于swoole是多进程的,所以程序全局对象在代码中仅是可读的
    程序全局对象所占用的内存是共享的,不会额外占用内存


    WORKER回调里的serv参数和全局的serv是什么关系? 



    可以在onStart回调中,将$serv->master_pid和$serv->manager_pid的值保存到一个文件中。这样可以编写脚本,向这两个PID发送信号来实现关闭和重启的操作。



    遍历连接并广播

    foreach($server->connections as $fd)
    {    $server->send($fd, "hello world\n");
    }


    队列

    $queue = new SplQueue;//入队$queue->push($data);//出队$data = $queue->shift();//查询队列中的排队数量$n = count($queue);

    Array模拟队列(性能较差)

    $queue = array();//入队$queue[] = $data;//出队$data = array_shift($queue);//查询队列中的排队数量$n = count($queue);


    Swoole提供了柔性终止/重启的机制,管理员只需要向SwooleServer发送特定的信号,Server的worker进程可以安全的结束。

    • SIGTERM: 向主进程发送此信号服务器将安全终止

    • 在PHP代码中可以调用$serv->shutdown()完成此操作

    • SIGUSR1: 向管理进程发送SIGUSR1信号,将平稳地restart所有worker进程

    • 在PHP代码中可以调用$serv->reload()完成此操作

    • swoole的reload有保护机制,当一次reload正在进行时,收到新的重启信号会丢弃

    • 如果设置了user/group,Worker进程可能没有权限向master进程发送信息,这种情况下必须使用root账户,在shell中执行kill指令进行重启

    #重启所有worker进程kill -USR1 主进程PID

    1.7.7版本增加了仅重启task_worker的功能。只需向服务器发送SIGUSR2即可。

    #仅重启task进程kill -USR2 主进程PID

    平滑重启只对onWorkerStart或onReceive等在Worker进程中include/require的PHP文件有效,Server启动前就已经include/require的PHP文件,不能通过平滑重启重新加载
    对于Server的配置即$serv->set()中传入的参数设置,必须关闭/重启整个Server才可以重新加载
    Server可以监听一个内网端口,然后可以接收远程的控制命令,去重启所有worker

    Reload有效范围

    Reload操作只能重新载入Worker进程启动后加载的PHP文件,建议使用get_included_files函数来列出哪些文件是在WorkerStart之前就加载的PHP文件,在此列表中的PHP文件,即使进行了reload操作也无法重新载入。比如要关闭服务器重新启动才能生效。

    $serv->on('WorkerStart', function($serv, $workerId) {    var_dump(get_included_files()); //此数组中的文件表示进程启动前就加载了,所以无法reload});

    APC/OpCache

    如果PHP开启了APC/OpCache,reload重载入时会受到影响,有2种解决方案

    • 打开APC/OpCache的stat检测,如果发现文件更新APC/OpCache会自动更新OpCode

    • 在onWorkerStart中执行apc_clear_cache或opcache_reset刷新OpCode缓存


    EVENTLOOP

    使用了异步组件后swoole内部会有个WHILE循环检测事件,PHP程序不会退出(类似WIN32 GUI程序事件机制)

    如果要取得控制权,

    while(true)
    {    swoole_event_dispatch();
    }

    或用swoole_event_cycle或定时器加入到事件循环中

    swoole_event_exit可以退出事件

    轮询,此函数仅在Client程序中有效。

    还可将其他PHP SOCKET对象加入到事件循环中,从而将原来的同步阻塞变为异步(例子:文件系统变化检测)


    PROCESS

    创建的子进程会继承主进程的变量。 TABLE或第三方存储可实现父子进程共享变量

    进退退出后有回调可以实现自动重启

    子进程的INPUT/OUPUT变成与父进程的管道通信


    上一篇:phpmqtt
    下一篇:ESP8266-ESP8266_NONOS_SDK开发包生成的镜像文件构建步骤分析