Swoole(二) PHP7&Swoole源码安装、玩转网络通信引擎、异步非堵塞IO场景

2 HTTP服务(常用)

ALT
netstat -anp | grep 8811 --检测端口号是否启动
php --info 可以看到swoole版本号 本文基于4.3.1

$http = new swoole_http_server("0.0.0.0", 8811);

$http->on('request', function ($request, $response) {
    var_dump($request->get);
    $response->header("Content-Type", "text/html; charset=utf-8");
    $response->cookie("singwa",'xsssss', time() + 1800);
    $response->end("<h1>Hello Swoole. #".rand(1000, 9999)."</h1>".json_encode($request->get));
});

$http->start();

开启http服务:(监听到了请求)

测试:
1)直接curl请求

2)浏览器访问

http返回的cookies: singwa

在服务器中除了会打印参数外 还多一个null值,原因是favicon.ico也是一个请求 参数为null
在这里插入图片描述
在这里插入图片描述

设置document_root并设置enable_static_handler为true后,底层收到Http请求会先判断document_root路径下是否存在此文件,如果存在会直接发送文件内容给客户端不再触发onRequest回调

$http = new swoole_http_server("0.0.0.0", 8811);

$http->set([
    'document_root' => '/usr/local/apache/htdocs/swoole/data',
    'enable_static_handler' => true,
]);

$http->on('request', function ($request, $response) {
    var_dump($request->get); // 浏览器打开会多出一个null?
    $response->header("Content-Type", "text/html; charset=utf-8");
    $response->cookie("singwa",'xsssss', time() + 1800);
    $response->end("<h1>Hello Swoole. #".rand(1000, 9999)."</h1>".json_encode($request->get));
});

$http->start();

文件结构
index.html内容
s/index.html内容

3 WebSocket服务

什么是WebSocket?

WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端

为什么要使用WebSocket?
缺陷:HTTP的通信只能由客户端发起

WebSocket特点:
*建立在TCP协议之上
*性能开销小通信高效
*客户端可以与任意服务器通信
*协议标识符ws wss
*持久化网络通信协议(长连接)

ws.php

<?php
class Ws{
    // ws连接
    public $ws;

    public function __construct(){
        $this->ws = new Swoole\WebSocket\Server('0.0.0.0',8812);
        $this->ws->on('open',[$this,'onOpen']);
        $this->ws->on('message',[$this,'onMessage']);
        $this->ws->on('close',[$this,'onClose']);
        $this->ws->start();
    }

    /**
     * 监听ws连接事件
     * @param $ws
     * @param $request
     */
    public function onOpen($server,$request){
        var_dump($request->fd);
    }

    public function onMessage($server,$frame){
        echo "server-receive-message:{$frame->data}\n";
        $server->push($frame->fd, "singwa push success!".date("Y-m-d H:i:s"));
    }

    public function onClose($server,$fd,$reactorId){
        echo "clientid:{$fd} close!";
    }
}
new Ws();

ws_client.html(可以通过8811 http服务启动页面,也可以通过ws set参数启动)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ws</title>
</head>
<body>
<h1>Singwa-Swoole-测试 </h1>
</body>
<script type="text/javascript">
    var wsUrl = "ws://192.168.119.3:8812";
    var websocket = new WebSocket(wsUrl);

    // 实例对象的onopen属性
    websocket.onopen = function(evt){
        console.log("connect-swoole-success!");
        websocket.send("singwa is good!");
    }

    websocket.onmessage = function(evt){
        console.log("ws-server-return-data:"+evt.data);
    }

    websocket.onclose = function(evt){
        console.log("close");
    }

    websocket.onerror = function(evt,e) {
        console.log("error:"+evt.data);
    }
</script>
</html>

4 Task任务使用

使用场景:
执行耗时的操作(发送邮件 广播等)
如何使用:
设置onTask
设置onFinish
设置task_worker_num
http,web socket,tcp都可以做异步task投放
ws.php

<?php
/**
 * Created by PhpStorm.
 * User: ZhangWei
 * Date: 2019-05-09
 * Time: 21:07
 */

class Ws{
    CONST HOST='0.0.0.0';
    CONST PORT=8812;
    // ws连接资源
    public $ws;

    public function __construct(){
        $this->ws = new Swoole\WebSocket\Server(self::HOST,self::PORT);
        $this->ws->set(
            [
                'worker_num' => 2,
                'task_worker_num' => 2,
            ]
        );
        $this->ws->on('open',[$this,'onOpen']);
        $this->ws->on('message',[$this,'onMessage']);
        $this->ws->on('task',[$this,'onTask']);
        $this->ws->on('finish',[$this,'onFinish']);
        $this->ws->on('close',[$this,'onClose']);
        $this->ws->start();
    }

