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进程内共享数据,可以用
Redis
、MySQL
、文件
、Swoole\Table
、APCu
、shmget
等工具实现不同进程的文件句柄是隔离的,所以在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程序事件机制)
如果要取得控制权,
或用swoole_event_cycle或定时器加入到事件循环中
swoole_event_exit可以退出事件
轮询,此函数仅在Client程序中有效。
还可将其他PHP SOCKET对象加入到事件循环中,从而将原来的同步阻塞变为异步(例子:文件系统变化检测)
PROCESS
创建的子进程会继承主进程的变量。 TABLE或第三方存储可实现父子进程共享变量
进退退出后有回调可以实现自动重启
子进程的INPUT/OUPUT变成与父进程的管道通信
上一篇:phpmqtt
下一篇:ESP8266-ESP8266_NONOS_SDK开发包生成的镜像文件构建步骤分析