前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >swoole学习笔记

swoole学习笔记

原创
作者头像
CS逍遥剑仙
修改于 2019-03-02 20:32:11
修改于 2019-03-02 20:32:11
1.1K0
举报
文章被收录于专栏:禅林阆苑禅林阆苑

swoole学习笔记

TOC

Write By CS逍遥剑仙

我的主页: www.csxiaoyao.com

GitHub: github.com/csxiaoyaojianxian

Email: sunjianfeng@csxiaoyao.com

QQ: 1724338257

swoole是面向生产环境的 PHP 异步网络通信引擎,本笔记是本人在学习完慕课网的课程《Swoole入门到实战打造高性能赛事直播平台》后的笔记,是对学习的代码整理的补充,学习过程中整理的github代码地址:

https://github.com/csxiaoyaojianxian/PhpStudy/tree/master/06-swoole

1. php7源码安装

步骤:解压、configure、make、make install,此处略。

2. 切换Mac默认PHP版本为MAMP

注:在实际学习过程中发现有问题,改为非MAMP的PHP

代码语言:txt
AI代码解释
复制
$ sudo vi ~/.bash_profile
export PATH="/Applications/MAMP/bin/php/php5.5.38/bin:$PATH"
$ source ~/.bash_profile
$ which php

如果安装了zsh,会出现重新打开terminal窗口时php又跳回自带的PHP,因为terminal在init的时候默认启动执行脚本变为了~/.zshrc,并不会执行~/.bash_profile、~/.bashrc等脚本了,解决方法是修改zsh配置文件:

代码语言:txt
AI代码解释
复制
$ vi ~/.zshrc
# 追加
source ~/.bash_profile

3. swoole安装

源码安装

代码语言:txt
AI代码解释
复制
$ git clone https://github.com/swoole/swoole-src.git
$ phpize
# ./configure --help
# ./configure --with-php-config=/home/work/study/soft/php/bin/php-config
$ ./configure
$ make
$ make install

配置php.ini

代码语言:txt
AI代码解释
复制
extension=swoole.so

验证

代码语言:txt
AI代码解释
复制
$ php -m
# 看到swoole即成功

4. redis安装

swoole使用异步redis的前置条件

  1. 安装redis服务
代码语言:txt
AI代码解释
复制
$ tar -zxvf redis-5.0.3.tar.gz
$ make
$ make install
$ cd src
$ ./redis-server
# (可选)修改端口等配置
$ vi redis.conf
# 使用客户端连接
$ ./redis-cli
> set csxiaoyao 1
> get csxiaoyao
  1. 安装hiredis库,参考swoole官方文档下载安装包
代码语言:txt
AI代码解释
复制
$ make && make install
  1. 重新编译swoole,需要加入 --enable-async-redis
代码语言:txt
AI代码解释
复制
$ ./configure --with-php-config=/home/work/study/soft/php/bin/php-config --enable-async-redis
  1. 验证
代码语言:txt
AI代码解释
复制
$ php --ri swoole
# 查看是否有 async_redis => enabled
  1. 封装redis类

关注 public function __call($name, $arguments) 方法

代码语言:txt
AI代码解释
复制
<?php
   namespace app\common\lib\redis;
   class Predis {
       public $redis = "";
       // 定义单例模式变量
       private static $_instance = null;
       public static function getInstance() {
           if(empty(self::$_instance)) {
               self::$_instance = new self();
           }
           return self::$_instance;
       }
       private function __construct() {
           $this->redis = new \Redis();
           $result = $this->redis->connect(config('redis.host'), config('redis.port'), config('redis.timeOut'));
           if($result === false) {
               throw new \Exception('redis connect error');
           }
       }
       /**
        * set
        * @return bool|string
        */
       public function set($key, $value, $time = 0 ) {
           if(!$key) {
               return '';
           }
           if(is_array($value)) {
               $value = json_encode($value);
           }
           if(!$time) {
               return $this->redis->set($key, $value);
           }
           return $this->redis->setex($key, $time, $value);
       }
       /**
        * get
        * @return bool|string
        */
       public function get($key) {
           if(!$key) {
               return '';
           }
           return $this->redis->get($key);
       }
       /**
        * 返回key中所有成员
        * @return array
        */
       public function sMembers($key) {
           return $this->redis->sMembers($key);
       }
       // 调用前面不存在的方法,如 sAdd()
       public function __call($name, $arguments) {
           //echo $name.PHP_EOL;
           //print_r($arguments);
           if(count($arguments) != 2) {
               return '';
           }
           $this->redis->$name($arguments[0], $arguments[1]);
       }
   }