    /**
     * 监听ws连接事件
     * @param $ws
     * @param $request
     */
    public function onOpen($server,$request){
        var_dump($request->fd);
    }

    /**
     * 监听客户端消息事件
     * @param $server
     * @param $frame
     */
    public function onMessage($server,$frame){
        echo "server-receive-message:{$frame->data}\n";
        // todo 10s
        $data = [
            'task' => 1,
            'fd' => $frame->fd
        ];
        $server->task($data); // 函数非阻塞,执行完毕立刻返回
        $server->push($frame->fd, "singwa push success!".date("Y-m-d H:i:s"));
    }

    /**
     * 监听task回调事件
     * @param $server
     * @param $task_id 任务id
     * @param $src_worker_id worker进程id
     * @param $data task传来的值
     */
    public function onTask($server,$task_id,$src_worker_id,$data){
        print_r($data);
        sleep(10);
        return "on tash finish\n";
    }

    /**
     * 监听task结束事件
     * @param $server
     * @param $task_id
     * @param $data
     */
    public function onFinish($server,$task_id,$data){
        echo "taskId:{$task_id}\n";
        echo "finish-task-success:".$data;
    }
    /**
     * 监听客户端关闭
     * @param $server
     * @param $fd
     * @param $reactorId
     */
    public function onClose($server,$fd,$reactorId){
        echo "clientid:{$fd} close!\n";
    }
}
new Ws();

ws_client.html 内容不变,还是上节中的。

5 Swoole定时器

常规定时器:
crontab
swoole定时器:
swoole_timer_tick 每个多久执行
swoole_timer_after 多久后执行
swoole_timer_clear 使用定时器ID来删除定时器
ws.php

<?php
/**
 * Ws基类
 * User: ZhangWei
 * Date: 2019-05-09
 * Time: 21:07
 */

class Ws{
    CONST HOST='0.0.0.0';
    CONST PORT=8812;
    // ws连接资源
    public $ws;

    public function __construct(){
        $this->ws = new Swoole\WebSocket\Server(self::HOST,self::PORT);
        $this->ws->set(
            [
                'worker_num' => 2,
                'task_worker_num' => 2,
            ]
        );
        $this->ws->on('open',[$this,'onOpen']);
        $this->ws->on('message',[$this,'onMessage']);
        $this->ws->on('task',[$this,'onTask']);
        $this->ws->on('finish',[$this,'onFinish']);
        $this->ws->on('close',[$this,'onClose']);
        $this->ws->start();
    }

    /**
     * 监听ws连接事件
     * @param $ws
     * @param $request
     */
    public function onOpen($server,$request){
        var_dump($request->fd);
        if($request->fd == 1){
            // 第一个连接时 每两秒中执行一次
            swoole_timer_tick(2000,function($timer_id){
                echo "2s: timerId:{$timer_id}\n";
            });
        }
    }

    /**
     * 监听客户端消息事件
     * @param $server
     * @param $frame
     */
    public function onMessage($server,$frame){
        echo "server-receive-message:{$frame->data}\n";
        // todo 10s
        $data = [
            'task' => 1,
            'fd' => $frame->fd
        ];
        //$server->task($data); // 函数非阻塞,执行完毕立刻返回
        swoole_timer_after(5000,function() use ($server,$frame){
            $server->push($frame->fd,"server-timer-after:5s\n");
        });
        $server->push($frame->fd, "singwa push success!".date("Y-m-d H:i:s"));
    }

    /**
     * 监听task回调事件
     * @param $server
     * @param $task_id 任务id
     * @param $src_worker_id worker进程id
     * @param $data task传来的值
     */
    public function onTask($server,$task_id,$src_worker_id,$data){
        print_r($data);
        sleep(10);
        return "on tash finish\n";
    }

    /**
     * 监听task结束事件
     * @param $server
     * @param $task_id
     * @param $data
     */
    public function onFinish($server,$task_id,$data){
        echo "taskId:{$task_id}\n";
        echo "finish-task-success:".$data;
    }
    /**
     * 监听客户端关闭
     * @param $server
     * @param $fd
     * @param $reactorId
     */
    public function onClose($server,$fd,$reactorId){
        echo "clientid:{$fd} close!\n";
    }
}
new Ws();

6 异步回调系统

异步回调模块已过时,目前仅修复 BUG,不再进行维护, 且在V4.3.0中移除了异步模块。请使用 Coroutine 协程模块。
AsynclO成为独立扩展,可安装后使用。

异步文件系统io

读取文件
read.php

// // Swoole\Async::readfile
$result = swoole_async_readfile(__DIR__."/1.txt",function($filename,$content){
    echo "filename:{$filename}".PHP_EOL;
    echo "content:{$content}".PHP_EOL;
});
echo "start".PHP_EOL; // 先执行此,再执行回调
文件不存在会返回false
成功打开文件立即返回true
数据读取完毕后会回调指定的callback函数。

