workerman 如何实现 http长轮询

dengyd

用workerman做的http服务器,如果用作长轮询的话如何能不造成阻塞呢?

4546 1 1
1个回答

walkor

这个是一个长轮训demo

use Workerman\Worker;
require_once './Workerman/Autoloader.php';

// 初始化一个worker容器,监听1234端口
$worker = new Worker('http://0.0.0.0:1234');
$worker->count = 1;

// 初始化一个数组,用来保存所有的长轮训连接
$poll_connections = array();

/*
 * 当有客户端发来消息时
 * 长轮训拉取数据url格式为 http://xxxx:1234/?type=get&uid=xxx
 * 向某uid发送数据格url格式 http://xxxx:1234/?type=send&to_uid=xxx&content=xxx
 */
$worker->onMessage = function($connection, $data)
{
    global $poll_connections;
    if(empty($_GET))
    {
        return $connection->close('type empty');
    }
    // 这个请求是长轮训活取数据请求
    if($_GET === 'get')
    {
        // 这里简单的用uid验证用户身份,没传uid视为非法请求
        if(!isset($_GET))
        {
            return $connection->close('empty uid');
        }
        // 临时给当前的连接对象添加一个属性保存uid
        $connection->uid = $_GET;
        // 保存客户端的连接对象,当需要向这个uid推送数据时需要使用这个对象
        $poll_connections = $connection;
        // 重点是这里,不send任何数据,则会和浏览器一直保持连接
        return;
    }
    // 这个请求是向某个uid长轮训推送数据
    elseif($_GET === 'send')
    {
        // 检查参数是否都传了
        if(!isset($_GET) || !isset($_GET))
        {
            return $connection->close('to_uid or content empty');
        }
        // 查找to_uid 对应的连接对象,以便向to_uid的浏览器发送数据
        if(!isset($poll_connections))
        {
            // to_uid 不在线,发送不在线提示
            return $connection->close("uid:{$_GET} offline");
        }
        // 注意这里,向to_uid的浏览器发送了数据
        $poll_connections->close($_GET);
        // 给发送者返回成功提示
        $connection->close("send success!");
        return;
    }
    // type传的不对,非法请求
    else
    {
        return $connection->close('unknown type :' . $_GET);
    }
};

// 当有客户端连接断开时,删除uid到连接对象的映射
$worker->onClose = function($connection)
{
    global $poll_connections;
    if(isset($connection->uid))
    {
        unset($poll_connections);
    }
};

// 运行所有的worker(其实当前只定义了一个)
Worker::runAll();

在线测试
长轮询接受url
http://workerman.net:1234/?type=get&uid=123
http://workerman.net:1234/?type=get&uid=456
http://workerman.net:1234/?type=get&uid=789
...

发送消息url
http://workerman.net:1234/?type=send&to_uid=123&content=hello
http://workerman.net:1234/?type=send&to_uid=456&content=hello
...

技巧是不向浏览器send数据,浏览器就会一直等待数据,然后服务端业务该干嘛干嘛,服务端不会阻塞,等需要发送数据时,在向浏览器send。

这样一个worker进程就能维持大量的连接(上万或者数十万),服务端是非阻塞的,性能很高,服务器也不会有任何负载

  • 暂无评论
年代过于久远,无法发表回答
🔝