5. thinkphp框架整合swoole

ws server 可以包含 http server,本案例在 onRequest 中启动thinkphp框架,实现了 ws server 处理 get、post 请求

5.1 使用swoole构建包含http server的websocket服务

详见demo的/script/server/ws.php

代码语言:txt
AI代码解释
复制
<?php
class Ws {
    CONST HOST = "0.0.0.0"; // 监听的ip
    CONST PORT = 8811; // 第一个port,用于直播
    CONST CHART_PORT = 8812; // 第二个port,用于聊天室
    public $ws = null;
    public function __construct() {
        $this->ws = new swoole_websocket_server(self::HOST, self::PORT);
        $this->ws->listen(self::HOST, self::CHART_PORT, SWOOLE_SOCK_TCP);
        $this->ws->set(
            [
                'enable_static_handler' => true,
                'document_root' => "/home/work/hdtocs/public/static",
                'worker_num' => 4,
                'task_worker_num' => 4,
            ]
        );
        $this->ws->on("start", [$this, 'onStart']);
        $this->ws->on("open", [$this, 'onOpen']); // ws
        $this->ws->on("message", [$this, 'onMessage']); // ws
        $this->ws->on("workerstart", [$this, 'onWorkerStart']);
        $this->ws->on("request", [$this, 'onRequest']); // request
        $this->ws->on("task", [$this, 'onTask']); // task
        $this->ws->on("finish", [$this, 'onFinish']); // task
        $this->ws->on("close", [$this, 'onClose']);
        $this->ws->start();
    }
    ...
}
new Ws();

5.2 onStart & onWorkerStart 指定进程名并加载框架

onStart 中指定进程名称,应用于reload.sh的服务重启脚本,重启脚本见后面小节。

onWorkerStart 加载thinkphp框架,用于处理请求。

代码语言:txt
AI代码解释
复制
public function onStart($server) {
    // 指定进程名称,应用于reload.sh的服务重启脚本
    swoole_set_process_name("live_master");
}
public function onWorkerStart($server,  $worker_id) {
    // 定义应用目录
    define('APP_PATH', __DIR__ . '/../application/');
    // 加载框架文件
    require __DIR__ . '/../thinkphp/start.php';
}

5.3 onRequest 处理 http 请求

onRequest 用于处理http请求,包含过滤请求、处理$_SERVER$_GET$_FILES$_POST并写入日志,最后调用thinkphp框架处理请求。日志落盘方法见后面小节。

代码语言:txt
AI代码解释
复制
public function onRequest($request, $response) {
    // 请求过滤,/favicon.ico如果不存在则直接返回404
    if($request->server['request_uri'] == '/favicon.ico') {
        $response->status(404);
        $response->end();
        return;
    }
    // 兼容php的 $_SERVER、$_GET、$_FILES、$_POST
    $_SERVER  =  [];
    if(isset($request->server)) {
        foreach($request->server as $k => $v) {
            $_SERVER[strtoupper($k)] = $v;
        }
    }
    if(isset($request->header)) {
        foreach($request->header as $k => $v) {
            $_SERVER[strtoupper($k)] = $v;
        }
    }
    $_GET = [];
    if(isset($request->get)) {
        foreach($request->get as $k => $v) {
            $_GET[$k] = $v;
        }
    }
    $_FILES = [];
    if(isset($request->files)) {
        foreach($request->files as $k => $v) {
            $_FILES[$k] = $v;
        }
    }
    $_POST = [];
    if(isset($request->post)) {
        foreach($request->post as $k => $v) {
            $_POST[$k] = $v;
        }
    }
    // 请求写入日志
    $this->writeLog();
    // http_server 不需要写入日志
    $_POST['http_server'] = $this->ws;
    ob_start(); // php打开缓冲区, 控制浏览器cache
    // 执行应用并响应
    try {
        think\Container::get('app', [APP_PATH])->run()->send();
    }catch (\Exception $e) {}
    $res = ob_get_contents(); // 获取缓冲区输出
    ob_end_clean(); // 清空cache
    $response->end($res);
}

5.4 onTask & onFinish 处理 task

onTask中可以处理task请求,例如task发送验证码

代码语言:txt
AI代码解释
复制
public function onTask($serv, $taskId, $workerId, $data) {
    $obj = new app\common\lib\ali\Sms();
    try {
        $response = $obj::sendSms($data['phone'], $data['code']);
    }catch (\Exception $e) {
        echo $e->getMessage();
    }
}

