Swoole 整合成一个小框架
目录
概述
这是关于 swoole 学习的第六篇文章:swoole 整合成一个小框架。
- 第五篇:swoole 多协议 多端口 的应用
- 第四篇:swoole http 的应用
- 第三篇:swoole websocket 的应用
- 第二篇:swoole task 的应用
- 第一篇:swoole timer 的应用
写了关于 swoole 入门的 5 篇文章后,增加了不少的关注者,也得到了一些大佬的鼓励,也有很多关注者都加了微信好友,交流之后发现一些朋友比我优秀还比我努力,也得到了一些大佬的建议。
发现持续写文章真的不是件容易的事,担心别人认为没价值,担心想法太幼稚或有漏洞被别人笑话,担心肚子里墨水太少,写不出来... 知道自己思路还不够清晰,逻辑还不够严谨,告诉自己没关系,一些都会好起来的,逆境才能成长嘛,敢写就是好的开始,以此来激励自己持续的学习和思考。
跑题了,说回正题。
这篇文章其实是读者的小小要求,事情是这样的:
读者:“亮哥,看了你的文章很有收获,将文章 demo 放在本地直接就能运行了,太感谢了”
本人:“哈哈。。。有收获就好,感谢支持 ~ ”
读者:“我有一个小小的要求,现在每个文件都是独立的,我想部署到生产环境,想操作上更便捷,有日志...”
本人:“你说的不是框架吗?现在有很多现成的,看 swoole 官网推荐的 swoft、easyswoole、mixphp 等。详细的参考这个地址:https://wiki.swoole.com/wiki/page/p-open_source.html”
读者:“看了,发现文件太多了,看不懂,你能帮忙讲解下吗?”
本人:“what?我也是入门呀,要不我搞个简单的*吧”
......
于是就有了这篇文章,正好也是对前面 5 篇文章的复习吧。
效果
命令如下:
- php index.php 可以查看到上图
- php index.php start 开启服务(debug模式)
- php index.php start -d 开启服务(daemon模式)
- php index.php status 查看服务状态
- php index.php reload 服务热加载
- php index.php stop 关闭服务
index.php 这是文件名称,大家叫什么都可以。
目录结构如下:
├─ controller │ ├── ... ├─ client │ ├─ websocket │ ├── ... │ ├─ tcp │ ├── ... ├─ server │ ├─ config │ ├── config.php │ ├─ core │ ├── common.php │ ├── core.php │ ├── handlerexception.php │ ├─ log -- 需要 读/写 权限 │ ├── ... ├─ index.php
目前就这几个文件,后期研究新的知识点会直接集成到这里面。
说说实现了什么:
1、启动了 websocket、http、tcp、udp 等服务。
2、websocket 例子,在 client/websocket 文件夹,实现了视频弹幕。
3、http 例子,在浏览器直接访问:http://ip:port,逻辑代码在 controller 文件夹。
4、tcp 例子,在 client/tcp 文件夹。
5、udp 例子,直接运行 netcat -u ip port
即可。
6、相关配置,在 server/config 文件夹。
代码
放不全,就放一个主要的文件吧(core.php)。
<?php if (!defined('server_path')) exit("no access"); class core { private static $serv; public function __construct() { set_error_handler(['handlerexception', 'apperror']); register_shutdown_function(['handlerexception', 'fatalerror']); } public static function run() { static::checkcli(); static::checkextension(); static::showusageui(); static::parsecommand(); } protected static function checkcli() { if (php_sapi_name() !== 'cli') { exit(output('服务只能运行在 cli sapi 模式下')); } } protected static function checkextension() { if (!extension_loaded('swoole')) { exit(output('请安装 swoole 扩展')); } } protected static function showusageui() { global $argc; if ($argc <= 1 || $argc >3) { echo php_eol; echo "----------------------------------------".php_eol; echo "| swoole |".php_eol; echo "|--------------------------------------|".php_eol; echo '| usage: php index.php commond |'.php_eol; echo '|--------------------------------------|'.php_eol; echo '| 1. start 以debug模式开启服务 |'.php_eol; echo '| 2. start -d 以daemon模式开启服务 |'.php_eol; echo '| 3. status 查看服务状态 |'.php_eol; echo '| 4. reload 热加载 |'.php_eol; echo '| 5. stop 关闭服务 |'.php_eol; echo "----------------------------------------".php_eol; echo php_eol; exit; } } protected static function parsecommand() { global $argv; $command = $argv[1]; $option = isset( $argv[2] ) ? $argv[2] : '' ; switch ($command) { case 'start': if ($option === '-d') { //以daemon形式启动 get_config(['set@daemonize' => true]); } self::workerstart(); break; case 'status': self::workerstatus(); break; case 'reload': self::workerreload(); break; case 'stop': self::workerstop(); break; default: echo "bad command.".php_eol; } } protected static function workerstart() { $config = get_config(); self::$serv = new swoole_websocket_server($config['ip'], $config['websocket_port']); self::$serv->set($config['set']); self::$serv->on('start', function ($serv) use ($config) { $start = new onstart(); $start::run($serv, $config); }); self::$serv->on('managerstart', function ($serv) use ($config) { $manager_start = new onmanagerstart(); $manager_start::run($serv, $config); }); self::$serv->on('workerstart', function ($serv, $worker_id) use ($config) { $worker_start = new onworkerstart(); $worker_start::run($serv, $worker_id, $config); }); //tcp $tcp = self::$serv->listen($config['ip'], $config['tcp_port'], swoole_sock_tcp); $tcp->set($config['tcp_set']); $tcp->on('receive', function ($serv, $fd, $reactor_id, $data) { $receive = new onreceive(); $receive::run($serv, $fd, $reactor_id, $data); }); //udp $udp = self::$serv->listen($config['ip'], $config['udp_port'], swoole_sock_udp); $udp->set($config['udp_set']); $udp->on('packet', function ($serv, $data, $client_info) { $packet = new onpacket(); $packet::run($serv, $data, $client_info); }); self::$serv->on('task', function ($serv, $task_id, $src_worker_id, $data) use ($config) { $task = new ontask(); $dataarr = json_decode($data, true); switch ($dataarr['server']) { case "tcp": $task::tcp_task_run($serv, $task_id, $src_worker_id, $data); break; case "ws": $task::ws_task_run($serv, $task_id, $src_worker_id, $data); break; } }); self::$serv->on('open', function ($serv, $request) { echo output("onopen: handshake success with fd={$request->fd}"); }); self::$serv->on('message', function ($serv, $frame) { $message = new onmessage(); $message::run($serv, $frame); }); self::$serv->on('request', function ($request, $response) { $req = new onrequest(); $req::run($request, $response); }); self::$serv->on('finish', function ($serv, $task_id, $data) { $finish = new onfinish(); $finish::run($serv, $task_id, $data); }); self::$serv->on('close', function ($serv, $fd, $reactor_id){ try { echo output('客户端关闭'); } catch(exception $e) { } }); self::$serv->on('shutdown', function ($serv) { echo output("服务关闭"); }); self::showprocessui(); self::$serv->start(); } protected static function workerstatus() { $config = get_config(); if (!file_exists($config['master_pid_file']) || !file_exists($config['manager_pid_file']) || !file_exists($config['worker_pid_file']) ) { echo output("暂无启动的服务"); return false; } self::showprocessui($config); $masterpidstring = trim(@file_get_contents($config['master_pid_file'])); $masterpidarr = explode( '-', $masterpidstring); echo str_pad("master", 18, ' ', str_pad_both ). str_pad($config['master_process_name'], 26, ' ', str_pad_both ). str_pad($masterpidarr[0], 16, ' ', str_pad_both ). str_pad($masterpidarr[1], 16, ' ', str_pad_both ). str_pad($masterpidarr[2], 16, ' ', str_pad_both ).php_eol; $managerpidstring = trim(@file_get_contents($config['manager_pid_file'])); $managerpidarr = explode( '-', $managerpidstring); echo str_pad("manager", 20, ' ', str_pad_both ). str_pad($config['manager_process_name'], 24, ' ', str_pad_both ). str_pad($managerpidarr[0], 16, ' ', str_pad_both ). str_pad($managerpidarr[1], 16, ' ', str_pad_both ). str_pad($managerpidarr[2], 16, ' ', str_pad_both ).php_eol; $workerpidstring = rtrim(@file_get_contents($config['worker_pid_file']), '|' ); $workerpidarr = explode( '|', $workerpidstring ); if (isset($workerpidarr) && !empty($workerpidarr)) { foreach ($workerpidarr as $key => $val) { $v = explode( '-', $val); echo str_pad("worker", 18, ' ', str_pad_both ). str_pad($config['worker_process_name'], 26, ' ', str_pad_both ). str_pad($v[0], 16, ' ', str_pad_both ). str_pad($v[1], 16, ' ', str_pad_both ). str_pad($v[2], 16, ' ', str_pad_both ).php_eol; } } $taskpidstring = rtrim(@file_get_contents($config['task_pid_file']), '|' ); $taskpidarr = explode( '|', $taskpidstring ); if (isset($taskpidarr) && !empty($taskpidarr)) { foreach ($taskpidarr as $key => $val) { $v = explode( '-', $val); echo str_pad("task", 18, ' ', str_pad_both ). str_pad($config['task_process_name'], 24, ' ', str_pad_both ). str_pad($v[0], 20, ' ', str_pad_both ). str_pad($v[1], 12, ' ', str_pad_both ). str_pad($v[2], 20, ' ', str_pad_both ).php_eol; } } } protected static function workerreload() { $config = get_config(); if (!file_exists($config['master_pid_file'])) { echo output("暂无启动的服务"); return false; } $masterpidstring = trim(file_get_contents($config['master_pid_file'])); $masterpidarr = explode( '-', $masterpidstring); if (!swoole_process::kill($masterpidarr[0], 0)) { echo output("pid:{$masterpidarr[0]} 不存在"); return false; } swoole_process::kill($masterpidarr[0], sigusr1); @unlink($config['worker_pid_file']); @unlink($config['task_pid_file']); echo output("热加载成功"); return true; } protected static function workerstop() { $config = get_config(); if (!file_exists($config['master_pid_file'])) { echo output("暂无启动的服务"); return false; } $masterpidstring = trim(file_get_contents($config['master_pid_file'])); $masterpidarr = explode( '-', $masterpidstring); if (!swoole_process::kill($masterpidarr[0], 0)) { echo output("pid:{$masterpidarr[0]} 不存在"); return false; } swoole_process::kill($masterpidarr[0]); $time = time(); while (true) { usleep(2000); if (!swoole_process::kill($masterpidarr[0], 0)) { unlink($config['master_pid_file']); unlink($config['manager_pid_file']); unlink($config['worker_pid_file']); unlink($config['task_pid_file']); echo output("服务关闭成功"); break; } else { if (time() - $time > 5) { echo output("服务关闭失败,请重试"); break; } } } return true; } protected static function showprocessui() { $config = get_config(); if ($config['set']['daemonize'] == true) { return false; } echo str_pad("-", 90, '-', str_pad_both) . php_eol; echo "|" . str_pad("启动/关闭", 92, ' ', str_pad_both) . "|" . php_eol; echo str_pad("-", 90, '-', str_pad_both) . php_eol; echo str_pad("start success.", 50, ' ', str_pad_both) . str_pad("php index.php stop", 50, ' ', str_pad_both) . php_eol; echo str_pad("-", 90, '-', str_pad_both) . php_eol; echo "|" . str_pad("版本信息", 92, ' ', str_pad_both) . "|" . php_eol; echo str_pad("-", 90, '-', str_pad_both) . php_eol; echo str_pad("swoole version:" . swoole_version, 50, ' ', str_pad_both) . str_pad("php version:" . php_version, 50, ' ', str_pad_both) . php_eol; echo str_pad("-", 90, '-', str_pad_both) . php_eol; echo "|" . str_pad("ip 信息", 90, ' ', str_pad_both) . "|" . php_eol; echo str_pad("-", 90, '-', str_pad_both) . php_eol; echo str_pad("ip:" . $config['ip'], 50, ' ', str_pad_both) . str_pad("port:" . $config['websocket_port'], 50, ' ', str_pad_both) . php_eol; echo str_pad("-", 90, '-', str_pad_both) . php_eol; echo "|" . str_pad("进程信息", 92, ' ', str_pad_both) . "|" . php_eol; echo str_pad("-", 90, '-', str_pad_both) . php_eol; echo str_pad("swoole进程", 20, ' ', str_pad_both) . str_pad('进程别名', 30, ' ', str_pad_both) . str_pad('进程id', 18, ' ', str_pad_both) . str_pad('父进程id', 18, ' ', str_pad_both) . str_pad('用户', 18, ' ', str_pad_both) . php_eol; } protected static function signalhandler() { //todo 未完成 //swoole_process::signal(sigint, function ($signal) { // echo $signal; // return; //}); } }
小结
耗费了 3 个晚上的时间,终于完成了一个初版,比较初级,希望可以给入门的同学一个参考吧。
当然我自己也会继续完善它,后期的一些新知识点会集成到这里面,做成自己迭代的小项目。
初版比较糙,不喜勿喷。
后期会新增:
- rpc
- coroutine - mysql
- coroutine - redis
- process
- ...
需要源码的,加我微信吧。(菜单-> 加我微信-> 扫我)
本文欢迎转发,转发请注明作者和出处,谢谢!
推荐阅读
-
Westore 1.0 正式发布 - 小程序框架一个就够
-
Swoole 整合成一个小框架
-
用Python 写一个用于芯片自动化测试的管理小框架
-
Westore 1.0 正式发布 - 小程序框架一个就够
-
快速了解微信小程序的使用,一个根据小程序的框架开发的todos app
-
快速了解微信小程序的使用,一个根据小程序的框架开发的todos app
-
已知有n个互不相等的整数,求它们两两组合的所有结果。输出时每行一个组合,按列表下标的字典 序排序。列表下标小的数在前,列表下标大的数在后。 输入 第1行:整数的个数n 接下来n行:n个互不相同的整
-
PHP框架Swoole的一个定时器Timer特性
-
PHP框架Swoole的一个定时器Timer特性
-
自己写了个phpMVC框架,用框架写了一个小的项目