swoole 学习日记 One
定义:
不扯犊子,swoole就是php的一个扩展(C编写的)
一般我们php 用于web端开发 对于http协议是非常清楚的,但是要想java那样 做一些socket 之类 的是实时的、在线的这种,如果我们使用http协议的话 只能使用ajax轮询,对于服务器来说得不偿失,这个时候就是swoole派上用场的时候,他可以实现php 做游戏、聊天室等之类的实时通讯的东西,大家也知道其实不只swoole可以让PHP实现这些功能,例如:workerman、meepops (PHP编写)都是非常不错的,相对于swoole来说这些对phper学习成本较高(就是有难度),但是swoole因为是纯c编写的比php编写的workerman、meepops来说速度功能方面要更加出色!!
1、环境安装(swoole扩展)
cd /usr/local/src wget https://github.com/swoole/swoole-src/archive/v1.9.17.tar.gz tar -zxvf v1.9.17.tar.gz cd swoole-src-1.9.17/ /usr/local/php/bin/phpize ./configure --with-php-config=/usr/local/php/bin/php-config make && make install vim /usr/local/php/etc/php.ini extension=swoole.so service php-fpm reload2、入门教程网址 点击打开链接
包含模块:
swoole_server
强大的TCP/UDP Server框架,多线程,EventLoop,事件驱动,异步,Worker进程组,Task异步任务,毫秒定时器,SSL/TLS隧道加密。
- swoole_http_server是swoole_server的子类,内置了Http的支持
- swoole_websocket_server是swoole_http_server的子类,内置了WebSocket的支持
swoole_client
TCP/UDP客户端,支持同步并发调用,也支持异步事件驱动。
swoole_event
EventLoop API,让用户可以直接操作底层的事件循环,将socket,stream,管道等Linux文件加入到事件循环中。
eventloop接口仅可用于socket类型的文件描述符,不能用于磁盘文件读写
swoole_async
异步IO接口,提供了 异步文件系统IO,异步DNS查询,异步MySQL等API。包括2个重要的子模块:
- swoole_timer,异步毫秒定时器,可以实现间隔时间或一次性的定时任务
- file,文件系统操作的异步接口
swoole_process
进程管理模块,可以方便的创建子进程,进程间通信,进程管理。
swoole_buffer
强大的内存区管理工具,像C一样进行指针计算,又无需关心内存的申请和释放,而且不用担心内存越界,底层全部做好了。
swoole_table
基于共享内存和自旋锁实现的超高性能内存表。彻底解决线程,进程间数据共享,加锁同步等问题。
swoole_table的性能可以达到单线程每秒读写50W次
编程注意点:
进程隔离
进程隔离也是很多新手经常遇到的问题。修改了全局变量的值,为什么不生效,原因就是全局变量在不同的进程,内存空间是隔离的,所以无效。所以使用swoole开发Server程序需要了解进程隔离问题。
- 不同的进程中PHP变量不是共享,即使是全局变量,在A进程内修改了它的值,在B进程内是无效的
- 如果需要在不同的Worker进程内共享数据,可以用Redis、MySQL、文件、SwooleTable、APCu、shmget等工具实现
- 不同进程的文件句柄是隔离的,所以在A进程创建的Socket连接或打开的文件,在B进程内是无效,即使是将它的fd发送到B进程也是不可用的
onStart事件在Master进程的主线程中被调用。在此回调响应之前Swoole Server已进行了如下操作
- 已创建了manager进程
- 已创建了worker子进程
- 已监听所有TCP/UDP端口
- 已监听了定时器
接下来要执行
- 主Reactor开始接收事件,客户端可以connect到Server
从1.7.5+ Master进程内不再支持定时器,onMasterConnect/onMasterClose2个事件回调也彻底移除。Master进程内不再保留任何PHP的接口。
在onStart中创建的全局资源对象不能在worker进程中被使用,因为发生onStart调用时,worker进程已经创建好了。新创建的对象在主进程内,worker进程无法访问到此内存区域,因此全局对象创建的代码需要放置在swoole_server_start之前。
3.onWorkerStart
描述:Worker进程启动的回调
函数原型:
function onWorkerStart( swoole_server $serv,int $worker_id);
参数 | 描述 |
---|---|
$serv | swoole_server对象 |
$worker_id | Worker进程的id |
说明:此事件在worker进程/task_worker启动时发生。
发生PHP致命错误或者代码中主动调用exit时,Worker/Task进程会退出,管理进程会重新创建新的进程 onWorkerStart/onStart是并发执行的,没有先后顺序
通过$worker_id参数的值来,判断worker是普通worker还是task_worker。$worker_id>= $serv->setting["worker_num"] 时表示这个进程是task_worker。
如果想使用swoole_server_reload实现代码重载入,必须在workerStart中require你的业务文件,而不是在文件头部。在onWorkerStart调用之前已包含的文件,不会重新载入代码。
可以将公用的,不易变的php文件放置到onWorkerStart之前。这样虽然不能重载入代码,但所有worker是共享的,不需要额外的内存来保存这些数据。
onWorkerStart之后的代码每个worker都需要在内存中保存一份 $worker_id是一个从0-$worker_num之间的数字,表示这个worker进程的ID $worker_id和进程PID没有任何关系
4.onConnect
描述:新连接接入时的回调
函数原型:
function onConnect( swoole_server $serv,int $fd, int $from_id);
参数 | 描述 |
---|---|
$serv | swoole_server对象 |
$fd | 连接的描述符 |
$from_id | reactor的id,无用 |
说明:有新的连接进入时,在worker进程中回调。onConnect/onClose这2个回调发生在worker进程内,而不是主进程。如果需要在主进程处理连接/关闭事件,请注册onMasterConnect/onMasterClose回调。onMasterConnect/onMasterClose回调总是先于onConnect/onClose被执行
5.onClose
描述:连接关闭时的回调
函数原型:
function onClose( swoole_server $serv,int $fd, int $from_id);
参数 | 描述 |
---|---|
$serv | swoole_server对象 |
$fd | 连接的描述符 |
$from_id | reactor的id,无用 |
说明:TCP客户端连接关闭后,在worker进程中回调此函数。无论close由客户端发起还是服务器端主动调用swoole_server_close关闭连接,都会触发此事件。 因此只要连接关闭,就一定会回调此函数。
6.onTask
描述:task_worker进程处理任务的回调
函数原型:
function onTask(swoole_server $serv, int $task_id, int $from_id, string $data);
参数 | 描述 |
---|---|
$serv | swoole_server对象 |
$task_id | 任务ID |
$from_id | 来自于哪个worker进程 |
$data | 任务内容 |
说明:在task_worker进程内被调用。worker进程可以使用swoole_server_task函数向task_worker进程投递新的任务。可以直接将任务结果字符串通过return方式返回给worker进程。worker进程将在onFinish回调中收到结果。注:如果serv->set(array("task_worker_num" => 8)) task_id 并不是从1-8 而是递增的。
7.onFinish
描述:task_worker进程处理任务结束的回调
函数原型:
function onFinish(swoole_server $serv, int $task_id, string $data);
参数 | 描述 |
---|---|
$serv | swoole_server对象 |
$task_id | 任务ID |
$data | 任务结果 |
说明:在此函数中会收到任务处理的结果,通过task_id和worker_id来区分不同的任务。
8.onTimer
描述:定时器触发的回调
函数原型:
function onTimer(swoole_server $serv, int $interval);
参数 | 描述 |
---|---|
$serv | swoole_server对象 |
$interval | 定时的间隔 |
说明:定时器被触发时,该函数被调用。通过interval来区分不同时间间隔的定时器。
php 服务端 demo:
<?php class Server { private $serv; public function __construct() { $this->serv = new swoole_websocket_server("0.0.0.0", 2888); $this->serv->set(array( "worker_num" => 4, "daemonize" => false, "max_request" => 10000, "dispatch_mode" => 2, "debug_mode" => 1, "task_worker_num" => 4 )); //启动开始 $this->serv->on("Start", array($this, "onStart")); //与onStart同级 $this->serv->on("WorkerStart", array($this, "onWorkerStart")); //webSocket open 连接触发回调 $this->serv->on("open", array($this, "onOpen")); //webSocket send 发送触发回调 $this->serv->on("message", array($this, "onMessage")); //webSocket close 关闭触发回调 $this->serv->on("Close", array($this, "onClose")); //tcp连接 触发 在 webSocket open 之前回调 $this->serv->on("Connect", array($this, "onConnect")); //tcp 模式下(eg:telnet ) 发送信息才会触发 webSocket 模式下没有触发 $this->serv->on("Receive", array($this, "onReceive")); // task_worker进程处理任务的回调 处理比较耗时的任务 $this->serv->on("Task", array($this, "onTask")); // task_worker进程处理任务结束的回调 $this->serv->on("Finish", array($this, "onFinish")); // 服务开启 $this->serv->start(); } public function onStart(swoole_websocket_server $serv) { // $this->serv->tick(1000, function() { // echo 1; // }); echo "Start "; } public function onWorkerStart(swoole_websocket_server $serv,$worker_id) { //判断是worker进程还是 task_worker进程 echo 次数 是worker_num+task_worker_num if($worker_id<$this->serv->setting["worker_num"]){ echo "worder".$worker_id." "; }else{ echo "task_worker".$worker_id." "; } // echo "workerStart{$worker_id} "; } public function onOpen(swoole_websocket_server $serv,$request) { echo "server: handshake success with fd{$request->fd} "; } public function onMessage(swoole_websocket_server $serv,$frame) { echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish} "; $param = array( "fd" => $frame->fd ); $this->serv->task( json_encode( $param ) ); // $server->push($frame->fd, "this is server"); } public function onConnect( $serv, $fd, $from_id ) { echo "Client {$fd} connect "; echo "{$from_id} "; } public function onReceive( swoole_websocket_server $serv, $fd, $from_id, $data ) { echo "Get Message From Client {$fd}:{$data} "; // send a task to task worker. // $param = array( // "fd" => $fd // ); // $serv->task( json_encode( $param ) ); echo "Continue Handle Worker "; } public function onClose($serv, $fd) { echo "Client {$fd} close connection "; } public function onTask($serv, $task_id, $from_id, $data) { echo "This Task {$task_id} from Worker {$from_id} "; echo "Data: {$data} "; for ($i = 0; $i < 10; $i++) { sleep(1); echo "Taks {$task_id} Handle {$i} times... "; } $fd = json_decode($data, true)["fd"]; echo "Data in Task {$task_id}"; // $serv->send($fd, "Data in Task {$task_id}"); return "Task {$task_id}"s result"; } public function onFinish($serv,$task_id, $data) { echo "Task {$task_id} finish "; echo "Result: {$data} "; } } $server = new Server();
- 上一篇: 【Swoole】当SWOOLE遇上PHP
- 下一篇: swoole-swoole是什么能做什么