swoole_async_readfile会将文件内容全部复制到内存,所以不能用于大文件的读取
如果要读取超大文件,请使用swoole_async_read函数
swoole_async_readfile最大可读取4M的文件,受限于SW_AIO_MAX_FILESIZE宏

在这里插入图片描述

swoole_async_read(__DIR__."/1.txt",function($filename,$content){
    echo "filename:{$filename}".PHP_EOL;
    echo "content:{$content}".PHP_EOL;
    return false;
},1,0);

与swoole_async_readfile不同,它是分段读取,可以用于读取超大文件。每次只读$size个字节,不会占用太多内存。
在读完后会自动回调$callback函数,回调函数接受2个参数:

bool callback(string $filename, string $content);
$filename,文件名称
$content,读取到的分段内容,如果内容为空,表明文件已读完

$callback函数,可以通过return true/false,来控制是否继续读下一段内容。

return true,继续读取
return false,停止读取并关闭文件

此处return false,只读一次。
在这里插入图片描述
写入文件
write.php

$content = date('Y-m-d H:i:s').PHP_EOL;
Swoole\Async::writefile(__DIR__.'/1.log',$content,function($filename){
    // todo
    echo "success".PHP_EOL;
},FILE_APPEND);

echo "start".PHP_EOL; // 先输出start 再走回调函数

参数1为文件的名称,必须有可写权限,文件不存在会自动创建。打开文件失败会立即返回false
参数2为要写入到文件的内容,最大可写入4M
参数3为写入成功后的回调函数,可选
参数4为写入的选项,可以使用FILE_APPEND表示追加到文件末尾
如果文件已存在,底层会覆盖旧的文件内容
FILE_APPEND在1.9.1或更高版本可用
Linux原生异步IO不支持FILE_APPEND,并且写入的内容长度必须为4096的整数倍,否则底层会自动在末尾填充0

在这里插入图片描述
tail -f 文件名 实时查看文件内容

http_server.php 写入访问信息到日志

$http = new swoole_http_server("0.0.0.0", 8811);

$http->set([
    'document_root' => '/usr/local/apache/htdocs/swoole/data',
    'enable_static_handler' => true,
]);

$http->on('request', function ($request, $response) {
    $content = [
        'date' => date("Y-m-d H:i:s"),
        'get' => $request->get,
        'post' => $request->post,
        'header' => $request->header
    ];
    swoole_async_writefile(__DIR__."/access.log",json_encode($content).PHP_EOL,function($filename){
        // todo
    },FILE_APPEND);
//    $response->header("Content-Type", "text/html; charset=utf-8");
    $response->cookie("singwa",'xsssss', time() + 1800);
    $response->end("<h1>Hello Swoole. #".rand(1000, 9999)."</h1>".json_encode($request->get));
});

$http->start();

在这里插入图片描述

异步MySQL

7 进程

什么是进程?
进程就是一个正在运行的程序的一个实例。

/*
 * php process.php 会开启子进程
 *
 * 在子进程里开启http服务
 * */
$process = new swoole_process(function(swoole_process $pro){
    // todo
    $pro->exec("/home/work/soft/php/php73/bin/php",[__DIR__.'/../server/http_server.php']);
},false);

$pid = $process->start();
echo $pid.PHP_EOL;
// 回收
swoole_process::wait();

4684是echo的4683创建进程后的id
在这里插入图片描述
在这里插入图片描述
如何去追踪进程之间的关系呐?
在这里插入图片描述
4686,4687,4688,4689是Reactor线程
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述理解Master/Reactor/Manager/Worker/TaskWorker ?
https://baijiahao.baidu.com/s?id=1622252306493560200&wfr=spider&for=pc

8 swoole多进程使用场景

背景
执行多个url
原始方案
同步顺序执行
问题
执行慢
解决方案
引入swoole_process
按需开启N个子进程执行
curl.php

echo "process-start-time:".date("Y-m-d H:i:s").PHP_EOL;
require_once 'function.php';
$workers = [];
$urls = [
    'http://baidu.com',
    'http://qq.com',
    'http://sina.com.cn',
    'http://baidu.com?search=singwa',
    'http://baidu.com?search=singwa2',
    'http://baidu.com?search=imooc',
];

for ($i=0;$i<6;$i++){
    // 子进程
    $process = new swoole_process(function(swoole_process $worker) use ($urls,$i){
        $content = curlGet($urls[$i]);
        $worker->write(json_encode($content));
    },true);
    $pid = $process->start();
    $workers[$pid] = $process;
}

foreach($workers as $process){
    echo $process->read().PHP_EOL;
}
echo "process-end-time:".date("Y-m-d H:i:s").PHP_EOL;
swoole_process::wait();

function.php

