Swoole入门

简介

是什么?

  • Swoole是一个PHP扩展,使用纯C语言编写。
  • Swoole内置了Http/WebSocket服务器端/客户端、Http2.0服务器端。
  • Swoole支持AsyncTask,消息队列,毫秒定时器,异步文件读写等功能。
  • Swoole从2.0版本开始支持了内置协程,可以使用完全同步的代码实现异步程序。PHP代码无需额外增加任何关键词,底层自动进行协程调度,实现异步。
  • Swoole可以广泛应用于互联网、移动通信、企业软件、网络游戏、物联网、车联网、智能家庭等领域。

版本选择

建议使用的版本:

  • 稳定版:v1.9.23
  • 预览版:v2.0.12

1.9.x分支已进入特性锁定期,不再开发新功能,仅修复BUG。

建议使用的PHP版本:

  • PHP5.5或更高版本
  • PHP7.0.13或更高版本

可以免费使用吗

Swoole是开源免费的自由软件,授权协议是Apache2.0。企业和个人开发者均可免费使用Swoole的代码,并且在Swoole之上所作的修改可用于商业产品,无需开源(注:必须保留原作者的版权声明)。

实例讲解

HttpServer

swoole内置Http服务器的支持。swoole版的http server相对于php-fpm,最大优势在于高性能:代码一次载入内存,后续无需再解释执行。缺点是调试没有nginx+php-fpm方便。

使用swoole,通过几行代码即可写出一个异步非阻塞多进程的Http服务器:

<?php
$server = new swoole_http_server("0.0.0.0", 9051);
$server->set([
    'daemonize' => false,//是否后台运行
    'log_file' => 'swoole.log',
]);

$server->on('Start', function() {
    swoole_set_process_name("swoole_http_server");
    echo "Start\n";
});

$server->on('Request', function($request, $response) {
    print_r($request->get);
    // print_r($request->post);
    // print_r($request->cookie);
    // print_r($request->files);
    // print_r($request->header);
    // print_r($request->server);

    $response->cookie("User", "Swoole");
    $response->header("X-Server", "Swoole");
    $response->end("<h1>Hello Swoole!</h1>");
});

$server->start();

swoole_http_server继承自swoole_server,是一个完整的http服务器实现。

注意:swoole_http_server对Http协议的支持并不完整,建议仅作为应用服务器。并且在前端增加Nginx作为代理:

server {
    root /data/wwwroot/;
    server_name local.swoole.com;

    location / {
        proxy_http_version 1.1;
        proxy_set_header Connection "keep-alive";
        proxy_set_header X-Real-IP $remote_addr;
        if (!-e $request_filename) {
            proxy_pass http://127.0.0.1:9501;
        }
    }
}

WebSocketServer

swoole增加了内置的WebSocket服务器支持,通过几行PHP代码就可以写出一个异步非阻塞多进程的WebSocket服务器。

<?php
$server = new swoole_websocket_server("0.0.0.0", 9052);

$server->on('Start', function (swoole_websocket_server $server) {
    swoole_set_process_name("swoole_websocket_server");
    echo "Server Start... \n";
});

$server->on('WorkerStart', function (swoole_websocket_server $server, $worker_id){
    if ($server->worker_id == 0){
        swoole_timer_tick(2000, function ($timer_id) { //2000ms
            echo "tick-2000ms\n";
        });
    }
});

$server->on('Open', function (swoole_websocket_server $server, $request) {
    echo "server: handshake success with fd:{$request->fd}\n";
});

$server->on('Message', function (swoole_websocket_server $server, $frame) {
    echo "receive from fd:{$frame->fd}:{$frame->data}\n";
    $server->push($frame->fd, "Hello fd:{$frame->fd}, I'm Server.\n");
});

$server->on('Close', function ($ser, $fd) {
    echo "client fd:{$fd} closed\n";
});

$server->start();

WebSocket客户端

最简单的是使用JS编写:

<script>
    socket = new WebSocket('ws://192.168.99.100:9052/'); 
    socket.onopen = function(evt) { 
        // 发送一个初始化消息
        socket.send('I am the client and I\'m listening!'); 
    }; 

    // 监听消息
    socket.onmessage = function(event) { 
        console.log('Received a message: ', event.data); 
        alert(event.data);
    }; 

    // 监听Socket的关闭
    socket.onclose = function(event) { 
        console.log('Socket has closed',event); 
    }; 

    socket.onerror = function(evt) { 
        console.log('onerror',event); 
    }; 
