Swoole HTTP模块,我们一起来研究一下?
- 内容介绍
- 文章标签
- 相关推荐
本文共计4324个文字,预计阅读时间需要18分钟。
目标:了解Swoole的HTTP服务器使用、TCP服务开发,以及实际项目中遇到的问题如粘包处理、代理热更新、用户验证等。Swoole与现有框架结合:风格、偏基础重写、环境、PHP版本:Swoole版本:H
目标
- 了解swoole的github.com/swoole/swoole-src
- zphp开发框架:github.com/shenzhe/zphp
HTTP Server
- 静态文件处理
- 动态请求与框架结合
# 查看SWOOLE版本 $ php -r 'echo SWOOLE_VERSION;' 4.3.1
推荐(免费):swoole
基础概念
HTTP报文
关于HTTP请求报文的组成结构
HTTP请求报文结构
POST /search HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-silverlight, application/x-shockwave-flash, */* Referer: www.google.cn/ Accept-Language: zh-cn Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; TheWorld) Host: www.google.cn Connection: Keep-Alive Cookie: PREF=ID=80a06da87be9ae3c:U=f7167333e2c3b714:NW=1:TM=1261551909:LM=1261551917:S=ybYcq2wpfefs4V9g; NID=31=ojj8d-IygaEtSxLgaJmqSjVhCspkviJrB6omjamNrSm8lZhKy_yMfO2M4QMRKcH1g0iQv9u-2hfBW7bUFwVh7pGaRUb0RnHcJU37y- FxlRugatx63JLv7CWMD6UB_O_r hl=zh-CN&source=hp&q=domety
关于HTTP响应报文的组成结构
HTTP响应报文结构
HTTP/1.1 200 OK Date: Mon, 23 May 2005 22:38:34 GMT Content-Type: text/html; charset=UTF-8 Content-Encoding: UTF-8 Content-Length: 138 Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux) ETag: "3f80f-1b6-3e1cb03b" Accept-Ranges: bytes Connection: close
创建HTTP服务器
Swoole在1.7.7版本后内置HTTP服务器,可创建一个异步非阻塞多进程的HTTP服务器。Swoole的HTTP服务器对HTTP协议支持的并不完整,建议仅作为应用服务器,并在前端增加Nginx作为代理。
因为Swoole是在CLI命令行中执行的,在传统的NGINX+FastCGI模式下很多root的shell是无法执行的,而使用Swoole服务器就能很好的控制rsync、git、svn等。
使用Swoole的API,构建HTTP服务器需要4个步骤
- 创建Server对象
- 设置运行时参数
- 注册事件回调函数
- 启动服务器
# 创建应用 $ mkdir test && cd test # 创建并编辑服务器文件 $ vim server.php
<?php //创建HTTP服务器对象 $host = "0.0.0.0"; $port = 9501; $server = new swoole_img.558idc.com/uploadfile/"; $server->set($configs);
POST解析swoole;
proxy_set_header Connection "";
proxy_127.0.0.1:9501;
}
}
}请求对象
swoole_input"),有时服务器不需要解析HTTP的POST请求参数。
Swoole1.7.18+版本增加了www.baidu.com";
$response->redirect($url, 301);
});
$server->start();Http\Response->write
write方法用于启用HTTP的chunk分段以向浏览器发送相应的内容,使用write分段发送数据后end方法将不再接收任何参数,调用end方法后会发送一个长度为0的分段chunk表示数据传输完毕。
bool Http\Response->write(string $data)
参数$data表示要发送的数据内容,最大长度不得超过2MB,受buffer_output_size配置项控制。
Http\Response->sendfile
sendfile用于发送文件到浏览器
function Http\Response->sendfile(
string $filename,
int $offset = 0,
int $length = 0
)
string $filename 表示要发送的文件名称,文件不存在或没有访问权限则会发送失败。int $offset 表示上传文件的偏移量,可以指定从文件在中间部分开始传输数据,用于断点续传,适用于Swoole1.9.11+。int $length 表示发送数据的尺寸,默认为整个文件的尺寸,适用于Swoole1.9.11+。
$response->header("Content-Type", "image/jpeg");
$filepath = $request->server["request_uri"];
$filename = __DIR__.$filepath;
$response->sendfile($filename);
由于Swoole底层无法推断要发送文件的媒体类型MIME格式,因此需要应用程序指定Content-Type。调用sendfile前不得使用write方法发送HTTP数据段Chunk,调用sendfile后Swoole底层会自动执行end方法,另外sendfile不支持gzip压缩。
Http\Response->end
end方法用于发送HTTP响应体,并结束请求处理。
function Http\Response->end(string $html);
end方法只能调用一次,如果需要分多次向客户端发送数据下需使用write方法,send操作后将会向客户端浏览器发送HTML内容。如果客户端开启了KeepAlive连接会保持,服务器会等待下一次请求。如果没有开启KeepAlive服务器将会切断连接。
Http\Response->detach
detach表示分离响应对应,调用后$response对象销毁时将不会自动执行end方法,一般detach会与Http\Response::create以及Server::send配合使用,适用于Swoole2.2.0+版本。
function Http\Response->detach():bool
detach方法操作后,若客户端已经完成响应则会返回true,否则返回false。
detach应用于跨进程响应
在某些情况下需要在Task任务进程中对客户端发出响应,此时可以利用detach方法使$response对象独立,如此一来在Task任务进程中就可以重新构建$response对象以发起HTTP请求响应。
<?php
//创建HTTP服务器对象
$host = "0.0.0.0";
$port = 9501;
$server = new swoole_http_server($host, $port);
//设置服务器运行参数
$configs = [];
$configs["worker_num"] = 1;//设置Worker工作进程数量
$configs["task_worker_num"] = 1;//设置Task任务进程数量
$configs["daemonize"] = 0;//设置是否已后台守护进程运行
$server->set($configs);
//注册客户端请求处理回调函数
$server->on("request", function(swoole_http_request $request, swoole_http_response $response) use($server){
//分离响应对象
$response->detach();
//在Task任务进程中对客户端发出响应
$fd = strval($response->fd);
$server->task($fd);
});
//注册异步任务处理回调函数
$server->on("task", function(swoole_http_server $server, $worker_id, $data){
//创建响应对象
$response = swoole_http_response::create($data);
//向客户端发送响应
$html = "in task";
$response->end($html);
});
//注册Task异步任务执行完毕回调函数
$server->on("finish", function(){
echo "[finish] task".PHP_EOL;
});
//启动服务器
$server->start();
detach方法应用于发送任意内容
在某些特殊场景下,需要对客户端发送特殊的响应内容,Http\Response对象自带的end方法无法满足需求,可以使用detach方法分离响应对象,然后自行组包并使用Server::send方法发送数据。
<?php
//创建HTTP服务器对象
$host = "0.0.0.0";
$port = 9501;
$server = new swoole_http_server($host, $port);
//设置服务器运行参数
$configs = [];
$configs["worker_num"] = 2;//设置Worker工作进程数量
$configs["daemonize"] = 0;//设置是否已后台守护进程运行
$server->set($configs);
//注册监听客户端HTTP请求回调事件
$server->on("request", function(swoole_http_request $request, swoole_http_response $response) use($server){
//分离响应对象
$response->detach();
//自行组包并使用Server::send方法发送数据
$fd = $response->fd;
$message = "HTTP/1.1 200 OK\r\n";
$message .= "Server: server\r\n";
$message .= "\r\n";
$message .= "Hello World\n";
$server->send($fd, $message);
});
//启动服务器
$server->start();
Http\Response::create
create静态方法用于构造新的Http\Response响应对象,使用前必须调用detach方法将旧有$response对象分离,否则 可能会造成同一个请求发送两次响应内容。
function Http\Response::createE(int $fd) : Http\Response
create静态方法的参数$fd表示需要绑定连接的文件描述符,调用Http\Response对象的end方法和write方法时会向此连接发送数据。如果调用成功则返回一个新的Http\Response对象,否则失败返回false,适用于Swoole2.2.0+版本。
注册事件回调函数
Http\Server注册事件回调函数于Http\Server->on相同,不同之处在于HTTP\Server->on不接受onConnect和onReceive回调设置,Http\Server->on会额外接受一种新的事务类型onRequest。
onRequest 事件
onRequest事件适用于Swoole1.7.7+版本,当服务器收到一个完整的HTTP请求后会调用onRequest函数。
$server->on("request", function(swoole_http_request $request, swoole_http_response $response) use($server){
$html = "success";
$response->end($html);
});
onRequest回调函数共有两个参数
swoole_http_requst $request HTTP请求信息对象,包含了Header/GET/POST/Cookie等信息。swoole_http_response $response HTTP响应信息对象,支持Cookie/Header/Status等HTTP操作。
在onRequest回调函数返回时会销毁$request和$response对象,如果未执行$response->end()操作,Swoole底层会自动执行一次$response->end("")。
$request和$response对象在传递给其它函数时,是不需要添加&取地址的引用符号的,传递后引用计数会增加,当onRequest退出时并不会被销毁。
案例

$ vim http_server.php
<?php
$addr = "0.0.0.0";
$port = 9501;
$svr = new swoole_http_server($addr, $port);
$svr->on("request", function(swoole_http_request $rq, swoole_http_response $rp){
//处理动态请求
$path_info = $rq->server["path_info"];
$file = __DIR__.$path_info;
echo "\nfile:{$file}";
if(is_file($file) && file_exists($file)){
$ext = pathinfo($path_info, PATHINFO_EXTENSION);
echo "\next:{$ext}";
if($ext == "php"){
ob_start();
include($file);
$contents = ob_get_contents();
ob_end_clean();
}else{
$contents = file_get_contents($file);
}
echo "\ncontents:{$contents}";
$rp->end($contents);
}else{
$rp->status(404);
$rp->end("404 not found");
}
});
$svr->start();
# 创建静态文件
$ vim index.html
index.html
# 测试静态文件
$ curl 127.0.0.1:9501/index.html
# 观察http_server输出
file:/home/jc/projects/swoole/chat/index.html
ext:html
contents:index.html
# 测试动态文件
$ vim index.php
<?php
echo "index.php";
#观察http_server日志输出
file:/home/jc/projects/swoole/chat/index.php
ext:php
contents:index.php
获取动态请求的参数
$ vim http_server.php
<?php
$addr = "0.0.0.0";
$port = 9501;
$svr = new swoole_http_server($addr, $port);
$svr->on("request", function(swoole_http_request $rq, swoole_http_response $rp){
//获取请求参数
$params = $rq->get;
echo "\nparams:".json_encode($params);
//处理动态请求
$path_info = $rq->server["path_info"];
$file = __DIR__.$path_info;
echo "\nfile:{$file}";
if(is_file($file) && file_exists($file)){
$ext = pathinfo($path_info, PATHINFO_EXTENSION);
echo "\next:{$ext}";
if($ext == "php"){
ob_start();
include($file);
$contents = ob_get_contents();
ob_end_clean();
}else{
$contents = file_get_contents($file);
}
echo "\ncontents:{$contents}";
$rp->end($contents);
}else{
$rp->status(404);
$rp->end("404 not found");
}
});
$svr->start();
测试带参数的请求
$ curl 127.0.0.1:9501?k=v
观察请求参数的输出
params:{"k":"v"}
file:/home/jc/projects/swoole/chat/index.html
ext:html
contents:index.html
静态文件处理
$ vim mimes.php
<?php
return [
"jpg"=>"image/jpeg",
"jpeg"=>"image/jpeg",
"bmp"=>"image/bmp",
"ico"=>"image/x-icon",
"gif"=>"image/gif",
"png"=>"image/png",
"css"=>"text/css",
"html"=>"text/html",
"xml"=>"text/xml",
"bin"=>"application/octet-stream",
"js"=>"application/javascript",
"tar"=>"application/x-tar",
"ppt"=>"application/vnd.ms-powerpoint",
"pdf"=>"application/pdf",
"swf"=>"application/x-shockwave-flash",
"zip"=>"application/x-zip-compressed"
];
$ vim http_server.php
<?php
//创建HTTP服务器
$addr = "0.0.0.0";
$port = 9501;
$srv = new swoole_http_server($addr, $port);
//设置HTTP服务器参数
$cfg = [];
$cfg["worker_num"] = 4;//设置工作进程数量
$cfg["daemonize"] = 0;//守护进程化,程序转入后台。
$srv->set($cfg);
//处理请求
$srv->on("request", function(swoole_http_request $rq, swoole_http_response $rp) use($srv){
//获取请求文件信息与文件后缀
$path_info = $rq->server["path_info"];
$ext = pathinfo($path_info, PATHINFO_EXTENSION);
//文件是否存在
$file = __DIR__.$path_info;
if(!is_file($file) || !file_exists($file)){
$rp->status(404);
$rp->end("404 NOT FOUND");
}
//处理静态请求
if($ext != "php"){
//设置响应头信息的内容内容
$mimes = include("mimes.php");
$rp->header("Content-Type", $mimes[$ext]);
//获取静态文件内容
$contents = file_get_contents($file);
//返回内容
$rp->end($contents);
}
});
//启动服务
$srv->start();
发送请求,浏览器访问127.0.0.1:9501/test.jpeg,查看图片。
面向对象
$ vim http_server.php
<?php
class HttpServer
{
public static function run($host, $port, $options=[])
{
$srv = new swoole_http_server($host, $port);
if(!empty($options)){
$srv->set($options);
}
$srv->on("request", function(swoole_http_request $rq, swoole_http_response $rp) use($srv){
$rp->end("test");
$srv->close($rq->fd);
});
$srv->start();
}
}
HttpServer::run("127.0.0.1", 9501, ["worker_num"=>2, "daemonize"=>0]);
压力测试
使用Apache Bench工具进行压力测试可以发现,swoole_http_server远超过PHP-FPM、Golang自带的HTTP服务器、Node.js自带的HTTP服务器,性能接近Nginx的静态文件处理。
Swoole的http server与PHP-FPM的性能对比
安装Apache的压测工作ab
$ sudo apt install apache2-util
使用100个客户端跑1000次,平均每个客户端10个请求。
$ ab -c 100 -n 1000 127.0.0.1:9501/index.php
Concurrency Level: 100
Time taken for tests: 0.480 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 156000 bytes
HTML transferred: 9000 bytes
Requests per second: 2084.98 [#/sec] (mean)
Time per request: 47.962 [ms] (mean)
Time per request: 0.480 [ms] (mean, across all concurrent requests)
Transfer rate: 317.63 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 3.0 0 12
Processing: 4 44 10.0 45 57
Waiting: 4 44 10.1 45 57
Total: 16 45 7.8 45 57
Percentage of the requests served within a certain time (ms)
50% 45
66% 49
75% 51
80% 52
90% 54
95% 55
98% 55
99% 56
100% 57 (longest request)
观察可以发现QPS可以达到 Requests per second: 2084.98 [#/sec] (mean)。
HTTP SERVER 配置选项
swoole_server::set()用于设置swoole_server运行时的各项参数化。
$cfg = [];
// 处理请求的进程数量
$cfg["worker_num"] = 4;
// 守护进程化
$cfg["daemonize"] = 1;
// 设置工作进程的最大任务数量
$cfg["max_request"] = 0;
$cfg["backlog"] = 128;
$cfg["max_request"] = 50;
$cfg["dispatch_mode"] = 1;
$srv->set($cfg);
配置HTTP SERVER参数后测试并发
$ vim http_server.php
<?php
//创建HTTP服务器
$addr = "0.0.0.0";
$port = 9501;
$srv = new swoole_http_server($addr, $port);
//设置HTTP服务器参数
$cfg = [];
$cfg["worker_num"] = 4;//设置工作进程数量
$cfg["daemonize"] = 1;//守护进程化,程序转入后台。
$srv->set($cfg);
$srv->on("request", function(swoole_http_request $rq, swoole_http_response $rp){
//获取请求参数
$params = $rq->get;
echo "\nparams:".json_encode($params);
//处理动态请求
$path_info = $rq->server["path_info"];
$file = __DIR__.$path_info;
echo "\nfile:{$file}";
if(is_file($file) && file_exists($file)){
$ext = pathinfo($path_info, PATHINFO_EXTENSION);
echo "\next:{$ext}";
if($ext == "php"){
ob_start();
include($file);
$contents = ob_get_contents();
ob_end_clean();
}else{
$contents = file_get_contents($file);
}
echo "\ncontents:{$contents}";
$rp->end($contents);
}else{
$rp->status(404);
$rp->end("404 not found");
}
});
//启动服务
$srv->start();
查看进程
$ ps -ef|grep http_server.php
root 16224 1207 0 22:41 ? 00:00:00 php http_server.php
root 16225 16224 0 22:41 ? 00:00:00 php http_server.php
root 16227 16225 0 22:41 ? 00:00:00 php http_server.php
root 16228 16225 0 22:41 ? 00:00:00 php http_server.php
root 16229 16225 0 22:41 ? 00:00:00 php http_server.php
root 16230 16225 0 22:41 ? 00:00:00 php http_server.php
root 16233 2456 0 22:42 pts/0 00:00:00 grep --color=auto http_server.php
查看后台守护进程
$ ps axuf|grep http_server.php
root 16622 0.0 0.0 21536 1044 pts/0 S+ 22:46 0:00 | | \_ grep --color=auto http_server.php
root 16224 0.0 0.3 269036 8104 ? Ssl 22:41 0:00 \_ php http_server.php
root 16225 0.0 0.3 196756 8440 ? S 22:41 0:00 \_ php http_server.php
root 16227 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php
root 16228 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php
root 16229 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php
root 16230 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php
$ ps auxf|grep http_server.php|wc -l
7
杀死后台进程
# 强杀后台进程
$ kill -9 $(ps aux|grep swoole|grep -v grep|awk '{print $2}')
$ kill -9 16224
$ kill -9 16225
$ kill -9 16227
$ kill -9 16228
$ kill -9 16229
$ kill -9 16230
# 重启后台进程
$ kill -10 $(ps aux|grep http_server|grep -v grep|awk '{print $2}')
压测
$ ab -c 100 -n 1000 127.0.0.1:9501/index.php
Server Software: swoole-http-server
Server Hostname: 127.0.0.1
Server Port: 9501
Document Path: /index.php
Document Length: 9 bytes
Concurrency Level: 100
Time taken for tests: 0.226 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 156000 bytes
HTML transferred: 9000 bytes
Requests per second: 4417.72 [#/sec] (mean)
Time per request: 22.636 [ms] (mean)
Time per request: 0.226 [ms] (mean, across all concurrent requests)
Transfer rate: 673.01 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 2.8 0 11
Processing: 4 21 7.2 20 49
Waiting: 1 21 7.2 20 49
Total: 5 22 7.6 20 56
Percentage of the requests served within a certain time (ms)
50% 20
66% 23
75% 25
80% 26
90% 30
95% 38
98% 45
99% 53
100% 56 (longest request)
观察可以发现QPC为Requests per second: 4417.72 [#/sec] (mean)。
性能优化
使用swoole_http_server服务后,若发现服务的请求耗时监控毛刺十分严重,接口耗时波动较大的情况,可以观察下服务的响应包response的大小,若响应包超过1~2M甚至更大,则可判断是由于包太多而且很大导致服务响应波动较大。
为什么响应包惠导致相应的时间波动呢?主要有两个方面的影响,第一是响应包太大导致Swoole之间进程通信更加耗时并占用更多资源。第二是响应包太大导致Swoole的Reactor线程发包更加耗时。
以上就是一起看看Swoole HTTP的详细内容,更多请关注自由互联其它相关文章!
请求对象
swoole_input"),有时服务器不需要解析HTTP的POST请求参数。
Swoole1.7.18+版本增加了 bool Http\Response->write(string $data) 参数 function Http\Response->sendfile(
string $filename,
int $offset = 0,
int $length = 0
) $response->header("Content-Type", "image/jpeg");
$filepath = $request->server["request_uri"];
$filename = __DIR__.$filepath;
$response->sendfile($filename); 由于Swoole底层无法推断要发送文件的媒体类型 function Http\Response->end(string $html); function Http\Response->detach():bool 在某些情况下需要在 <?php
//创建HTTP服务器对象
$host = "0.0.0.0";
$port = 9501;
$server = new swoole_http_server($host, $port);
//设置服务器运行参数
$configs = [];
$configs["worker_num"] = 1;//设置Worker工作进程数量
$configs["task_worker_num"] = 1;//设置Task任务进程数量
$configs["daemonize"] = 0;//设置是否已后台守护进程运行
$server->set($configs);
//注册客户端请求处理回调函数
$server->on("request", function(swoole_http_request $request, swoole_http_response $response) use($server){
//分离响应对象
$response->detach();
//在Task任务进程中对客户端发出响应
$fd = strval($response->fd);
$server->task($fd);
});
//注册异步任务处理回调函数
$server->on("task", function(swoole_http_server $server, $worker_id, $data){
//创建响应对象
$response = swoole_http_response::create($data);
//向客户端发送响应
$html = "in task";
$response->end($html);
});
//注册Task异步任务执行完毕回调函数
$server->on("finish", function(){
echo "[finish] task".PHP_EOL;
});
//启动服务器
$server->start(); 在某些特殊场景下,需要对客户端发送特殊的响应内容, <?php
//创建HTTP服务器对象
$host = "0.0.0.0";
$port = 9501;
$server = new swoole_http_server($host, $port);
//设置服务器运行参数
$configs = [];
$configs["worker_num"] = 2;//设置Worker工作进程数量
$configs["daemonize"] = 0;//设置是否已后台守护进程运行
$server->set($configs);
//注册监听客户端HTTP请求回调事件
$server->on("request", function(swoole_http_request $request, swoole_http_response $response) use($server){
//分离响应对象
$response->detach();
//自行组包并使用Server::send方法发送数据
$fd = $response->fd;
$message = "HTTP/1.1 200 OK\r\n";
$message .= "Server: server\r\n";
$message .= "\r\n";
$message .= "Hello World\n";
$server->send($fd, $message);
});
//启动服务器
$server->start(); Http\Response::create function Http\Response::createE(int $fd) : Http\Response 注册事件回调函数 onRequest 事件 $server->on("request", function(swoole_http_request $request, swoole_http_response $response) use($server){
$html = "success";
$response->end($html);
}); 在 案例www.baidu.com";
$response->redirect($url, 301);
});
$server->start();Http\Response->write
write方法用于启用HTTP的chunk分段以向浏览器发送相应的内容,使用write分段发送数据后end方法将不再接收任何参数,调用end方法后会发送一个长度为0的分段chunk表示数据传输完毕。$data表示要发送的数据内容,最大长度不得超过2MB,受buffer_output_size配置项控制。Http\Response->sendfile
sendfile用于发送文件到浏览器string $filename 表示要发送的文件名称,文件不存在或没有访问权限则会发送失败。int $offset 表示上传文件的偏移量,可以指定从文件在中间部分开始传输数据,用于断点续传,适用于Swoole1.9.11+。int $length 表示发送数据的尺寸,默认为整个文件的尺寸,适用于Swoole1.9.11+。MIME格式,因此需要应用程序指定Content-Type。调用sendfile前不得使用write方法发送HTTP数据段Chunk,调用sendfile后Swoole底层会自动执行end方法,另外sendfile不支持gzip压缩。Http\Response->end
end方法用于发送HTTP响应体,并结束请求处理。end方法只能调用一次,如果需要分多次向客户端发送数据下需使用write方法,send操作后将会向客户端浏览器发送HTML内容。如果客户端开启了KeepAlive连接会保持,服务器会等待下一次请求。如果没有开启KeepAlive服务器将会切断连接。Http\Response->detach
detach表示分离响应对应,调用后$response对象销毁时将不会自动执行end方法,一般detach会与Http\Response::create以及Server::send配合使用,适用于Swoole2.2.0+版本。detach方法操作后,若客户端已经完成响应则会返回true,否则返回false。detach应用于跨进程响应Task任务进程中对客户端发出响应,此时可以利用detach方法使$response对象独立,如此一来在Task任务进程中就可以重新构建$response对象以发起HTTP请求响应。detach方法应用于发送任意内容Http\Response对象自带的end方法无法满足需求,可以使用detach方法分离响应对象,然后自行组包并使用Server::send方法发送数据。create静态方法用于构造新的Http\Response响应对象,使用前必须调用detach方法将旧有$response对象分离,否则 可能会造成同一个请求发送两次响应内容。create静态方法的参数$fd表示需要绑定连接的文件描述符,调用Http\Response对象的end方法和write方法时会向此连接发送数据。如果调用成功则返回一个新的Http\Response对象,否则失败返回false,适用于Swoole2.2.0+版本。Http\Server注册事件回调函数于Http\Server->on相同,不同之处在于HTTP\Server->on不接受onConnect和onReceive回调设置,Http\Server->on会额外接受一种新的事务类型onRequest。onRequest事件适用于Swoole1.7.7+版本,当服务器收到一个完整的HTTP请求后会调用onRequest函数。onRequest回调函数共有两个参数swoole_http_requst $request HTTP请求信息对象,包含了Header/GET/POST/Cookie等信息。swoole_http_response $response HTTP响应信息对象,支持Cookie/Header/Status等HTTP操作。onRequest回调函数返回时会销毁$request和$response对象,如果未执行$response->end()操作,Swoole底层会自动执行一次$response->end("")。$request和$response对象在传递给其它函数时,是不需要添加&取地址的引用符号的,传递后引用计数会增加,当onRequest退出时并不会被销毁。
$ vim http_server.php
<?php $addr = "0.0.0.0"; $port = 9501; $svr = new swoole_http_server($addr, $port); $svr->on("request", function(swoole_http_request $rq, swoole_http_response $rp){ //处理动态请求 $path_info = $rq->server["path_info"]; $file = __DIR__.$path_info; echo "\nfile:{$file}"; if(is_file($file) && file_exists($file)){ $ext = pathinfo($path_info, PATHINFO_EXTENSION); echo "\next:{$ext}"; if($ext == "php"){ ob_start(); include($file); $contents = ob_get_contents(); ob_end_clean(); }else{ $contents = file_get_contents($file); } echo "\ncontents:{$contents}"; $rp->end($contents); }else{ $rp->status(404); $rp->end("404 not found"); } }); $svr->start();
# 创建静态文件 $ vim index.html index.html # 测试静态文件 $ curl 127.0.0.1:9501/index.html # 观察http_server输出 file:/home/jc/projects/swoole/chat/index.html ext:html contents:index.html # 测试动态文件 $ vim index.php <?php echo "index.php"; #观察http_server日志输出 file:/home/jc/projects/swoole/chat/index.php ext:php contents:index.php
获取动态请求的参数
$ vim http_server.php
<?php $addr = "0.0.0.0"; $port = 9501; $svr = new swoole_http_server($addr, $port); $svr->on("request", function(swoole_http_request $rq, swoole_http_response $rp){ //获取请求参数 $params = $rq->get; echo "\nparams:".json_encode($params); //处理动态请求 $path_info = $rq->server["path_info"]; $file = __DIR__.$path_info; echo "\nfile:{$file}"; if(is_file($file) && file_exists($file)){ $ext = pathinfo($path_info, PATHINFO_EXTENSION); echo "\next:{$ext}"; if($ext == "php"){ ob_start(); include($file); $contents = ob_get_contents(); ob_end_clean(); }else{ $contents = file_get_contents($file); } echo "\ncontents:{$contents}"; $rp->end($contents); }else{ $rp->status(404); $rp->end("404 not found"); } }); $svr->start();
测试带参数的请求
$ curl 127.0.0.1:9501?k=v
观察请求参数的输出
params:{"k":"v"} file:/home/jc/projects/swoole/chat/index.html ext:html contents:index.html
静态文件处理
$ vim mimes.php
<?php return [ "jpg"=>"image/jpeg", "jpeg"=>"image/jpeg", "bmp"=>"image/bmp", "ico"=>"image/x-icon", "gif"=>"image/gif", "png"=>"image/png", "css"=>"text/css", "html"=>"text/html", "xml"=>"text/xml", "bin"=>"application/octet-stream", "js"=>"application/javascript", "tar"=>"application/x-tar", "ppt"=>"application/vnd.ms-powerpoint", "pdf"=>"application/pdf", "swf"=>"application/x-shockwave-flash", "zip"=>"application/x-zip-compressed" ];
$ vim http_server.php
<?php //创建HTTP服务器 $addr = "0.0.0.0"; $port = 9501; $srv = new swoole_http_server($addr, $port); //设置HTTP服务器参数 $cfg = []; $cfg["worker_num"] = 4;//设置工作进程数量 $cfg["daemonize"] = 0;//守护进程化,程序转入后台。 $srv->set($cfg); //处理请求 $srv->on("request", function(swoole_http_request $rq, swoole_http_response $rp) use($srv){ //获取请求文件信息与文件后缀 $path_info = $rq->server["path_info"]; $ext = pathinfo($path_info, PATHINFO_EXTENSION); //文件是否存在 $file = __DIR__.$path_info; if(!is_file($file) || !file_exists($file)){ $rp->status(404); $rp->end("404 NOT FOUND"); } //处理静态请求 if($ext != "php"){ //设置响应头信息的内容内容 $mimes = include("mimes.php"); $rp->header("Content-Type", $mimes[$ext]); //获取静态文件内容 $contents = file_get_contents($file); //返回内容 $rp->end($contents); } }); //启动服务 $srv->start();
发送请求,浏览器访问127.0.0.1:9501/test.jpeg,查看图片。
面向对象
$ vim http_server.php
<?php class HttpServer { public static function run($host, $port, $options=[]) { $srv = new swoole_http_server($host, $port); if(!empty($options)){ $srv->set($options); } $srv->on("request", function(swoole_http_request $rq, swoole_http_response $rp) use($srv){ $rp->end("test"); $srv->close($rq->fd); }); $srv->start(); } } HttpServer::run("127.0.0.1", 9501, ["worker_num"=>2, "daemonize"=>0]);
压力测试
使用Apache Bench工具进行压力测试可以发现,swoole_http_server远超过PHP-FPM、Golang自带的HTTP服务器、Node.js自带的HTTP服务器,性能接近Nginx的静态文件处理。
Swoole的http server与PHP-FPM的性能对比
安装Apache的压测工作ab
$ sudo apt install apache2-util
使用100个客户端跑1000次,平均每个客户端10个请求。
$ ab -c 100 -n 1000 127.0.0.1:9501/index.php Concurrency Level: 100 Time taken for tests: 0.480 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 156000 bytes HTML transferred: 9000 bytes Requests per second: 2084.98 [#/sec] (mean) Time per request: 47.962 [ms] (mean) Time per request: 0.480 [ms] (mean, across all concurrent requests) Transfer rate: 317.63 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 3.0 0 12 Processing: 4 44 10.0 45 57 Waiting: 4 44 10.1 45 57 Total: 16 45 7.8 45 57 Percentage of the requests served within a certain time (ms) 50% 45 66% 49 75% 51 80% 52 90% 54 95% 55 98% 55 99% 56 100% 57 (longest request)
观察可以发现QPS可以达到 Requests per second: 2084.98 [#/sec] (mean)。
HTTP SERVER 配置选项
swoole_server::set()用于设置swoole_server运行时的各项参数化。
$cfg = []; // 处理请求的进程数量 $cfg["worker_num"] = 4; // 守护进程化 $cfg["daemonize"] = 1; // 设置工作进程的最大任务数量 $cfg["max_request"] = 0; $cfg["backlog"] = 128; $cfg["max_request"] = 50; $cfg["dispatch_mode"] = 1; $srv->set($cfg);
配置HTTP SERVER参数后测试并发
$ vim http_server.php
<?php //创建HTTP服务器 $addr = "0.0.0.0"; $port = 9501; $srv = new swoole_http_server($addr, $port); //设置HTTP服务器参数 $cfg = []; $cfg["worker_num"] = 4;//设置工作进程数量 $cfg["daemonize"] = 1;//守护进程化,程序转入后台。 $srv->set($cfg); $srv->on("request", function(swoole_http_request $rq, swoole_http_response $rp){ //获取请求参数 $params = $rq->get; echo "\nparams:".json_encode($params); //处理动态请求 $path_info = $rq->server["path_info"]; $file = __DIR__.$path_info; echo "\nfile:{$file}"; if(is_file($file) && file_exists($file)){ $ext = pathinfo($path_info, PATHINFO_EXTENSION); echo "\next:{$ext}"; if($ext == "php"){ ob_start(); include($file); $contents = ob_get_contents(); ob_end_clean(); }else{ $contents = file_get_contents($file); } echo "\ncontents:{$contents}"; $rp->end($contents); }else{ $rp->status(404); $rp->end("404 not found"); } }); //启动服务 $srv->start();
查看进程
$ ps -ef|grep http_server.php root 16224 1207 0 22:41 ? 00:00:00 php http_server.php root 16225 16224 0 22:41 ? 00:00:00 php http_server.php root 16227 16225 0 22:41 ? 00:00:00 php http_server.php root 16228 16225 0 22:41 ? 00:00:00 php http_server.php root 16229 16225 0 22:41 ? 00:00:00 php http_server.php root 16230 16225 0 22:41 ? 00:00:00 php http_server.php root 16233 2456 0 22:42 pts/0 00:00:00 grep --color=auto http_server.php
查看后台守护进程
$ ps axuf|grep http_server.php root 16622 0.0 0.0 21536 1044 pts/0 S+ 22:46 0:00 | | \_ grep --color=auto http_server.php root 16224 0.0 0.3 269036 8104 ? Ssl 22:41 0:00 \_ php http_server.php root 16225 0.0 0.3 196756 8440 ? S 22:41 0:00 \_ php http_server.php root 16227 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php root 16228 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php root 16229 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php root 16230 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php $ ps auxf|grep http_server.php|wc -l 7
杀死后台进程
# 强杀后台进程 $ kill -9 $(ps aux|grep swoole|grep -v grep|awk '{print $2}') $ kill -9 16224 $ kill -9 16225 $ kill -9 16227 $ kill -9 16228 $ kill -9 16229 $ kill -9 16230 # 重启后台进程 $ kill -10 $(ps aux|grep http_server|grep -v grep|awk '{print $2}')
压测
$ ab -c 100 -n 1000 127.0.0.1:9501/index.php Server Software: swoole-http-server Server Hostname: 127.0.0.1 Server Port: 9501 Document Path: /index.php Document Length: 9 bytes Concurrency Level: 100 Time taken for tests: 0.226 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 156000 bytes HTML transferred: 9000 bytes Requests per second: 4417.72 [#/sec] (mean) Time per request: 22.636 [ms] (mean) Time per request: 0.226 [ms] (mean, across all concurrent requests) Transfer rate: 673.01 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 2.8 0 11 Processing: 4 21 7.2 20 49 Waiting: 1 21 7.2 20 49 Total: 5 22 7.6 20 56 Percentage of the requests served within a certain time (ms) 50% 20 66% 23 75% 25 80% 26 90% 30 95% 38 98% 45 99% 53 100% 56 (longest request)
观察可以发现QPC为Requests per second: 4417.72 [#/sec] (mean)。
性能优化
使用swoole_http_server服务后,若发现服务的请求耗时监控毛刺十分严重,接口耗时波动较大的情况,可以观察下服务的响应包response的大小,若响应包超过1~2M甚至更大,则可判断是由于包太多而且很大导致服务响应波动较大。
为什么响应包惠导致相应的时间波动呢?主要有两个方面的影响,第一是响应包太大导致Swoole之间进程通信更加耗时并占用更多资源。第二是响应包太大导致Swoole的Reactor线程发包更加耗时。
以上就是一起看看Swoole HTTP的详细内容,更多请关注自由互联其它相关文章!
本文共计4324个文字,预计阅读时间需要18分钟。
目标:了解Swoole的HTTP服务器使用、TCP服务开发,以及实际项目中遇到的问题如粘包处理、代理热更新、用户验证等。Swoole与现有框架结合:风格、偏基础重写、环境、PHP版本:Swoole版本:H
目标
- 了解swoole的github.com/swoole/swoole-src
- zphp开发框架:github.com/shenzhe/zphp
HTTP Server
- 静态文件处理
- 动态请求与框架结合
# 查看SWOOLE版本 $ php -r 'echo SWOOLE_VERSION;' 4.3.1
推荐(免费):swoole
基础概念
HTTP报文
关于HTTP请求报文的组成结构
HTTP请求报文结构
POST /search HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-silverlight, application/x-shockwave-flash, */* Referer: www.google.cn/ Accept-Language: zh-cn Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; TheWorld) Host: www.google.cn Connection: Keep-Alive Cookie: PREF=ID=80a06da87be9ae3c:U=f7167333e2c3b714:NW=1:TM=1261551909:LM=1261551917:S=ybYcq2wpfefs4V9g; NID=31=ojj8d-IygaEtSxLgaJmqSjVhCspkviJrB6omjamNrSm8lZhKy_yMfO2M4QMRKcH1g0iQv9u-2hfBW7bUFwVh7pGaRUb0RnHcJU37y- FxlRugatx63JLv7CWMD6UB_O_r hl=zh-CN&source=hp&q=domety
关于HTTP响应报文的组成结构
HTTP响应报文结构
HTTP/1.1 200 OK Date: Mon, 23 May 2005 22:38:34 GMT Content-Type: text/html; charset=UTF-8 Content-Encoding: UTF-8 Content-Length: 138 Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux) ETag: "3f80f-1b6-3e1cb03b" Accept-Ranges: bytes Connection: close
创建HTTP服务器
Swoole在1.7.7版本后内置HTTP服务器,可创建一个异步非阻塞多进程的HTTP服务器。Swoole的HTTP服务器对HTTP协议支持的并不完整,建议仅作为应用服务器,并在前端增加Nginx作为代理。
因为Swoole是在CLI命令行中执行的,在传统的NGINX+FastCGI模式下很多root的shell是无法执行的,而使用Swoole服务器就能很好的控制rsync、git、svn等。
使用Swoole的API,构建HTTP服务器需要4个步骤
- 创建Server对象
- 设置运行时参数
- 注册事件回调函数
- 启动服务器
# 创建应用 $ mkdir test && cd test # 创建并编辑服务器文件 $ vim server.php
<?php //创建HTTP服务器对象 $host = "0.0.0.0"; $port = 9501; $server = new swoole_img.558idc.com/uploadfile/"; $server->set($configs);
POST解析swoole;
proxy_set_header Connection "";
proxy_127.0.0.1:9501;
}
}
}请求对象
swoole_input"),有时服务器不需要解析HTTP的POST请求参数。
Swoole1.7.18+版本增加了www.baidu.com";
$response->redirect($url, 301);
});
$server->start();Http\Response->write
write方法用于启用HTTP的chunk分段以向浏览器发送相应的内容,使用write分段发送数据后end方法将不再接收任何参数,调用end方法后会发送一个长度为0的分段chunk表示数据传输完毕。
bool Http\Response->write(string $data)
参数$data表示要发送的数据内容,最大长度不得超过2MB,受buffer_output_size配置项控制。
Http\Response->sendfile
sendfile用于发送文件到浏览器
function Http\Response->sendfile(
string $filename,
int $offset = 0,
int $length = 0
)
string $filename 表示要发送的文件名称,文件不存在或没有访问权限则会发送失败。int $offset 表示上传文件的偏移量,可以指定从文件在中间部分开始传输数据,用于断点续传,适用于Swoole1.9.11+。int $length 表示发送数据的尺寸,默认为整个文件的尺寸,适用于Swoole1.9.11+。
$response->header("Content-Type", "image/jpeg");
$filepath = $request->server["request_uri"];
$filename = __DIR__.$filepath;
$response->sendfile($filename);
由于Swoole底层无法推断要发送文件的媒体类型MIME格式,因此需要应用程序指定Content-Type。调用sendfile前不得使用write方法发送HTTP数据段Chunk,调用sendfile后Swoole底层会自动执行end方法,另外sendfile不支持gzip压缩。
Http\Response->end
end方法用于发送HTTP响应体,并结束请求处理。
function Http\Response->end(string $html);
end方法只能调用一次,如果需要分多次向客户端发送数据下需使用write方法,send操作后将会向客户端浏览器发送HTML内容。如果客户端开启了KeepAlive连接会保持,服务器会等待下一次请求。如果没有开启KeepAlive服务器将会切断连接。
Http\Response->detach
detach表示分离响应对应,调用后$response对象销毁时将不会自动执行end方法,一般detach会与Http\Response::create以及Server::send配合使用,适用于Swoole2.2.0+版本。
function Http\Response->detach():bool
detach方法操作后,若客户端已经完成响应则会返回true,否则返回false。
detach应用于跨进程响应
在某些情况下需要在Task任务进程中对客户端发出响应,此时可以利用detach方法使$response对象独立,如此一来在Task任务进程中就可以重新构建$response对象以发起HTTP请求响应。
<?php
//创建HTTP服务器对象
$host = "0.0.0.0";
$port = 9501;
$server = new swoole_http_server($host, $port);
//设置服务器运行参数
$configs = [];
$configs["worker_num"] = 1;//设置Worker工作进程数量
$configs["task_worker_num"] = 1;//设置Task任务进程数量
$configs["daemonize"] = 0;//设置是否已后台守护进程运行
$server->set($configs);
//注册客户端请求处理回调函数
$server->on("request", function(swoole_http_request $request, swoole_http_response $response) use($server){
//分离响应对象
$response->detach();
//在Task任务进程中对客户端发出响应
$fd = strval($response->fd);
$server->task($fd);
});
//注册异步任务处理回调函数
$server->on("task", function(swoole_http_server $server, $worker_id, $data){
//创建响应对象
$response = swoole_http_response::create($data);
//向客户端发送响应
$html = "in task";
$response->end($html);
});
//注册Task异步任务执行完毕回调函数
$server->on("finish", function(){
echo "[finish] task".PHP_EOL;
});
//启动服务器
$server->start();
detach方法应用于发送任意内容
在某些特殊场景下,需要对客户端发送特殊的响应内容,Http\Response对象自带的end方法无法满足需求,可以使用detach方法分离响应对象,然后自行组包并使用Server::send方法发送数据。
<?php
//创建HTTP服务器对象
$host = "0.0.0.0";
$port = 9501;
$server = new swoole_http_server($host, $port);
//设置服务器运行参数
$configs = [];
$configs["worker_num"] = 2;//设置Worker工作进程数量
$configs["daemonize"] = 0;//设置是否已后台守护进程运行
$server->set($configs);
//注册监听客户端HTTP请求回调事件
$server->on("request", function(swoole_http_request $request, swoole_http_response $response) use($server){
//分离响应对象
$response->detach();
//自行组包并使用Server::send方法发送数据
$fd = $response->fd;
$message = "HTTP/1.1 200 OK\r\n";
$message .= "Server: server\r\n";
$message .= "\r\n";
$message .= "Hello World\n";
$server->send($fd, $message);
});
//启动服务器
$server->start();
Http\Response::create
create静态方法用于构造新的Http\Response响应对象,使用前必须调用detach方法将旧有$response对象分离,否则 可能会造成同一个请求发送两次响应内容。
function Http\Response::createE(int $fd) : Http\Response
create静态方法的参数$fd表示需要绑定连接的文件描述符,调用Http\Response对象的end方法和write方法时会向此连接发送数据。如果调用成功则返回一个新的Http\Response对象,否则失败返回false,适用于Swoole2.2.0+版本。
注册事件回调函数
Http\Server注册事件回调函数于Http\Server->on相同,不同之处在于HTTP\Server->on不接受onConnect和onReceive回调设置,Http\Server->on会额外接受一种新的事务类型onRequest。
onRequest 事件
onRequest事件适用于Swoole1.7.7+版本,当服务器收到一个完整的HTTP请求后会调用onRequest函数。
$server->on("request", function(swoole_http_request $request, swoole_http_response $response) use($server){
$html = "success";
$response->end($html);
});
onRequest回调函数共有两个参数
swoole_http_requst $request HTTP请求信息对象,包含了Header/GET/POST/Cookie等信息。swoole_http_response $response HTTP响应信息对象,支持Cookie/Header/Status等HTTP操作。
在onRequest回调函数返回时会销毁$request和$response对象,如果未执行$response->end()操作,Swoole底层会自动执行一次$response->end("")。
$request和$response对象在传递给其它函数时,是不需要添加&取地址的引用符号的,传递后引用计数会增加,当onRequest退出时并不会被销毁。
案例