/**
 * curl发送数据
 * @param $url
 * @param $data
 * @param $type
 * @return mixed
 */
function curlData($url)
{
    $ch     = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT_MS, 5000);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 3000);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $r = curl_exec($ch);
    curl_close($ch);
    return $r;
}

/**
 * curl
 * @param $url
 * @param $data
 * @param int $timeout
 * @return mixed
 */
function curlPostData($url, $data, $timeout=10)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    //curl_setopt($ch, CURLOPT_HEADER, false);
    //curl_setopt($ch, CURLOPT_TIMEOUT, 5);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch,CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
    $rst = curl_exec($ch);
    curl_close($ch);
    return $rst;
}

/**
 * [curlGet description]
 * @param string  $url     [description]
 * @param integer $timeOut [description]
 * @param [type]  $header  [description]
 * @param [type]  $data    [description]
 * @return [type]  [description]
 */
function curlGet($url = '', $timeOut = 3, $header = null){
    $return = [
        'result' => '',
        'error'  => '',
        'info'   => '',
    ];

    if (empty($url)) {
        $return['error'] = 'url empty';
        return $return;
    }

    $timeOut = intval($timeOut);
    $timeOut <= 0 && $timeOut = 3;

    $curl = curl_init();
    if(is_array($header)){
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
    }

    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
    curl_setopt($curl, CURLOPT_TIMEOUT, $timeOut);
    $ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36';
    curl_setopt($curl, CURLOPT_USERAGENT, $ua);

    $return['result'] = curl_exec($curl);
    $return['error']  = curl_error($curl);
    $return['info']   = curl_getinfo($curl);

    curl_close($curl);
    return $return;
}

9 Swoole Table

介绍:
swoole_table一个基于共享内存和锁实现的超高性能,并发数据结构
用于解决多进程/多线程数据共享和同步加锁问题。
table.php 简单实用

// 创建内存表
$table = new swoole_table(1024);

// 创建字段
$table->column('id',$table::TYPE_INT,4);
$table->column('name',$table::TYPE_STRING,20);
$table->column('age',$table::TYPE_INT,4);

$table->create();

$table->set('singwa_imooc',['id'=>1,'name'=>'singwa','age'=>18]);

print_r($table->get('singwa_imooc'));

在这里插入图片描述
table.php 高级使用

// 创建内存表(当进程执行完毕 内存表会自动释放)
$table = new swoole_table(1024);

// 创建字段
$table->column('id',$table::TYPE_INT,4);
$table->column('name',$table::TYPE_STRING,20);
$table->column('age',$table::TYPE_INT,4);

$table->create();

$table->set('singwa_imooc',['id'=>1,'name'=>'singwa','age'=>18]);

print_r($table->get('singwa_imooc'));

// 另一种方案 数组形式
$table['singwa_imooc2'] = [
    'id' => 2,
    'name' => 'singwa2',
    'age' => 28
];

$table->incr('singwa_imooc2','age',2);  // 30 自增2
$table->decr('singwa_imooc2','age',10);  // 20 自减10

print_r($table['singwa_imooc2']); // 对象

echo 'delete start'.PHP_EOL;
$table->del('singwa_imooc2');  // 删除key
print_r($table['singwa_imooc2']); // 为空

在这里插入图片描述

10 Swoole协程(Coroutine)

协程可以理解为纯用户态的线程,其通过协作而不是抢占来进行切换

协程相关api 查看文档

redis.php

/**
 * 1 redis
 * 2 mysql
 * 同步执行时间 = redis时间 + mysql时间
 * redis+mysql
 * 通过异步执行 = max(redis时间 , mysql时间)
 */
$http = new swoole_http_server("0.0.0.0", 8001);

$http->on("request", function ($request, $response) {
    // 获取redis中的key值 然后输出浏览器

    $redis = new Swoole\Coroutine\Redis();
    $redis->connect('127.0.0.1',6379);
    $value = $redis->get($request->get['key']);

    /*$swoole_mysql = new Swoole\Coroutine\MySQL();
    $swoole_mysql->connect([
        'host' => '127.0.0.1',
        'port' => 3306,
        'user' => 'root',
        'password' => 'root',
        'database' => 'swoole',
    ]);
    $res = $swoole_mysql->query('show tables');*/

    $response->header("Content-Type","text/plain");
    $response->end($value);
});

$http->start();

注意

测试tcp服务器方法
netstat -anp | grep 9501
通过telnet方式登录远程主机:telnet 127.0.0.1 9501
tcp客户端脚本
查看当前worker进程数:ps -aft | grep tcp_server.php
在这里插入图片描述
cat /proc/cpuinfo | grep ‘cores’ | uniq 查看系统核心数
在这里插入图片描述
连接:telnet 127.0.0.1 9501
ctrl+] ,quit退出
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值