欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Swoole 整合成一个小框架

程序员文章站 2022-04-28 15:12:53
[TOC] 概述 这是关于 Swoole 学习的第六篇文章:Swoole 整合成一个小框架。 "第五篇:Swoole 多协议 多端口 的应用" "第四篇:Swoole HTTP 的应用" "第三篇:Swoole WebSocket 的应用" "第二篇:Swoole Task 的应用" "第一篇:Sw ......

目录

概述

这是关于 swoole 学习的第六篇文章:swoole 整合成一个小框架。

写了关于 swoole 入门的 5 篇文章后,增加了不少的关注者,也得到了一些大佬的鼓励,也有很多关注者都加了微信好友,交流之后发现一些朋友比我优秀还比我努力,也得到了一些大佬的建议。

发现持续写文章真的不是件容易的事,担心别人认为没价值,担心想法太幼稚或有漏洞被别人笑话,担心肚子里墨水太少,写不出来... 知道自己思路还不够清晰,逻辑还不够严谨,告诉自己没关系,一些都会好起来的,逆境才能成长嘛,敢写就是好的开始,以此来激励自己持续的学习和思考。

跑题了,说回正题。

这篇文章其实是读者的小小要求,事情是这样的:

读者:“亮哥,看了你的文章很有收获,将文章 demo 放在本地直接就能运行了,太感谢了”

本人:“哈哈。。。有收获就好,感谢支持 ~ ”

读者:“我有一个小小的要求,现在每个文件都是独立的,我想部署到生产环境,想操作上更便捷,有日志...”

本人:“你说的不是框架吗?现在有很多现成的,看 swoole 官网推荐的 swoft、easyswoole、mixphp 等。详细的参考这个地址:https://wiki.swoole.com/wiki/page/p-open_source.html”

读者:“看了,发现文件太多了,看不懂,你能帮忙讲解下吗?”

本人:“what?我也是入门呀,要不我搞个简单的*吧”

......

于是就有了这篇文章,正好也是对前面 5 篇文章的复习吧。

效果

Swoole 整合成一个小框架
Swoole 整合成一个小框架

命令如下:

  • 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
  • ...

需要源码的,加我微信吧。(菜单-> 加我微信-> 扫我)

本文欢迎转发,转发请注明作者和出处,谢谢!