task的异步机制能够提高处理效率,随着task类型的增加,将处理事件直接写在ws.php中不利于维护,利用php的动态函数名进行task分发,将具体task内容封装为Task类,用方法进行区分,使代码结构更加清晰。

代码语言:txt
AI代码解释
复制
/**
 * task
 * $serv: swoole server对象
 * $data: 调用task时候传入的参数
 */
public function onTask($serv, $taskId, $workerId, $data) {
    // 分发 task 任务机制,类似于不同的 controller
    $obj = new app\common\lib\task\Task;
    $method = $data['method']; // 执行的函数名
    $flag = $obj->$method($data['data'], $serv);
    return $flag; // 通知worker处理结果
}
// task完成
public function onFinish($serv, $taskId, $data) {
    echo "taskId:{$taskId}\n";
    echo "finish-data-sucess:{$data}\n";
}

task的调用,在controller中调用$_POST['http_server']->task,server对象在上一步存入到了POST中,$_POST['http_server'] = $this->ws;

代码语言:txt
AI代码解释
复制
$taskData = [
    'method' => 'sendSms',
    'data' => [
        'phone' => $phoneNum,
        'code' => rand(1000, 9999)
    ]
];
$_POST['http_server']->task($taskData);

5.5 onOpen & onMessage & onClose 处理ws客户端连接

onOpen中向redis的指定key的集合存储ws客户端。

onMessage处理ws消息。

onClose在连接断开时清除redis集合中的ws客户端。

代码语言:txt
AI代码解释
复制
// 监听ws连接事件
public function onOpen($ws, $request) {
    // 调用封装的redis类Predis向redis集合中存入连接的客户端信息 fd
    \app\common\lib\redis\Predis::getInstance()->sAdd(config('redis.live_game_key'), $request->fd);
    var_dump($request->fd);
}
// 监听ws消息事件
public function onMessage($ws, $frame) {
    echo "ser-push-message:{$frame->data}\n";
    $ws->push($frame->fd, "server-push:".date("Y-m-d H:i:s"));
}
public function onClose($ws, $fd) {
    // fd del
    \app\common\lib\redis\Predis::getInstance()->sRem(config('redis.live_game_key'), $fd);
    echo "clientid:{$fd}\n";
}

6. 日志落盘

在前面的http请求中,对于每个请求,需要调用$this->writeLog();记录日志,使用swoole的异步文件写入来记录日志。

代码语言:txt
AI代码解释
复制
// swoole异步记录日志
public function writeLog() {
    $datas = array_merge(['date' => date("Ymd H:i:s")],$_GET, $_POST, $_SERVER);
    $logs = "";
    foreach($datas as $key => $value) {
        $logs .= $key . ":" . $value . " ";
    }
    swoole_async_writefile(APP_PATH.'../runtime/log/'.date("Ym")."/".date("d")."_access.log", $logs.PHP_EOL, function($filename){
        // todo
    }, FILE_APPEND);
}

7. 服务平滑重启

利用kill -USR1 $pid可以平滑重启服务,不影响处理请求,其中$pid为onStart中swoole_set_process_name("live_master")指定的进程名称,脚本reload.sh如下。

代码语言:txt
AI代码解释
复制
echo "loading..."
pid=`pidof live_master`
echo $pid
kill -USR1 $pid
echo "loading success"

8. 服务监控

8.1 监控shell脚本

在项目下建立 monitor/server.php 监控程序对ws http 8811服务进行监控。首先分析监控的shell脚本。

代码语言:txt
AI代码解释
复制
$ netstat -anp | grep 8811

得到的结果大致为

代码语言:txt
AI代码解释
复制
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
 xxx
 xxx
 xxx

使用 grep LISTEN 在得到的多条结果中过滤掉其他非目标结果,使用wc -l返回得到的结果行数。

代码语言:txt
AI代码解释
复制
$ netstat -anp | grep 8811 | grep LISTEN | wc -l

此时的正常返回结果为:

代码语言:txt
AI代码解释
复制
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
1

使用2>/dev/null将前两行提示输出到null,只留下结果行数。

代码语言:txt
AI代码解释
复制
$ netstat -anp 2>/dev/null | grep 8811 | grep LISTEN | wc -l

最终的正常结果为:

代码语言:txt
AI代码解释
复制
1

8.2 使用swoole定时器执行shell脚本

linux的定时任务crontab的精度为分钟,用来进行实时监控太长,需要利用swoole的定时器来调用shell脚本监控,定时器每两秒执行一次。

