Swoole教程(一):服务器开发
Swoole是什么
Swoole 是一个使用 C++ 语言编写的基于异步事件驱动和协程的并行网络通信引擎,为 PHP 提供协程、高性能网络编程支持。提供了多种通信协议的网络服务器和客户端模块,可以方便快速的实现 TCP/UDP服务、高性能Web、WebSocket服务、物联网、实时通讯、游戏、微服务等,使 PHP 不再局限于传统的 Web 领域
以上是swoole官网的介绍,这里说下我自己的一些看法。与其他 PHP 扩展(如 Opcache、Imagick、Redis 等)一样,Swoole 也是一个外部扩展,但是Swoole 带来了一些概念。例如,服务器模式、原生协程、协程调度器、多路复用和异步 I/O、协程调度器和异步 I/O 状态管理、进程管理等。与其他普通php扩展不一样的地方是,Swoole为php开发人员带来的编程方式的改变,让php编程更加Nodejs化,Swoole 为开发者提供使用 PHP 语法方式创建和管理 TCP、HTTP、WebSocket 服务器的手段,与 PHP-FPM 仅支持无状态模型不同,使用 Swoole,您可以在服务器内提供缓存和管理状态以提高性能,达到类型java等静态语言那样常驻内存的效果。它完全提供了一种新的PHP应用程序运行模式
为什么要使用Swoole
Swoole已经经过了压力测试和验证
国内很多公司都已经用上了swoole了,在招聘网站输入swoole可以看到很多的招聘需求。据统计,swoole已经在高流量的生产环境中经过了压力测试和验证。说明php使用swoole是值得信赖和使用的。
支持多线程
如上所述,Swoole具有多个网络工作者和独立的任务工作者,这使得代码可以延期。长时间运行的进程的延期为你的API和应用程序中许多以前无法实现的方法打开了大门,例如将处理过程推迟到发送响应之后
支持协程
Swoole的Coroutine支持意味着你可以处理大量的请求,即使你正在做大量昂贵的I/O(例如,与你的数据库对话,使用文件系统,进行HTTP请求)。
自举系统只被加载一次,所以你消除了每次请求的15-25%的税。因为它是你初始化的一部分,这意味着你在每次请求中使用的资源更少,包括内存和CPU。对于一些应用程序,这可能意味着你需要更少的服务器,由于异步运行时的存在,这可能已经成为可能。
不需要Server支持,本身就是Server
说到更少的服务器,你不需要网络服务器,因为Swoole就是网络服务器。你可以启动一个只安装PHP的docker容器,你不需要NGINX坐在它的前面。
你不必把NGINX或Apache编在同一个容器中,它可以只是PHP。而且,如果你正在做任何形式的容器化,拥有这些单进程的容器和一种语言是真正的黄金标准。
更多并发请求支持
异步服务器能够处理的请求是非异步服务器的4到7倍,Node已经一次又一次地证明了这点。
安装swoole
通过宝塔安装swoole
这种方式是最简单的方式。直接安装php的swoole插件即可
通过源码编译安装swoole
此方法同时适用于ubuntu和centos等linux系统
git clone https://github.com/swoole/swoole-src.git
cd swoole-src
phpize
./configure
make && make install
编译安装到系统成功后,需要在 php.ini 中加入一行 extension=swoole.so 来启用 Swoole 扩展
通过docker直接安装启用
docker pull phpswoole/swoole
使用swoole进行服务端编程
Tcp服务器
网络编程中常用Tcp服务器,即使最常见的HTTP1和HTTP2也是基于TCP协议上封装的的,HTTP3才是基于UDP。用Swoole创建服务一般2种方式,异步风格和协程风格,分别对应的Server类是Swoole\Server和Swoole\Coroutine\Server ,
异步风格
$server = new Swoole\Server("127.0.0.1", 9501);
$server->on('connect', function ($server, $fd){
echo "Client:Connect.\n";
});
$server->on('receive', function ($server, $fd, $reactor_id, $data) {
$server->send($fd, 'Swoole: '.$data);
$server->close($fd);
});
$server->on('close', function ($server, $fd) {
echo "Client: Close.\n";
});
$server->start();
协程风格
协程风格下链接是动态创建和销毁的,可以设置5秒没收到数据自动超时关闭
go(function(){
$server = new \Swoole\Coroutine\Server('0.0.0.0',9501,false,true);
$server->handle(function (\Swoole\Coroutine\Server\Connection $conn){
echo '您好,欢迎进入聊天室'."\n";
$socket = $conn->exportSocket();
while (true){
$data = $conn->recv(5);
if ($data === '' || $data === false) {
// $errCode = swoole_last_error();
// $errMsg = socket_strerror($errCode);
echo "协程风格下链接是动态创建和销毁的,可以设置5秒没收到数据自动超时关闭\n";
$conn->close();
break;
}
//发送数据
$conn->send('您好,您输入的data是: '.$data);
}
echo '客户端:'.$socket->fd.'的链接已经关闭';
});
$server->start();
});
Swoole\Event::wait();
由于我个人异步风格方式写得比较多,后面就都用协程风格来写。
UDP服务器
Swoole创建Udp服务的类是Swoole\Coroutine\Socket,异步方式请同学自行查看官方文档。
<?php
use function Swoole\Coroutine\go;
go(function (){
$socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); //domain, type, protocol
// domain=AF_INET, 使用ipv4通信
// type=SOCK_DGRAM, 无保障的面向消息的socket,主要用于在网络上发广播信息
$socket->bind('0.0.0.0', 9501);//绑定监听的ip和端口
$socket->listen(128);
$peer = null;
while (true){
$data = $socket->recvfrom($peer,5);
var_dump($data);
if($peer){
echo "收到来自{$peer['address']}:{$peer['port']}的消息 : $data\n";
$socket->sendto($peer['address'], $peer['port'], "你好,你的消息是: $data");
}
}
});
Swoole\Event::wait();
HTTP服务器
HTTP服务器对应的类是Swoole\Coroutine\Http\Server,这个就比较常用了,同TCP一样也是通过handle来处理请求,但回调函数置入了Swoole\Http\Request 和 Swoole\Http\Response 两个对象,这就非常好办了
<?php
use Swoole\Coroutine\Http\Server;
use function Swoole\Coroutine\run;
run(function () {
$server = new Server('0.0.0.0', 9501, false);
//可以通过request对象来进一步判断映射路由,
$server->handle('/', function ($request, $response) {
$info = $request->header['host'];
$response->end("<h1>您访问了路径/,来自{$info}</h1>");
});
$server->handle('/test', function ($request, $response) {
$response->end("<h1>您访问了路径/test</h1>");
});
$server->handle('/stop', function ($request, $response) use ($server) {
$response->end("<h1>您访问了路径/stop</h1>");
$server->shutdown();
});
$server->start();
});
WebSocket服务器
WebSocket同样是采用Swoole\Coroutine\Http\Server,但是处理流程有点不一样。请求进来时必须先调用$response->upgrade();向客户端发送WebSocket握手消息,将请求升级为websocket请求。然后通过while(true)循环处理消息的接收和发送
<?php
use Swoole\WebSocket\CloseFrame;
use function Swoole\Coroutine\go;
go(function (){
$server = new \Swoole\Coroutine\Http\Server('0.0.0.0',9502,false);
$server->handle('/',function (\Swoole\Http\Request $request,\Swoole\Http\Response $response){
$response->upgrade();
while (true) {
$frame = $response->recv();
if ($frame === '') {
$response->close();
break;
} else if ($frame === false) {
echo '发生错误: ' . swoole_last_error() . "\n";
$response->close();
break;
} else {
if ($frame->data == 'close' || get_class($frame) === CloseFrame::class) {
$response->close();
break;
}
$response->push("你好 {$request->fd},你说了:{$frame->data}!");
$response->push("你好啊{$request->fd},我又一次回复 {$frame->data}给你");
}
}
echo '客户端断开了'."\n";
});
$server->start();
});
Swoole\Event::wait();
其他服务端
Swoole还可以做很多事情,比如用于物联网的Mqtt服务器,比如用于定时任务处理,还比如消息队列等等,本篇作为开篇先讲到这,后面有时间我会继续输出Swoole相关的知识。
发表评论