</script>

进程模型

我们来使用实例进行分析:

<?php
$server = new \swoole_server("0.0.0.0",9053);//tcp_server

$server->set([
    'daemonize' => false,//是否后台运行
    'log_file' => 'swoole.log',
    // 'worker_num' => 2,
]);

$server->on('connect', function ($serv, $fd){ 
    echo "client connect. fd is {$fd}\n";
});

$server->on('receive', function ($serv, $fd, $from_id, $data){
    echo "client connect. fd is {$fd}\n";
});

$server->on('close', function ($serv, $fd){
    echo "client close. fd is {$fd}\n";
});

// 以下回调发生在Master进程
$server->on("start", function (\swoole_server $server){
    echo "On master start.\n";
});
$server->on('shutdown', function (\swoole_server $server){
    echo "On master shutdown.\n";
});

// 以下回调发生在Manager进程
$server->on('ManagerStart', function (\swoole_server $server){
    echo "On manager start.\n";
});
$server->on('ManagerStop', function (\swoole_server $server){
    echo "On manager stop.\n";
});

// 以下回调也发生在Worker进程
$server->on('WorkerStart', function (\swoole_server $server, $worker_id){
    echo "Worker start\n";
});
$server->on('WorkerStop', function(\swoole_server $server, $worker_id){
    echo "Worker stop\n";
});
$server->on('WorkerError', function(\swoole_server $server, $worker_id, $worker_pid, $exit_code){
    echo "Worker error\n";
});

$server->start();

我们使用 pstree -ap PID查看swoole进程数:

[root@9355490fe5da /]# pstree -ap 235
php,235 tcp_server.php
|-php,236 tcp_server.php
|   |-php,239 tcp_server.php
|   `-php,240 tcp_server.php

发现有4个进程。按PID从小到大分别是 Master进程、Manager进程、Worker进程(2个)。

基于此,我们简单梳理一下,当执行的start方法之后,发生了什么:

  • 守护进程模式下,当前进程fork出Master进程,然后退出,Master进程触发OnMasterStart事件。
  • Master进程启动成功之后,fork出Manager进程,并触发OnManagerStart事件。
  • Manager进程启动成功时候,fork出Worker进程,并触发OnWorkerStart事件。

非守护进程模式下,则当前进程直接作为Master进程工作。

所以,一个最基础的Swoole Server,至少需要有3个进程,分别是Master进程、Manager进程和Worker进程。

事实上,一个多进程模式下的Swoole Server中,有且只有一个Master进程;有且只有一个Manager进程;却可以有n个Worker进程。

Master进程 是一个多线程进程,其中有一组非常重要的线程,叫做Reactor线程(组)。

每当一个客户端连接上服务器的时候,都会由Master进程从已有的Reactor线程中,根据一定规则挑选一个,专门负责向这个客户端提供维持链接、处理网络IO与收发数据等服务。

Manager进程 ,某种意义上可以看做一个代理层。

它本身并不直接处理业务,其主要工作是将Master进程中收到的数据转交给Worker进程,或者将Worker进程中希望发给客户端的数据转交给Master进程进行发送。

Manager进程还负责监控Worker进程,如果Worker进程因为某些意外挂了,Manager进程会重新拉起新的Worker进程。

Worker进程 其实就是处理各种业务工作的进程,Manager将数据包转交给Worker进程,然后Worker进程进行具体的处理,并根据实际情况将结果反馈给客户端。

参考资料

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • 前文再续,就书接上一回,随着与Server、TCP、Protocol的邂逅,Swoole终于迎来了自己的故事,今天...
    蜗牛淋雨阅读 1,706评论 1 14
  • 1. 入门abc 1.1 github账号添加 第一步依然是配置git用户名和邮箱 生成ssh key时同时指定保...
    小聪明李良才阅读 2,039评论 5 23
  • 本文示例代码详见:https://github.com/52fhy/swoole_demo。 简介 Swoole是...
    jiancaigege阅读 793评论 1 6
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • 最近在看《销售洗脑》的这本书,很想加入自己的一些想法分享给大家。我自己做销售做了3年时间,当然我不算是一个特别成...
    吴凤蝶阅读 1,692评论 1 3