代码语言:txt
AI代码解释
复制
class Server {
    const PORT = 8811;
    public function port() {
        $shell = "netstat -anp 2>/dev/null | grep ". self::PORT . " | grep LISTEN | wc -l";
		// php 执行 shell 脚本
        $result = shell_exec($shell);
        if($result != 1) { // 返回的结果行数不为1
            // todo 告警服务 邮件 短信 ...
            echo date("Ymd H:i:s")."error".PHP_EOL;
        } else {
            echo date("Ymd H:i:s")."succss".PHP_EOL;
        }
    }
}
// nohup
swoole_timer_tick(2000, function($timer_id) {
    (new Server())->port(); // 执行
    echo "time-start".PHP_EOL;
});

8.3 启动监控服务

后台执行监控服务,每两秒输出结果到demo.log文件。

代码语言:txt
AI代码解释
复制
$ nohub /xxx/php /xxx/ws.php > /xxx/demo.log &

查看监控服务是否正常启动。

代码语言:txt
AI代码解释
复制
$ ps aux | grep monitor/server.php

查看监控日志文件

代码语言:txt
AI代码解释
复制
$ tail -f demo.log

9. 附录1: websocket使用task群发消息的实现

task任务的实现。

代码语言:txt
AI代码解释
复制
<?php
namespace app\common\lib\task;
use app\common\lib\redis\Predis;
class Task {
    /**
     * 通过task机制发送赛况实时数据给客户端
     * @param $data
     * @param $serv swoole server对象
     */
    public function pushLive($data, $serv) {
        $clients = Predis::getInstance()->sMembers(config("redis.live_game_key"));
        foreach($clients as $fd) {
            $serv->push($fd, json_encode($data));
        }
    }
}

controller中调用task 任务

