PHP项目中如何使用Workerman?

wen PHP项目 4

PHP项目中如何高效使用Workerman?从入门到实战指南

目录导读

  1. Workerman是什么?为什么选它?
  2. 环境要求与安装步骤
  3. 核心概念:进程、连接、事件
  4. 搭建一个简单的WebSocket聊天室
  5. 结合Laravel框架实现实时消息推送
  6. 常见问题与问答
  7. 性能调优与最佳实践
  8. Workerman在项目中的落地策略

Workerman是什么?为什么选它?

核心定义

Workerman是一款纯PHP开发的开源高性能异步网络通信框架,它无需扩展、无需nginx/apache,直接基于PHP的socket和事件循环机制,支持TCP、UDP、WebSocket、HTTP等协议。

PHP项目中如何使用Workerman?

与传统的PHP-FPM模式对比

维度 PHP-FPM(传统) Workerman
运行模型 请求-响应,进程短生命周期 常驻内存,事件驱动
连接管理 每个请求新建进程 单进程可处理数千并发连接
实时推送 需轮询或依赖第三方 原生支持WebSocket/长连接
性能 受限于进程频繁创建销毁 内存常驻,毫秒级响应

适用场景

  • 即时通讯:聊天、客服、直播弹幕
  • 物联网(IoT):设备数据采集、指令下发
  • 游戏服务端:实时对战、状态同步
  • API网关:长连接聚合、负载均衡
  • 定时任务:替代crontab的常驻调度器

问答:
问:Workerman是否适合高并发场景?
答:是的,单台机器使用Workerman可支持数万并发TCP连接,同时得益于PHP的select/epoll事件轮询,CPU占用远低于同等数量的PHP-FPM进程,实测某日活百万的IM系统,使用Workerman的WebSocket服务器,单机稳定承载2万在线用户。


环境要求与安装步骤

