前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >社区分享〡Workerman中利用popen实现多进程Web SSH

社区分享〡Workerman中利用popen实现多进程Web SSH

作者头像
Tinywan
发布2025-01-23 19:47:45
发布2025-01-23 19:47:45
5100
代码可运行
举报
文章被收录于专栏:开源技术小栈
运行总次数:0
代码可运行

起源

来源于群里兔子大佬 @chaz6chez 的分享,自己整理测试了下。

群友询问如何进程异步执行命令行任务,且有通知机制,想用来做web ssh,兔子大佬指导可以通过popen来实现:

群友讨论

实践

测试使用的是 workerman5.0 版本,基于兔佬提供是伪代码进行调整,使用websocket交互

在项目根目录新建start.php文件,代码如下:

代码语言:javascript
代码运行次数:0
复制
<?php

require_once __DIR__ . '/vendor/autoload.php';

date_default_timezone_set('Asia/Shanghai');

//进程池类
class ProcessPool
{
    private array $processes = [];
    public int $maxProcesses;

    public function __construct($maxProcesses = 5)
    {
        $this->maxProcesses = $maxProcesses;
    }

    //添加进程 popen打开的进程执行完会退出,不做复用
    public function add(string $command)
    {
        $process           = popen($command, 'r');
        $this->processes[] = $process;
        return $process;
    }

    // 释放进程
    public function releaseProcess($process): void
    {
        $key = array_search($process, $this->processes);
        if ($key !== false) {
            unset($this->processes[$key]); // 从活动进程池移除
        }
    }

    // 获取池中的所有进程数量
    public function processCount(): int
    {
        return count($this->processes);
    }
}

// 创建一个websocket Worker
$task = new \Workerman\Worker("websocket://0.0.0.0:3232");

// 初始化进程池
$processPool = new ProcessPool(5);

// 添加定时器,每秒打印进程数
$task->onWorkerStart = function (\Workerman\Worker $worker) use ($processPool) {
    \Workerman\Timer::add(1, function () use ($processPool) {
        var_dump('[' . date('H:i:s') . '] 活动进程数:' . $processPool->processCount());
    });
};

$task->onMessage = function (Workerman\Connection\TcpConnection $connection, $str) use ($processPool) {
    $data = json_decode($str, JSON_OBJECT_AS_ARRAY);
    $command = $data['command'] ?? '';
    if (empty($command)) {
        return $connection->send('无效命令');
    }

    //超过最大进程数时阻塞等待退出
    if ($processPool->processCount() >= $processPool->maxProcesses) {
        return $connection->send('使用进程数已达最大数,等待中...');
    }

    // 获取一个进程资源
    $process = $processPool->add($command);
    if (is_resource($process)) {

        // 创建读事件
        \Workerman\Worker::$globalEvent->onReadable($process, function ($pipe) use ($connection, $process, $processPool) {
            // 读取进程输出
            $output = fread($pipe, 8192);
            if ($output === false || feof($pipe)) {
                // 进程结束,释放资源
                fclose($pipe);
                $processPool->releaseProcess($pipe);
                $connection->send("进程结束.");
                \Workerman\Worker::$globalEvent->offReadable($process);
            } else {
                // 将输出发送给客户端
                $connection->send($output);
            }
        });

        return true;

    } else {
        return $connection->send("服务繁忙,请稍后重试.");
    }
};

\Workerman\Worker::runAll();

测试

项目根目录新建一个command.php文件,用于测试

代码语言:javascript
代码运行次数:0
复制
<?php

for ($i = 0; $i < 5; $i++) {
    sleep(1);
    echo '测试' . $i . PHP_EOL;
}

php start.php start启动项目,前端使用 【WebSocket 测试工具】进行连接,发送消息执行命令:/usr/bin/php8.2 command.php

websocket 调试工具

执行pstree -ap | grep -C 20 /usr/bin/php8.2,可以看到有 5 个进程在跑,popen 打开的command.php进程执行完成后,就会自动退出。

进程查看

总结

通过 workerman 和 popen 可以异步进程执行命令,前端再搭配xterm.js就可以做 web ssh了;当然还可以有很多适用的场景,比如用 workerman 拉起think-queue进程等等,自行拓展...

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-01-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源技术小栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 起源
  • 实践
  • 测试
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档