代码语言:txt
AI代码解释
复制
$taskData = [
	'method' => 'pushLive',
	'data' => $data
];
$_POST['http_server']->task($taskData);

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
暂无评论
推荐阅读
plotly-express-12-plotly实现多子图
在很多的实际业务需求中,需要将多个图形集中放置一个figure中,而不是单独显示,在这种情况下我们需要使用子图的概念。本文中讲解如何在plotly中使用plotly.graph_objects绘制各种形式的子图
皮大大
2021/03/01
3.3K0
plotly-express-12-plotly实现多子图
把饼图的位置移到想要的位置就报错了,怎么破?
前几天在Python最强王者交流群【哎呦喂 是豆子~】问了一个Python画图的问题,一起来看看吧。问题描述:
Python进阶者
2023/12/21
1900
把饼图的位置移到想要的位置就报错了,怎么破?
中国奥运会成绩,知道多少?13张图告诉你
最近奥运会也是非常热门的事件,但是针对本次奥运会有很多值得吐槽的地方,小岛国的骚气操作不想写
Python进阶者
2021/08/20
4990
kaggle-3-Appstore
The ever-changing mobile landscape is a challenging space to navigate. . The percentage of mobile over desktop is only increasing. Android holds about 53.2% of the smartphone market, while iOS is 43%. To get more people to download your app, you need to make sure they can easily find your app. Mobile app analytics is a great way to understand the existing strategy to drive growth and retention of future user.
皮大大
2021/03/01
5980
kaggle-3-Appstore
Plotly绘图,快速入门
Plotly是一个用于创建交互式图表的Python库,它支持多种图表类型,如折线图、散点图、饼图、热力图等。Plotly的特点如下:
皮大大
2024/06/29
3540
kaggle实战-黑色星期图画像分析
本文是对还是kaggle上一份黑色星期五消费数据的分析,主要是针对用户和商品信息的画像分析。
皮大大
2023/08/25
2720
深入探索 Plotly-打造交互式数据可视化的终极指南
文章链接:https://cloud.tencent.com/developer/article/2466316
一键难忘
2024/11/22
5480
Plotly,一个超强的Python可视化库!
数据可视化是数据分析和探索的一个重要方面,它有助于深入了解数据集中的潜在模式、趋势和关系。
小F
2023/12/21
6440
Plotly,一个超强的Python可视化库!
8个plotly绘图技巧
Plotly 是一个用于创建交互式数据可视化的 Python 库,它允许你轻松地生成各种类型的图表和图形,包括折线图、散点图、柱状图、饼图、热力图、3D 图等。
皮大大
2023/09/17
8030
我用python掐指一算,2020高考分数和录取情况可能是这样
“迟到”了一个月的高考终于要来了。 正好我得到了一份山东新高考模拟考的成绩和山东考试院公布的一分一段表,以及过去三年的普通高考本科普通批首次志愿录取情况统计。2020年是山东新高考改革的元年,全新的录取模式以及选考科目要求都给考生带来了非常大的挑战。 我正好就本次山东模拟考的成绩进行深入数据分析,用python可视化带大家模拟一下2020高考分数和录取情况。 (代码较长,故只展示部分,完整数据+源码下载见文末) 1、不同考生的成绩分布图 首先对山东新高考模拟考的成绩进行总体描述: fig = 
腾讯NEXT学位
2020/07/07
4990
Pandas数据分析经典案例
本案例中用的数据是小编自行模拟的,主要包含两个数据:订单数据和水果信息数据,并且会将两份数据合并
皮大大
2022/01/12
2K1
Pandas数据分析经典案例
Python数据可视化入门教程
什么是数据可视化?数据可视化是为了使得数据更高效地反应数据情况,便于让读者更高效阅读,通过数据可视化突出数据背后的规律,以此突出数据中的重要因素,如果使用Python做数据可视化,建议学好如下这四个Python数据分析包,分别是:
张俊红
2023/03/21
2.6K0
Python数据可视化入门教程
Python多维数据可视化详解
数据聚合、汇总和可视化是支撑数据分析领域的三大支柱。长久以来,数据可视化都是一个强有力的工具,被业界广泛使用,却受限于 2 维。在本文中,作者将探索一些有效的多维数据可视化策略(范围从 1 维到 6 维)。
用户8949263
2022/04/08
1.2K0
Python多维数据可视化详解
Python一行代码搞定炫酷可视化,你需要了解一下Cufflinks
导读:学过Python数据分析的朋友都知道,在可视化的工具中,有很多优秀的三方库,比如matplotlib,seaborn,plotly,Boken,pyecharts等等。这些可视化库都有自己的特点,在实际应用中也广为大家使用。
IT阅读排行榜
2019/08/21
1.2K0
Python一行代码搞定炫酷可视化,你需要了解一下Cufflinks
python绘图与数据可视化(二)
上一次是于老师要求我做一次备课,讲一节课,上周于老师又自己准备了这个课程,这里放一下于老师课上补充的知识点
十二惊惶
2024/02/28
5200
python绘图与数据可视化(二)
数据可视化 | 手撕 Matplotlib 绘图原理(二)
关于箭头和注释风格的更多介绍与示例,可以在 Matplotlib 的画廊gallery[1]中看到,尤其推荐
数据STUDIO
2021/06/24
1.5K1
plotly可视化快速教程
Plotly是新一代的Python数据可视化开发库,它提供了完善的交互能力和灵活的绘制选项。本文将介绍新手如何安装plotly并编写第一个plotly绘图程序,以及使用plotly绘制常见的5种数据图表。
用户1408045
2019/10/10
2.9K0
plotly可视化快速教程
安利个一行代码的Python可视化神器!
学过Python数据分析的朋友都知道,在可视化的工具中,有很多优秀的三方库,比如matplotlib,seaborn,plotly,Boken,pyecharts等等。这些可视化库都有自己的特点,在实际应用中也广为大家使用。
Python数据科学
2023/09/14
4870
安利个一行代码的Python可视化神器!
plotly-express-22-plotly使用技巧大全
本文中将前段时间写的plotly-express可视化库的相关技巧进行整理,方便后续快速实现调用
皮大大
2021/03/01
3K0
plotly-express-22-plotly使用技巧大全
cufflinks可视化包初探
不多说,先画一张再说.还是上文的环境.直接pip install,xxxxx,记得换源
云深无际
2021/04/14
6300
cufflinks可视化包初探
相关推荐
plotly-express-12-plotly实现多子图
更多 >
LV.0
这个人很懒,什么都没有留下~
目录
  • swoole学习笔记
    • 1. php7源码安装
    • 2. 切换Mac默认PHP版本为MAMP
    • 3. swoole安装
    • 4. redis安装
    • 5. thinkphp框架整合swoole
      • 5.1 使用swoole构建包含http server的websocket服务
      • 5.2 onStart & onWorkerStart 指定进程名并加载框架
      • 5.3 onRequest 处理 http 请求
      • 5.4 onTask & onFinish 处理 task
      • 5.5 onOpen & onMessage & onClose 处理ws客户端连接
    • 6. 日志落盘
    • 7. 服务平滑重启
    • 8. 服务监控
      • 8.1 监控shell脚本
      • 8.2 使用swoole定时器执行shell脚本
      • 8.3 启动监控服务
    • 9. 附录1: websocket使用task群发消息的实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档