$ vim http_server.php
<?php
$addr = "0.0.0.0";
$port = 9501;
$svr = new swoole_http_server($addr, $port);
$svr->on("request", function(swoole_http_request $rq, swoole_http_response $rp){
//处理动态请求
$path_info = $rq->server["path_info"];
$file = __DIR__.$path_info;
echo "\nfile:{$file}";
if(is_file($file) && file_exists($file)){
$ext = pathinfo($path_info, PATHINFO_EXTENSION);
echo "\next:{$ext}";
if($ext == "php"){
ob_start();
include($file);
$contents = ob_get_contents();
ob_end_clean();
}else{
$contents = file_get_contents($file);
}
echo "\ncontents:{$contents}";
$rp->end($contents);
}else{
$rp->status(404);
$rp->end("404 not found");
}
});
$svr->start();
# 创建静态文件
$ vim index.html
index.html
# 测试静态文件
$ curl 127.0.0.1:9501/index.html
# 观察http_server输出
file:/home/jc/projects/swoole/chat/index.html
ext:html
contents:index.html
# 测试动态文件
$ vim index.php
<?php
echo "index.php";
#观察http_server日志输出
file:/home/jc/projects/swoole/chat/index.php
ext:php
contents:index.php
获取动态请求的参数
$ vim http_server.php
<?php
$addr = "0.0.0.0";
$port = 9501;
$svr = new swoole_http_server($addr, $port);
$svr->on("request", function(swoole_http_request $rq, swoole_http_response $rp){
//获取请求参数
$params = $rq->get;
echo "\nparams:".json_encode($params);
//处理动态请求
$path_info = $rq->server["path_info"];
$file = __DIR__.$path_info;
echo "\nfile:{$file}";
if(is_file($file) && file_exists($file)){
$ext = pathinfo($path_info, PATHINFO_EXTENSION);
echo "\next:{$ext}";
if($ext == "php"){
ob_start();
include($file);
$contents = ob_get_contents();
ob_end_clean();
}else{
$contents = file_get_contents($file);
}
echo "\ncontents:{$contents}";
$rp->end($contents);
}else{
$rp->status(404);
$rp->end("404 not found");
}
});
$svr->start();
测试带参数的请求
$ curl 127.0.0.1:9501?k=v
观察请求参数的输出
params:{"k":"v"}
file:/home/jc/projects/swoole/chat/index.html
ext:html
contents:index.html
静态文件处理
$ vim mimes.php
<?php
return [
"jpg"=>"image/jpeg",
"jpeg"=>"image/jpeg",
"bmp"=>"image/bmp",
"ico"=>"image/x-icon",
"gif"=>"image/gif",
"png"=>"image/png",
"css"=>"text/css",
"html"=>"text/html",
"xml"=>"text/xml",
"bin"=>"application/octet-stream",
"js"=>"application/javascript",
"tar"=>"application/x-tar",
"ppt"=>"application/vnd.ms-powerpoint",
"pdf"=>"application/pdf",
"swf"=>"application/x-shockwave-flash",
"zip"=>"application/x-zip-compressed"
];
$ vim http_server.php
<?php
//创建HTTP服务器
$addr = "0.0.0.0";
$port = 9501;
$srv = new swoole_http_server($addr, $port);
//设置HTTP服务器参数
$cfg = [];
$cfg["worker_num"] = 4;//设置工作进程数量
$cfg["daemonize"] = 0;//守护进程化,程序转入后台。
$srv->set($cfg);
//处理请求
$srv->on("request", function(swoole_http_request $rq, swoole_http_response $rp) use($srv){
//获取请求文件信息与文件后缀
$path_info = $rq->server["path_info"];
$ext = pathinfo($path_info, PATHINFO_EXTENSION);
//文件是否存在
$file = __DIR__.$path_info;
if(!is_file($file) || !file_exists($file)){
$rp->status(404);
$rp->end("404 NOT FOUND");
}
//处理静态请求
if($ext != "php"){
//设置响应头信息的内容内容
$mimes = include("mimes.php");
$rp->header("Content-Type", $mimes[$ext]);
//获取静态文件内容
$contents = file_get_contents($file);
//返回内容
$rp->end($contents);
}
});
//启动服务
$srv->start();
发送请求,浏览器访问127.0.0.1:9501/test.jpeg,查看图片。
面向对象
$ vim http_server.php
<?php
class HttpServer
{
public static function run($host, $port, $options=[])
{
$srv = new swoole_http_server($host, $port);
if(!empty($options)){
$srv->set($options);
}
$srv->on("request", function(swoole_http_request $rq, swoole_http_response $rp) use($srv){
$rp->end("test");
$srv->close($rq->fd);
});
$srv->start();
}
}
HttpServer::run("127.0.0.1", 9501, ["worker_num"=>2, "daemonize"=>0]);
压力测试
使用Apache Bench工具进行压力测试可以发现,swoole_http_server远超过PHP-FPM、Golang自带的HTTP服务器、Node.js自带的HTTP服务器,性能接近Nginx的静态文件处理。
Swoole的http server与PHP-FPM的性能对比
安装Apache的压测工作ab
$ sudo apt install apache2-util
使用100个客户端跑1000次,平均每个客户端10个请求。
$ ab -c 100 -n 1000 127.0.0.1:9501/index.php
Concurrency Level: 100
Time taken for tests: 0.480 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 156000 bytes
HTML transferred: 9000 bytes
Requests per second: 2084.98 [#/sec] (mean)
Time per request: 47.962 [ms] (mean)
Time per request: 0.480 [ms] (mean, across all concurrent requests)
Transfer rate: 317.63 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 3.0 0 12
Processing: 4 44 10.0 45 57
Waiting: 4 44 10.1 45 57
Total: 16 45 7.8 45 57
Percentage of the requests served within a certain time (ms)
50% 45
66% 49
75% 51
80% 52
90% 54
95% 55
98% 55
99% 56
100% 57 (longest request)
观察可以发现QPS可以达到 Requests per second: 2084.98 [#/sec] (mean)。
HTTP SERVER 配置选项
swoole_server::set()用于设置swoole_server运行时的各项参数化。
$cfg = [];
// 处理请求的进程数量
$cfg["worker_num"] = 4;
// 守护进程化
$cfg["daemonize"] = 1;
// 设置工作进程的最大任务数量
$cfg["max_request"] = 0;
$cfg["backlog"] = 128;
$cfg["max_request"] = 50;
$cfg["dispatch_mode"] = 1;
$srv->set($cfg);
配置HTTP SERVER参数后测试并发
$ vim http_server.php
<?php
//创建HTTP服务器
$addr = "0.0.0.0";
$port = 9501;
$srv = new swoole_http_server($addr, $port);
//设置HTTP服务器参数
$cfg = [];
$cfg["worker_num"] = 4;//设置工作进程数量
$cfg["daemonize"] = 1;//守护进程化,程序转入后台。
$srv->set($cfg);
$srv->on("request", function(swoole_http_request $rq, swoole_http_response $rp){
//获取请求参数
$params = $rq->get;
echo "\nparams:".json_encode($params);
//处理动态请求
$path_info = $rq->server["path_info"];
$file = __DIR__.$path_info;
echo "\nfile:{$file}";
if(is_file($file) && file_exists($file)){
$ext = pathinfo($path_info, PATHINFO_EXTENSION);
echo "\next:{$ext}";
if($ext == "php"){
ob_start();
include($file);
$contents = ob_get_contents();
ob_end_clean();
}else{
$contents = file_get_contents($file);
}
echo "\ncontents:{$contents}";
$rp->end($contents);
}else{
$rp->status(404);
$rp->end("404 not found");
}
});
//启动服务
$srv->start();
查看进程
$ ps -ef|grep http_server.php
root 16224 1207 0 22:41 ? 00:00:00 php http_server.php
root 16225 16224 0 22:41 ? 00:00:00 php http_server.php
root 16227 16225 0 22:41 ? 00:00:00 php http_server.php
root 16228 16225 0 22:41 ? 00:00:00 php http_server.php
root 16229 16225 0 22:41 ? 00:00:00 php http_server.php
root 16230 16225 0 22:41 ? 00:00:00 php http_server.php
root 16233 2456 0 22:42 pts/0 00:00:00 grep --color=auto http_server.php
查看后台守护进程
$ ps axuf|grep http_server.php
root 16622 0.0 0.0 21536 1044 pts/0 S+ 22:46 0:00 | | \_ grep --color=auto http_server.php
root 16224 0.0 0.3 269036 8104 ? Ssl 22:41 0:00 \_ php http_server.php
root 16225 0.0 0.3 196756 8440 ? S 22:41 0:00 \_ php http_server.php
root 16227 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php
root 16228 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php
root 16229 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php
root 16230 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php
$ ps auxf|grep http_server.php|wc -l
7
杀死后台进程
# 强杀后台进程
$ kill -9 $(ps aux|grep swoole|grep -v grep|awk '{print $2}')
$ kill -9 16224
$ kill -9 16225
$ kill -9 16227
$ kill -9 16228
$ kill -9 16229
$ kill -9 16230
# 重启后台进程
$ kill -10 $(ps aux|grep http_server|grep -v grep|awk '{print $2}')
压测
$ ab -c 100 -n 1000 127.0.0.1:9501/index.php
Server Software: swoole-http-server
Server Hostname: 127.0.0.1
Server Port: 9501
Document Path: /index.php
Document Length: 9 bytes
Concurrency Level: 100
Time taken for tests: 0.226 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 156000 bytes
HTML transferred: 9000 bytes
Requests per second: 4417.72 [#/sec] (mean)
Time per request: 22.636 [ms] (mean)
Time per request: 0.226 [ms] (mean, across all concurrent requests)
Transfer rate: 673.01 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 2.8 0 11
Processing: 4 21 7.2 20 49
Waiting: 1 21 7.2 20 49
Total: 5 22 7.6 20 56
Percentage of the requests served within a certain time (ms)
50% 20
66% 23
75% 25
80% 26
90% 30
95% 38
98% 45
99% 53
100% 56 (longest request)
观察可以发现QPC为Requests per second: 4417.72 [#/sec] (mean)。
性能优化
使用swoole_http_server服务后,若发现服务的请求耗时监控毛刺十分严重,接口耗时波动较大的情况,可以观察下服务的响应包response的大小,若响应包超过1~2M甚至更大,则可判断是由于包太多而且很大导致服务响应波动较大。
为什么响应包惠导致相应的时间波动呢?主要有两个方面的影响,第一是响应包太大导致Swoole之间进程通信更加耗时并占用更多资源。第二是响应包太大导致Swoole的Reactor线程发包更加耗时。
以上就是一起看看Swoole HTTP的详细内容,更多请关注自由互联其它相关文章!
请求对象
swoole_input"),有时服务器不需要解析HTTP的POST请求参数。
Swoole1.7.18+版本增加了 bool Http\Response->write(string $data) 参数 function Http\Response->sendfile(
string $filename,
int $offset = 0,
int $length = 0
) $response->header("Content-Type", "image/jpeg");
$filepath = $request->server["request_uri"];
$filename = __DIR__.$filepath;
$response->sendfile($filename); 由于Swoole底层无法推断要发送文件的媒体类型 function Http\Response->end(string $html); function Http\Response->detach():bool 在某些情况下需要在 <?php
//创建HTTP服务器对象
$host = "0.0.0.0";
$port = 9501;
$server = new swoole_http_server($host, $port);
//设置服务器运行参数
$configs = [];
$configs["worker_num"] = 1;//设置Worker工作进程数量
$configs["task_worker_num"] = 1;//设置Task任务进程数量
$configs["daemonize"] = 0;//设置是否已后台守护进程运行
$server->set($configs);
//注册客户端请求处理回调函数
$server->on("request", function(swoole_http_request $request, swoole_http_response $response) use($server){
//分离响应对象
$response->detach();
//在Task任务进程中对客户端发出响应
$fd = strval($response->fd);
$server->task($fd);
});
//注册异步任务处理回调函数
$server->on("task", function(swoole_http_server $server, $worker_id, $data){
//创建响应对象
$response = swoole_http_response::create($data);
//向客户端发送响应
$html = "in task";
$response->end($html);
});
//注册Task异步任务执行完毕回调函数
$server->on("finish", function(){
echo "[finish] task".PHP_EOL;
});
//启动服务器
$server->start(); 在某些特殊场景下,需要对客户端发送特殊的响应内容, <?php
//创建HTTP服务器对象
$host = "0.0.0.0";
$port = 9501;
$server = new swoole_http_server($host, $port);
//设置服务器运行参数
$configs = [];
$configs["worker_num"] = 2;//设置Worker工作进程数量
$configs["daemonize"] = 0;//设置是否已后台守护进程运行
$server->set($configs);
//注册监听客户端HTTP请求回调事件
$server->on("request", function(swoole_http_request $request, swoole_http_response $response) use($server){
//分离响应对象
$response->detach();
//自行组包并使用Server::send方法发送数据
$fd = $response->fd;
$message = "HTTP/1.1 200 OK\r\n";
$message .= "Server: server\r\n";
$message .= "\r\n";
$message .= "Hello World\n";
$server->send($fd, $message);
});
//启动服务器
$server->start(); Http\Response::create function Http\Response::createE(int $fd) : Http\Response 注册事件回调函数 onRequest 事件 $server->on("request", function(swoole_http_request $request, swoole_http_response $response) use($server){
$html = "success";
$response->end($html);
}); 在 案例www.baidu.com";
$response->redirect($url, 301);
});
$server->start();Http\Response->write
write方法用于启用HTTP的chunk分段以向浏览器发送相应的内容,使用write分段发送数据后end方法将不再接收任何参数,调用end方法后会发送一个长度为0的分段chunk表示数据传输完毕。$data表示要发送的数据内容,最大长度不得超过2MB,受buffer_output_size配置项控制。Http\Response->sendfile
sendfile用于发送文件到浏览器string $filename 表示要发送的文件名称,文件不存在或没有访问权限则会发送失败。int $offset 表示上传文件的偏移量,可以指定从文件在中间部分开始传输数据,用于断点续传,适用于Swoole1.9.11+。int $length 表示发送数据的尺寸,默认为整个文件的尺寸,适用于Swoole1.9.11+。MIME格式,因此需要应用程序指定Content-Type。调用sendfile前不得使用write方法发送HTTP数据段Chunk,调用sendfile后Swoole底层会自动执行end方法,另外sendfile不支持gzip压缩。Http\Response->end
end方法用于发送HTTP响应体,并结束请求处理。end方法只能调用一次,如果需要分多次向客户端发送数据下需使用write方法,send操作后将会向客户端浏览器发送HTML内容。如果客户端开启了KeepAlive连接会保持,服务器会等待下一次请求。如果没有开启KeepAlive服务器将会切断连接。Http\Response->detach
detach表示分离响应对应,调用后$response对象销毁时将不会自动执行end方法,一般detach会与Http\Response::create以及Server::send配合使用,适用于Swoole2.2.0+版本。detach方法操作后,若客户端已经完成响应则会返回true,否则返回false。detach应用于跨进程响应Task任务进程中对客户端发出响应,此时可以利用detach方法使$response对象独立,如此一来在Task任务进程中就可以重新构建$response对象以发起HTTP请求响应。detach方法应用于发送任意内容Http\Response对象自带的end方法无法满足需求,可以使用detach方法分离响应对象,然后自行组包并使用Server::send方法发送数据。create静态方法用于构造新的Http\Response响应对象,使用前必须调用detach方法将旧有$response对象分离,否则 可能会造成同一个请求发送两次响应内容。create静态方法的参数$fd表示需要绑定连接的文件描述符,调用Http\Response对象的end方法和write方法时会向此连接发送数据。如果调用成功则返回一个新的Http\Response对象,否则失败返回false,适用于Swoole2.2.0+版本。Http\Server注册事件回调函数于Http\Server->on相同,不同之处在于HTTP\Server->on不接受onConnect和onReceive回调设置,Http\Server->on会额外接受一种新的事务类型onRequest。onRequest事件适用于Swoole1.7.7+版本,当服务器收到一个完整的HTTP请求后会调用onRequest函数。onRequest回调函数共有两个参数swoole_http_requst $request HTTP请求信息对象,包含了Header/GET/POST/Cookie等信息。swoole_http_response $response HTTP响应信息对象,支持Cookie/Header/Status等HTTP操作。onRequest回调函数返回时会销毁$request和$response对象,如果未执行$response->end()操作,Swoole底层会自动执行一次$response->end("")。$request和$response对象在传递给其它函数时,是不需要添加&取地址的引用符号的,传递后引用计数会增加,当onRequest退出时并不会被销毁。
$ vim http_server.php
<?php $addr = "0.0.0.0"; $port = 9501; $svr = new swoole_http_server($addr, $port); $svr->on("request", function(swoole_http_request $rq, swoole_http_response $rp){ //处理动态请求 $path_info = $rq->server["path_info"]; $file = __DIR__.$path_info; echo "\nfile:{$file}"; if(is_file($file) && file_exists($file)){ $ext = pathinfo($path_info, PATHINFO_EXTENSION); echo "\next:{$ext}"; if($ext == "php"){ ob_start(); include($file); $contents = ob_get_contents(); ob_end_clean(); }else{ $contents = file_get_contents($file); } echo "\ncontents:{$contents}"; $rp->end($contents); }else{ $rp->status(404); $rp->end("404 not found"); } }); $svr->start();
# 创建静态文件 $ vim index.html index.html # 测试静态文件 $ curl 127.0.0.1:9501/index.html # 观察http_server输出 file:/home/jc/projects/swoole/chat/index.html ext:html contents:index.html # 测试动态文件 $ vim index.php <?php echo "index.php"; #观察http_server日志输出 file:/home/jc/projects/swoole/chat/index.php ext:php contents:index.php
获取动态请求的参数
$ vim http_server.php
<?php $addr = "0.0.0.0"; $port = 9501; $svr = new swoole_http_server($addr, $port); $svr->on("request", function(swoole_http_request $rq, swoole_http_response $rp){ //获取请求参数 $params = $rq->get; echo "\nparams:".json_encode($params); //处理动态请求 $path_info = $rq->server["path_info"]; $file = __DIR__.$path_info; echo "\nfile:{$file}"; if(is_file($file) && file_exists($file)){ $ext = pathinfo($path_info, PATHINFO_EXTENSION); echo "\next:{$ext}"; if($ext == "php"){ ob_start(); include($file); $contents = ob_get_contents(); ob_end_clean(); }else{ $contents = file_get_contents($file); } echo "\ncontents:{$contents}"; $rp->end($contents); }else{ $rp->status(404); $rp->end("404 not found"); } }); $svr->start();
测试带参数的请求
$ curl 127.0.0.1:9501?k=v
观察请求参数的输出
params:{"k":"v"} file:/home/jc/projects/swoole/chat/index.html ext:html contents:index.html
静态文件处理
$ vim mimes.php
<?php return [ "jpg"=>"image/jpeg", "jpeg"=>"image/jpeg", "bmp"=>"image/bmp", "ico"=>"image/x-icon", "gif"=>"image/gif", "png"=>"image/png", "css"=>"text/css", "html"=>"text/html", "xml"=>"text/xml", "bin"=>"application/octet-stream", "js"=>"application/javascript", "tar"=>"application/x-tar", "ppt"=>"application/vnd.ms-powerpoint", "pdf"=>"application/pdf", "swf"=>"application/x-shockwave-flash", "zip"=>"application/x-zip-compressed" ];
$ vim http_server.php
<?php //创建HTTP服务器 $addr = "0.0.0.0"; $port = 9501; $srv = new swoole_http_server($addr, $port); //设置HTTP服务器参数 $cfg = []; $cfg["worker_num"] = 4;//设置工作进程数量 $cfg["daemonize"] = 0;//守护进程化,程序转入后台。 $srv->set($cfg); //处理请求 $srv->on("request", function(swoole_http_request $rq, swoole_http_response $rp) use($srv){ //获取请求文件信息与文件后缀 $path_info = $rq->server["path_info"]; $ext = pathinfo($path_info, PATHINFO_EXTENSION); //文件是否存在 $file = __DIR__.$path_info; if(!is_file($file) || !file_exists($file)){ $rp->status(404); $rp->end("404 NOT FOUND"); } //处理静态请求 if($ext != "php"){ //设置响应头信息的内容内容 $mimes = include("mimes.php"); $rp->header("Content-Type", $mimes[$ext]); //获取静态文件内容 $contents = file_get_contents($file); //返回内容 $rp->end($contents); } }); //启动服务 $srv->start();
发送请求,浏览器访问127.0.0.1:9501/test.jpeg,查看图片。
面向对象
$ vim http_server.php
<?php class HttpServer { public static function run($host, $port, $options=[]) { $srv = new swoole_http_server($host, $port); if(!empty($options)){ $srv->set($options); } $srv->on("request", function(swoole_http_request $rq, swoole_http_response $rp) use($srv){ $rp->end("test"); $srv->close($rq->fd); }); $srv->start(); } } HttpServer::run("127.0.0.1", 9501, ["worker_num"=>2, "daemonize"=>0]);
压力测试
使用Apache Bench工具进行压力测试可以发现,swoole_http_server远超过PHP-FPM、Golang自带的HTTP服务器、Node.js自带的HTTP服务器,性能接近Nginx的静态文件处理。
Swoole的http server与PHP-FPM的性能对比
安装Apache的压测工作ab
$ sudo apt install apache2-util
使用100个客户端跑1000次,平均每个客户端10个请求。
$ ab -c 100 -n 1000 127.0.0.1:9501/index.php Concurrency Level: 100 Time taken for tests: 0.480 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 156000 bytes HTML transferred: 9000 bytes Requests per second: 2084.98 [#/sec] (mean) Time per request: 47.962 [ms] (mean) Time per request: 0.480 [ms] (mean, across all concurrent requests) Transfer rate: 317.63 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 3.0 0 12 Processing: 4 44 10.0 45 57 Waiting: 4 44 10.1 45 57 Total: 16 45 7.8 45 57 Percentage of the requests served within a certain time (ms) 50% 45 66% 49 75% 51 80% 52 90% 54 95% 55 98% 55 99% 56 100% 57 (longest request)
观察可以发现QPS可以达到 Requests per second: 2084.98 [#/sec] (mean)。
HTTP SERVER 配置选项
swoole_server::set()用于设置swoole_server运行时的各项参数化。
$cfg = []; // 处理请求的进程数量 $cfg["worker_num"] = 4; // 守护进程化 $cfg["daemonize"] = 1; // 设置工作进程的最大任务数量 $cfg["max_request"] = 0; $cfg["backlog"] = 128; $cfg["max_request"] = 50; $cfg["dispatch_mode"] = 1; $srv->set($cfg);
配置HTTP SERVER参数后测试并发
$ vim http_server.php
<?php //创建HTTP服务器 $addr = "0.0.0.0"; $port = 9501; $srv = new swoole_http_server($addr, $port); //设置HTTP服务器参数 $cfg = []; $cfg["worker_num"] = 4;//设置工作进程数量 $cfg["daemonize"] = 1;//守护进程化,程序转入后台。 $srv->set($cfg); $srv->on("request", function(swoole_http_request $rq, swoole_http_response $rp){ //获取请求参数 $params = $rq->get; echo "\nparams:".json_encode($params); //处理动态请求 $path_info = $rq->server["path_info"]; $file = __DIR__.$path_info; echo "\nfile:{$file}"; if(is_file($file) && file_exists($file)){ $ext = pathinfo($path_info, PATHINFO_EXTENSION); echo "\next:{$ext}"; if($ext == "php"){ ob_start(); include($file); $contents = ob_get_contents(); ob_end_clean(); }else{ $contents = file_get_contents($file); } echo "\ncontents:{$contents}"; $rp->end($contents); }else{ $rp->status(404); $rp->end("404 not found"); } }); //启动服务 $srv->start();
查看进程
$ ps -ef|grep http_server.php root 16224 1207 0 22:41 ? 00:00:00 php http_server.php root 16225 16224 0 22:41 ? 00:00:00 php http_server.php root 16227 16225 0 22:41 ? 00:00:00 php http_server.php root 16228 16225 0 22:41 ? 00:00:00 php http_server.php root 16229 16225 0 22:41 ? 00:00:00 php http_server.php root 16230 16225 0 22:41 ? 00:00:00 php http_server.php root 16233 2456 0 22:42 pts/0 00:00:00 grep --color=auto http_server.php
查看后台守护进程
$ ps axuf|grep http_server.php root 16622 0.0 0.0 21536 1044 pts/0 S+ 22:46 0:00 | | \_ grep --color=auto http_server.php root 16224 0.0 0.3 269036 8104 ? Ssl 22:41 0:00 \_ php http_server.php root 16225 0.0 0.3 196756 8440 ? S 22:41 0:00 \_ php http_server.php root 16227 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php root 16228 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php root 16229 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php root 16230 0.0 0.6 195212 14524 ? S 22:41 0:00 \_ php http_server.php $ ps auxf|grep http_server.php|wc -l 7
杀死后台进程
# 强杀后台进程 $ kill -9 $(ps aux|grep swoole|grep -v grep|awk '{print $2}') $ kill -9 16224 $ kill -9 16225 $ kill -9 16227 $ kill -9 16228 $ kill -9 16229 $ kill -9 16230 # 重启后台进程 $ kill -10 $(ps aux|grep http_server|grep -v grep|awk '{print $2}')
压测
$ ab -c 100 -n 1000 127.0.0.1:9501/index.php Server Software: swoole-http-server Server Hostname: 127.0.0.1 Server Port: 9501 Document Path: /index.php Document Length: 9 bytes Concurrency Level: 100 Time taken for tests: 0.226 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 156000 bytes HTML transferred: 9000 bytes Requests per second: 4417.72 [#/sec] (mean) Time per request: 22.636 [ms] (mean) Time per request: 0.226 [ms] (mean, across all concurrent requests) Transfer rate: 673.01 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 2.8 0 11 Processing: 4 21 7.2 20 49 Waiting: 1 21 7.2 20 49 Total: 5 22 7.6 20 56 Percentage of the requests served within a certain time (ms) 50% 20 66% 23 75% 25 80% 26 90% 30 95% 38 98% 45 99% 53 100% 56 (longest request)
观察可以发现QPC为Requests per second: 4417.72 [#/sec] (mean)。
性能优化
使用swoole_http_server服务后,若发现服务的请求耗时监控毛刺十分严重,接口耗时波动较大的情况,可以观察下服务的响应包response的大小,若响应包超过1~2M甚至更大,则可判断是由于包太多而且很大导致服务响应波动较大。
为什么响应包惠导致相应的时间波动呢?主要有两个方面的影响,第一是响应包太大导致Swoole之间进程通信更加耗时并占用更多资源。第二是响应包太大导致Swoole的Reactor线程发包更加耗时。
以上就是一起看看Swoole HTTP的详细内容,更多请关注自由互联其它相关文章!

