PHP Swoole 安装
网络通信引擎 异步非堵塞IO场景
源码 //www.php.net/downloads.php
https://www.php.net/distributions/php-7.4.6.tar.gz
tar -xzvf php-7.4.6.tar.gz # 解压命令
./configure --prefix=/home/study/php # 安装至某 路径
make # 编译
make install # 安装
执行文件放在 bin 目录
php -m # 查看 PHP 扩展
PHP执行命令
alias 命令=命令的绝对路径
vim /.bash_profile
alias php=/home/work/soft/php/bin/php # 添加
source /.bash_profile # 注意
source FileName
在当前bash环境下读取并执行FileName中的命令
用于重新执行刚修改的初始化文档 如 .bash_profile 和 .profile等等
注 该命令通常用命令 . 来替代
如 source /etc/profile 与 . /etc/profile是等效的
php -i | grep php.ini # 查找PHP的配置文件
Swoole源码编译
swoole源码 //gitee.com/swoole/swoole.gitphpize 用来扩展php模块 通过phpize 建立php 外挂模块 解决没有configure问题
/usr/local/php/bin/phpize # 在需要执行的目录执行这行代码
./configure --with-php-config=/usr/local/php/bin/php-config
make
make install
在PHP的扩展目录中 swoole.so 文件
PECL
PECL 发布时间晚于 Github 发布时间
Swoole 项目已收录到 PHP 官方扩展库,除了手工下载编译外,还可以通过 PHP 官方提供的 pecl 命令,一键下载安装
pecl install swoole
No releases for package "pecl/swoole" exist
install failed
地址 //pecl.php.net/package/swoole
解决方案:
1.更新channel
$ pecl channel-update https://pecl.php.net/channel.xml
Update of Channel "pecl.php.net" succeeded
2.清除pear缓存
$ pear clear-cache
reading directory /var/cache/pear
96 cache entries cleared
$ pear update-channels
Updating channel "doc.php.net"
Update of Channel "doc.php.net" succeeded
Updating channel "pear.php.net"
Update of Channel "pear.php.net" succeeded
Updating channel "pecl.php.net"
Channel "pecl.php.net" is up to date
$ pear upgrade
重新执行
pecl install https://pecl.php.net/get/swoole-4.5.1.tgz
enable sockets supports? [no] : yes
enable openssl support? [no] : yes
enable http2 support? [no] : yes
enable mysqlnd support? [no] : yes
PHP7支持swoole
php.ini文件添加 extension=swoole.so查看是否添加成功 php -m
在swoole/examples/server下执行php echo.php
查看是否执行端口 9501
netstat -anp|grep 9501
网络通信引擎
TCP服务&TCP客户端TCP服务
Swoole 创建TCP服务器 | 创建UDP服务器
//创建Server对象 监听 127.0.0.1:9501 端口
$serv = new swoole_server("127.0.0.1", 9501);//swoole_server->set函数用于设置swoole_server运行时的各项参数
$serv->set(['worker_num' => 6 , // worker进程数 cpu 1-4倍
'max_request' => 10000,
]);
$serv->on('connect', function ($serv, $fd, $reactor_id) { echo "Client: {$reactor_id} - {$fd}-Connect.\n"; });
$serv->on('receive', function ($serv, $fd, $reactor_id, $data) { $serv->send($fd, "Server: {$reactor_id} - {$fd}".$data); });
$serv->on('close', function ($serv, $fd) {//监听连接关闭事件
echo "Client: Close.\n";
});
$serv->start();//启动服务器
测试tcp服务器方法
netstat -anp | grep 9501
通过telnet方式登录远程主机 telnet 127.0.0.1 9501
tcp客户端脚本 查看当前worker进程数 ps -aft | grep tcp_server.php
Tips 为了保证程序执行的完整性 当修改tcp服务器脚本后最好设置平滑重启worker进程
TCP客户端
<?php // 连接 swoole tcp 服务$client = new swoole_client(SWOOLE_SOCK_TCP);
if(!$client->connect("127.0.0.1", 9501)) { echo "连接失败"; exit; }
// php cli常量
fwrite(STDOUT, "请输入消息:");
$msg = trim(fgets(STDIN));
$client->send($msg);// 发送消息给 tcp server服务器
$result = $client->recv();// 接受来自server 的数据
echo $result;
HTTP服务
$http = new swoole_http_server("0.0.0.0", 8811);//添加测试一 获取参数并打印出来//$http->on('request', function ($request, $response) {
// $response->cookie("singwa",'xsssss', time() + 1800);
// $response->end('sss'.json_encode($request->get));
//});
$http->set(['enable_static_handler' => true,
'document_root' => "/home/work/hdtocs/swoole_mooc/data",
]
);
$http->on('request', function($request, $response) {//print_r($request->get);
$content = [
'date:' => date("Ymd 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->cookie("singwa", "xsssss", time() + 1800);
$response->end("sss". json_encode($request->get));
});
$http->start();
WebSocket服务
WebSocket协议 基于TCP 新的网络协议 实现浏览器与服务器 全双工(full-duplex)通信 允许服务器主动发送信息给客户端为什么需要WebSocket
HTTP 通信只能由客户端发起,WebSocket特点 建立在TCP协议之上 性能开销小 通信高效
客户端 与任意服务器通信 协议标识符ws wss持久化网络通信协议
Swoole服务端实现
面向过程 procedure_ws_server.php$server = new swoole_websocket_server("0.0.0.0", 9912);//配置静态文件根目录 可选
$server->set(['enable_static_handler' => true,
'document_root' => "/home/wwwroot/server.com/public/",
]
);//监听websocket连接打开事件
$server->on('open', 'onOpen');
function onOpen($server, $request) {
print_r($request->fd);
}// 监听ws消息事件
$server->on('message', function (swoole_websocket_server $server, $frame) {
echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
$server->push($frame->fd, "singwa-push-secesss");
});
$server->on('close', function ($ser, $fd) {
echo "client {$fd} closed\n";
});
$server->start();
SwooleWebSocket服务优化
基础类库 面向对象 object_ws_server.phpclass Ws {
CONST HOST = "0.0.0.0";
CONST PORT = 9912;
public $ws = null;
public function __construct() {
$this->ws = new swoole_websocket_server(self::HOST, self::PORT); //配置静态文件根目录 可选
$this->ws->set( ['enable_static_handler' => true,
'document_root' => "/home/wwwroot/server.com/public/",
]
);
$this->ws->on("open", [$this, 'onOpen']);
$this->ws->on("message", [$this, 'onMessage']);
$this->ws->on("close", [$this, 'onClose']);
$this->ws->start();
}
public function onOpen($ws, $request) {
print_r($request->fd);
}
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) {
echo "clientid:{$fd}\n";
}
}
$obj = new Ws();
Swoole客户端实现
ws_client.html<!DOCTYPE html><html><head><meta charset="UTF-8"><title></title></head>
<body>
<h1>swoole-ws测试</h1>
<script>
var wsUrl = "ws://120.77.206.215:9912";
var websocket = new WebSocket(wsUrl); //实例对象的onopen属性
websocket.onopen = function(evt) {
websocket.send("hello-sinwa");
console.log("conected-swoole-success");
}
// 实例化 onmessage
websocket.onmessage = function(evt) {
console.log("ws-server-return-data:" + evt.data);
}
//onclose
websocket.onclose = function(evt) {
console.log("close");
}
//onerror
websocket.onerror = function(evt, e) {
console.log("error:" + evt.data);
}
</script>
</body>
</html>
WebSocket 静态文件目录 通过HTTP服务测试
Swoole异步Task任务使用
执行耗时的操作 发送邮件 广播等投递异步任务之后程序会 继续往下执行 不 等待任务执行完 继续向下执行
class Ws {
CONST HOST = "0.0.0.0";
CONST PORT = 9912;
public $ws = null;
public function __construct() {
$this->ws = new swoole_websocket_server(self::HOST, self::PORT);
$this->ws->set( [ 'worker_num' => 2, 'task_worker_num' => 2, ]
); //注册Server的事件回调函数
$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();
}
public function onOpen($ws, $request) {
var_dump($request->fd);
}
public function onMessage($ws, $frame) {
echo "ser-push-message:{$frame->data}\n";
// todo 10s
$data = [
'task' => 1,
'fd' => $frame->fd,
];
//投递异步任务
//注意 程序会继续往下执行 不会等待任务执行完后再继续向下执行
$ws->task($data);
//客户端会马上收到以下信息
$ws->push($frame->fd, "server-push:".date("Y-m-d H:i:s"));
}
public function onTask($serv, $taskId, $workerId, $data) {
print_r($data);
// 耗时场景 10s
sleep(10);
return "on task finish"; // 告诉worker 并返回给onFinish的$data
}
public function onFinish($serv, $taskId, $data) {
echo "taskId:{$taskId}\n";
echo "finish-data-sucess:{$data}\n";
}
public function onClose($ws, $fd) {
echo "clientid:{$fd}\n";
}
}
$obj = new Ws();
Swoole异步非堵塞IO
异步 阻塞 和 IO模型同步和异步 消息通知机制
同步 调用发出之后不会立即返回 但一旦返回 则返回最终结果
异步 调用发出之后 被调用方立即返回消息 但返回 并非最终结果 被调用者通过状态 通知机制等来通知调用者 或通过回调函数来处理结果
阻塞(block)和非阻塞(nonblock) 调用者等待 被调用者返回调用结果时的状态
阻塞 调用结果返回之前 调用者会被挂起 调用者只 在得到返回结果之后 才能继续
非阻塞 调用者在结果返回之前 不会被挂起
Swoole IO模型
blocking IO 阻塞式IOnonblocking IO 非阻塞IO
multiplexing IO 多路复用IO
signal driven IO 事件驱动式IO
asynchronous IO 异步IO
真正执行IO过程的阶段是 内核 内存数据拷贝到 进程内存中
Swoole异步毫秒定时器 异步高精度定时器 粒度为毫秒级
swoole_timer_tick(2000, function ($timer_id) {//每隔2000ms触发一次
echo "tick-2000ms\n";
});
swoole_timer_after(3000, function () {//3000ms后执行此函数
echo "after 3000ms.\n";
});
Swoole异步文件系统IO
异步文件系统IO异步读
/ 读取文件 __DIR__ 文件不存在会返回false* 成功打开文件立即返回true
* 数据读取完毕后会回调指定的callback函数。
*/
$result = swoole_async_readfile(__DIR__."/1.txt", function($filename, $fileContent) {
echo "filename:".$filename.PHP_EOL; // \n \r\n
echo "content:".$fileContent.PHP_EOL;
});
//命名空间风格
$result = Swoole\Async::readfile(__DIR__."/1.txt", function($filename, $fileContent) {
echo "filename:".$filename.PHP_EOL; // \n \r\n
echo "content:".$fileContent.PHP_EOL;
});
var_dump($result);
echo "start".PHP_EOL;
异步写(如日志)
$http->on('request', function($request, $response) {$content = [
'date:' => date("Ymd 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->end("response ". json_encode($request->get));
});
异步MySQL
class AsyncMySql {public $dbSource = "";
public $dbConfig = [];
public function __construct() {
//new swoole_mysql;
$this->dbSource = new Swoole\Mysql;
$this->dbConfig = [
'host' => '127.0.0.1',
'port' => 3306,
'user' => 'root',
'password' => 'test',
'database' => 'test',
'charset' => 'utf8',
];
}
public function update() {}
public function add() {}
public function execute($id, $username) {
$this->dbSource->connect($this->dbConfig, function($db, $result) use($id, $username) {
echo "mysql-connect".PHP_EOL;
if($result === false) {
var_dump($db->connect_error);
// todo
}
$sql = "select * from cmf_user where id=1";
//$sql = "update test set `username` = '".$username."' where id=".$id;
// insert into
// query (add select update delete)
$db->query($sql, function($db, $result){ // select => result返回的是 查询的结果内容
if($result === false) { // todo
var_dump($db->error);
}elseif($result === true) {// add update delete
// todo
var_dump($db->affected_rows);
}else {
print_r($result);
}
$db->close();
});
});
return true;
}
}
$obj = new AsyncMySql();
$flag = $obj->execute(1, 'singwa-111112');
var_dump($flag).PHP_EOL;
echo "start".PHP_EOL;
Swoole异步Redis
swoole用 redis 前置条件redis 服务hiredis 库
编译 swoole 需要加入 -enable-async-redis
安装 hiredis
使用Redis客户端 需要安装hiredis库 下载hiredis源码后 执行
make -j
sudo make install
sudo ldconfig
hiredis下载地址
启用异步Redis客户端
编译swoole时 在configure指令中加入--enable-async-redis
# ./configure --with-php-config=/usr/local/php/bin/php-config --enable-async-redis
make clean
make -j
sudo make install
查看PHP的swoole扩展 php -m
查看hiredis是否编译安装成功 php --ri swoole
$redisClient = new swoole_redis;// Swoole\Redis
$redisClient->connect('127.0.0.1', 6379, function(swoole_redis $redisClient, $result) {
echo "connect".PHP_EOL;
var_dump($result);
// 同步 redis (new Redis())->set('key',2);
$redisClient->keys('*gw*', function(swoole_redis $redisClient, $result) {
var_dump($result);
$redisClient->close();
});
});
千年的回眸
Cannot find php-config. Please use --with-php-config=PATH
./configure --with-php-config=/usr/local/php/bin/php-config
千年的回眸
wget -o ./swoole.tar.gz https://github.com/swoole/swoole-src/archive/master.tar.gz
phpize && ./configure --with-php-config=/usr/local/php/bin/php-config --enable-openssl --enable-http2 && make && make install