环境要求

  • PHP版本:>=5.4(推荐4+x
  • 扩展:posixpcntl(Linux必需)、sockets(建议开启)
  • 操作系统:Linux最佳(Windows可开发,生产环境不建议)

安装方式

Composer(推荐)
composer require workerman/workerman
手动下载
  1. 访问GitHub仓库:[workerman/workerman](已替换域名)
  2. 解压后引入Autoloader.php

验证安装

创建test.php

<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
$worker = new Worker('http://0.0.0.0:8080');
$worker->onMessage = function($connection, $data) {
    $connection->send('Hello Workerman!');
};
Worker::runAll();

在终端运行:php test.php start,访问http://你的IP:8080应看到响应。

问答:
问:为什么我的Workerman启动后报错require_once找不到文件?
答:检查Composer是否已正确安装依赖,确保vendor/autoload.php存在,若是手动下载,确认Workerman.php文件路径正确,并正确注册了自动加载。


核心概念:进程、连接、事件

进程模型

  • Master进程:负责监控子进程,处理信号(如重启、停止)
  • Worker进程:实际处理业务逻辑,数量可设置(默认4个)
  • Connection对象:代表一个客户端连接,拥有sendclose等方法

事件回调

Workerman通过事件驱动业务,最常用的三个回调:

  1. onConnect:新连接建立时触发(如用户上线)
  2. onMessage:收到客户端消息时触发(核心业务入口)
  3. onClose:连接断开时触发(如用户下线清理)

示例:带心跳的TCP服务器

$worker = new Worker('tcp://0.0.0.0:5678');
$worker->onConnect = function($conn) {
    // 给每个连接设置超时检测(30秒无消息断开)
    $conn->maxSendBufferSize = 102400;
    echo "新连接: {$conn->id}\n";
};
$worker->onMessage = function($conn, $data) {
    if (trim($data) === 'ping') {
        $conn->send('pong');
    } else {
        // 业务逻辑处理
    }
};
$worker->onClose = function($conn) {
    echo "连接断开: {$conn->id}\n";
};

问答:
问:Workerman的Worker进程数如何设置最佳?
答:一般设置为CPU核心数的2~3倍,例如4核CPU可设置8~12个进程,注意:每个Worker进程独立运行,内存占用会叠加,可在Worker::$count = 4;中调整。


实战一:搭建简单的WebSocket聊天室

完整代码

<?php
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';
// 创建WebSocket服务器
$ws_worker = new Worker('websocket://0.0.0.0:2346');
$ws_worker->count = 2; // 2个进程
$ws_worker->name = 'ChatServer';
// 存储所有连接的用户信息
$connections = [];
$ws_worker->onConnect = function($connection) {
    echo "用户{$connection->id}连接\n";
};
$ws_worker->onMessage = function($connection, $data) use (&$connections) {
    // 首次消息视为用户名注册
    if (!isset($connection->username)) {
        $connection->username = trim($data);
        $connections[$connection->id] = $connection;
        // 广播新用户上线
        foreach ($connections as $conn) {
            $conn->send("系统: {$connection->username} 加入了聊天室");
        }
        return;
    }
    // 群发消息
    $message = $connection->username . ': ' . $data;
    foreach ($connections as $conn) {
        $conn->send($message);
    }
};
$ws_worker->onClose = function($connection) use (&$connections) {
    // 用户离开,广播通知
    if (isset($connection->username)) {
        unset($connections[$connection->id]);
        foreach ($connections as $conn) {
            $conn->send("系统: {$connection->username} 离开了聊天室");
        }
    }
};
Worker::runAll();

运行测试

  1. 启动:php chat.php start -d(-d表示守护进程)
  2. 浏览器打开WebSocket测试工具(如wscat),连接ws://你的IP:2346
  3. 发送第一条消息作为用户名,之后可自由聊天

问答:
问:为什么客户端连接后,服务器没有收到消息?
答:检查防火墙是否开放了2346端口,WebSocket握手需要HTTP协议升级,确保客户端使用ws://而非wss://(安全连接需额外配置SSL)。


实战二:结合Laravel框架实现实时消息推送

场景描述

用户提交表单后,需要实时通知后台管理员,传统方式需要轮询,使用Workerman可主动推送。

实现架构

  • Laravel应用:处理HTTP请求,写入消息到Redis List
  • Workerman服务:订阅Redis,当有新数据时推送到已连接的管理端

关键代码

Laravel端(消息入队)
// Controller
public function submitOrder(Request $request) {
    // 保存订单逻辑...
    Redis::lpush('new_orders', json_encode(['order_id' => 123, 'time' => now()]));
    return response()->json(['status' => 'success']);
}
Workerman端(消费队列并推送)
<?php
use Workerman\Worker;
use Workerman\Lib\Timer;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('websocket://0.0.0.0:2347');
$worker->count = 1;
$admin_connections = [];
$worker->onConnect = function($connection) use (&$admin_connections) {
    $admin_connections[$connection->id] = $connection;
    echo "管理员连接: {$connection->id}\n";
};
$worker->onClose = function($connection) use (&$admin_connections) {
    unset($admin_connections[$connection->id]);
};
// 定时器:每秒轮询Redis
Timer::add(1, function() use (&$admin_connections) {
    if (empty($admin_connections)) return;
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    $order_json = $redis->lPop('new_orders');
    if ($order_json) {
        foreach ($admin_connections as $conn) {
            $conn->send($order_json);
        }
    }
});
Worker::runAll();

问答:
问:为什么不用Laravel自带的队列直接推送?
答:Laravel队列需配合Supervisor等进程管理器,且无法直接维持WebSocket长连接,Workerman作为独立服务,既处理WebSocket推送,又通过Redis解耦,不阻塞Laravel的HTTP请求处理。


常见问题与问答

问题1:Workerman在Windows下总是报错?

答: Windows不支持pcntl扩展,部分功能受限,建议:

  • 开发阶段:使用php start.php start(不带-d,前台运行)
  • 生产环境:务必使用Linux

问题2:如何重启Workerman服务?

答:

# 平滑重启(不中断现有连接)
php start.php reload
# 停止所有进程
php start.php stop
# 查看运行状态
php start.php status

问题3:连接数过多导致内存溢出?

答: 设置连接级缓冲区限制:

$connection->maxSendBufferSize = 102400; // 限制发送缓冲区100KB
$connection->maxPackageSize = 102400; // 限制单包大小100KB

同时开启Worker::$logFile记录异常日志。

问题4:如何在分布式环境中共享连接状态?

答: 使用Redis或数据库存储用户-连接映射。

  • 用户登录时:Redis::hSet('user_connections', $userId, $connection->id)
  • 推送时:从Redis获取connection ID,通过通道通信(如Workerman的Channel组件)

性能调优与最佳实践

核心调优参数

// 在启动文件前设置
Worker::$stdoutFile = '/dev/null'; // 关闭标准输出
Worker::$logFile = '/tmp/workerman.log';
Worker::$maxWorkerBufferSize = 2*1024*1024; // 每个进程最大内存2MB
// 设置打开文件数(Linux)
// 在终端执行:ulimit -n 65535

安全加固

  • 使用$connection->getRemoteIp()限制来源IP
  • 对WebSocket消息做长度限制:
    if (strlen($data) > 10240) { // 限制10KB
      $connection->close("消息过长");
    }
  • 启用SSL加密(wss协议):
    $context = [
      'ssl' => [
          'local_cert' => '/path/to/cert.pem',
          'local_pk'  => '/path/to/private.key',
      ]
    ];
    $worker = new Worker('websocket://0.0.0.0:443', $context);
    $worker->transport = 'ssl';

日志与监控

  • 使用Worker::$logFile记录报错信息
  • 集成Prometheus:在定时任务中统计连接数、消息吞吐量
  • 配合HealthCheck定时器:每5秒检查内存占用,超过阈值自动重启

问答:
问:Workerman运行时CPU占用100%怎么办?
答:通常是死循环或无效循环导致,检查onMessage中是否有while(true)未正确退出,使用Worker::safeSleep(0.1)在定时任务中释放CPU。


Workerman在项目中的落地策略

适合集成的项目阶段

  • 新项目:优先使用Workerman作为长连接服务,替代传统的ajax轮询
  • 遗留系统:在边缘模块(如聊天、通知)引入,通过Redis/消息队列与主系统解耦
  • 物联网项目:Workerman+MQTT协议,实现设备端到服务端的高效通信

不建议使用的场景

  • 纯HTTP RESTful API(Nginx/php-fpm更成熟)
  • 单页应用的前端路由(应由前端框架处理)
  • 仅需要定时任务(使用crontab更轻量)

学习路线建议

  1. 掌握基础用法:TCP/WebSocket服务器、定时器、多进程
  2. 研究GatewayWorker框架(Workerman的增强版,支持分布式)
  3. 深入源码:理解事件循环、进程间通信机制

最后提醒: Workerman是PHP开发者突破“PHP只能做Web”认知的利器,本文中所有代码示例均可直接运行,建议动手搭建一个简单的推送服务,体会常驻内存带来的性能飞跃。

抱歉,评论功能暂时关闭!