您好,欢迎访问一九零五行业门户网

Swoole与HTTP

目标
了解swoole的http_server的使用了解swoole的tcp服务开发实际项目中问题如粘包处理、代理热更新、用户验证等。swoole与现有框架结合
推荐(免费):swoole
风格
偏基础重代码环境
php版本:swoole版本:https://github.com/swoole/swoole-srczphp开发框架:https://github.com/shenzhe/zphphttp server
静态文件处理动态请求与框架结合# 查看swoole版本$ php -r 'echo swoole_version;'4.3.1
基础概念
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: http://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 okdate: mon, 23 may 2005 22:38:34 gmtcontent-type: text/html; charset=utf-8content-encoding: utf-8content-length: 138last-modified: wed, 08 jan 2003 23:11:55 gmtserver: apache/1.3.3.7 (unix) (red-hat/linux)etag: 3f80f-1b6-3e1cb03baccept-ranges: bytesconnection: 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_http_server($host, $port);var_dump($server);//设置服务器运行参数$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){    var_dump($request);    var_dump($response);        //获取客户端文件描述符    $fd = $request->fd;    if(!empty($fd)){        //获取连接信息        $clientinfo = $server->getclientinfo($fd);        var_dump($clientinfo);        //获取收包时间        var_dump($clientinfo[last_time]);    }    //响应客户端http请求    $response->write(success);//向客户端写入响应数据    $response->end();//浏览器中输出结果});//启动服务器$server->start();
# 使用php-cli运行服务器脚本$ php server.php# 使用curl向http服务器发送请求测试$ curl 127.0.0.1:9501
使用注意
echo、var_dump、print_r的内容是在服务器中输出的浏览器中输出需要使用$rp->end(string $contents),end()方法只能调用一次。如果需要多次先客户端发送消息可使用$rp->write(string $content)方法完整的http协议请求会被解析并封装在swoole_http_request对象中所有的http协议响应会通过swoole_http_response对象进行封装并发送http服务器的本质
由于swoole_http_server是基于swoole_server的,所以swoole_server下的方法在swoole_http_server中都可以使用,只是swoole_http_server只能被客户端唤起。简单来说,swoole_http_server是基于swoole_server加上http协议,再加上request和response类库去实现请求数据和获取数据。与php-fpm不同的是,web服务器收到请求后会传递给swoole的http服务器,直接返回请求。
swoole_http_server
http\serverswoole\http\server继承自server,是一个http服务器实现,支持同步与有异步两种模式。无论是同步模式还是异步模式,http服务器都可以维持大量的tcp客户端连接,同步与异步仅仅提现在对请求的处理方式。
同步模式同步模式等同于nginx+php-fpm/apache,需要设置大量worker工作进程来完成并发请求处理,worker工作进程可以使用同步阻塞io,编程方式与普通的php的web程序完全一致。与php-fpm/apache不同的是,客户端连接并不会独占进程,服务器依然可以应对大量并发连接。
异步模式异步模式下整个http服务器是异步非阻塞的,服务器可以应答大规模的并发连接和并发请求,编程方式需要完全使用异步api,如mysql、redis、http客户端、file_get_contents、sleep等阻塞io操作必须切换为异步方式,如异步client、event、timer等api。
查看http服务器实例对象
var_dump($server);
object(swoole\connection\iterator)#2 (0) {  [host]=>string(7) 0.0.0.0  [port]=>int(9501)  [type]=>int(1)  [mode]=>int(2)  [ports]=>  array(1) {    [0]=>    object(swoole\server\port)#3 (16) {      [onconnect:swoole\server\port:private]=>null      [onreceive:swoole\server\port:private]=>null      [onclose:swoole\server\port:private]=>null      [onpacket:swoole\server\port:private]=>null      [onbufferfull:swoole\server\port:private]=>null      [onbufferempty:swoole\server\port:private]=>null      [onrequest:swoole\server\port:private]=>null      [onhandshake:swoole\server\port:private]=>null      [onopen:swoole\server\port:private]=>null      [onmessage:swoole\server\port:private]=>null      [host]=>string(7) 0.0.0.0      [port]=>int(9501)      [type]=>int(1)      [sock]=>int(4)      [setting]=>null      [connections]=>object(swoole\connection\iterator)#4 (0) {      }    }  }  [master_pid]=>int(0)  [manager_pid]=>int(0)  [worker_id]=>int(-1)  [taskworker]=>bool(false)  [worker_pid]=>int(0)  [onrequest:swoole\http\server:private]=>null  [onhandshake:swoole\http\server:private]=>null}
配置选项
文件上传upload_tmp_dir http服务器支持大文件上传,但由于swoole底层的限制,文件内容是存放在内存中的,因此如果并发上传大量文件可能会导致内存占用量过大的问题。
可以修改upload_tmp_dir选项用于配置上传文件的临时目录,需要注意是目录文件夹的名称最大长度不得超过220个字节。
$configs = [];$configs[upload_tmp_dir] = /data/uploads/;$server->set($configs);
post解析http_parse_post
可通过修改http_parse_post配置项用来设置表单post提交后是否解析,若设置为true则表示会自动将content-type内容类型为x-www-urlencode的请求包体解析到 post 数组,若设置为false则表示将会关闭 post解析。
$configs = [];$configs[http_parse_post] = true;$server->set($configs);
post尺寸 package_max_length
默认情况下,表单上传或post提交2mb的数据的限制,可通过修改package_max_length选项调整post尺寸大小。
$configs = [];$configs[package_max_length] = 2*1024;$server->set($configs);
解析cookiehttp_parse_cookie
通过修改http_parse_cookie配置项可以开启或关闭cookie解析,关闭后将会在header头信息学中保留未经处理的原始cookies信息。
$configs = [];$configs[http_parse_cookie] = true;$server->set($configs);
文件压缩http_compression http_compression适用于swoole4.1.0+版本,用于启用或关闭对http信息的压缩,默认为开启状态。
由于http-chunk不支持分段独立压缩,因此默认已强制关闭了压缩功能。
$configs = [];$configs[http_compression] = false;$server->set($configs);
目前http支持gzip、br(需google brotli库支持)、deflate三种压缩格式,swoole底层会根据客户端浏览器传入的accept-encoding头信息自动选择压缩方式。
压缩级别http_compression_level http_compression_level选项用于配置压缩的级别,压缩级别越高压缩后体积越小,同时也会越占用cpu。
$configs = [];$configs[http_compression_level] = 1;$server->set($configs);
静态根目录document_root document_root选项适用于swoole1.9.17+版本,用于配置静态文件的根目录,该功能由于较为简易不推荐在公网环境下直接使用,常于enable_static_handler选项配合使用。
如果设置document_root和enable_static_handler = true后,swoole底层收到http请求时会先判断document_root的路径下是否存在某静态文件,如果存在会直接发送内容给客户端,并不再调用onrequest函数。
这里需要注意的时,在使用静态文件处理特性时,应当将动态php代码于静态文件进行隔离,静态文件应存放到特定的目录下。
$configs = [];$configs[document_root] = /app;$server->set($configs);
静态处理 enable_static_handler enable_static_handler选项用于开启或关闭静态文件请求处理功能,常配合document_root选项使用。
$configs = [];$configs[enable_static_handler] = true;$server->set($configs);
静态处理器路径static_handler_locations static_handler_location选项适用于swoole4.4.0+版本,用于设置静态处理器的路径,类型为数组,默认不启用。
静态处理器类似于nginx的location指令,可以指定一个或多个路径为静态路径。只有url在指定路径下才会启用静态问而建处理器,否则会视为动态请求。location选项必须以/开头并支持多级路径,如/app/images。
当启用static_handler_locations选项后,如果请求对应的文件不存在,将直接会返回404错误。
$configs = [];$configs[static_handler_locations] = [/static, /public/assets];$server->set($configs);
设置代理由于swoole_http_server对http协议支持的并不完整,建议仅仅作为应用服务器,并在前端增加nginx作为反向代理。
操作前需要修改服务器的运行参数,设置enable_static_handle为true后,底层收到http请求会像判断document_root路径下是否存在目标文件,若存在则会直接发送文件给客户端,不再触发onrequest回调。
设置服务器运行时环境$ vim server.php
$configs = [];$configs[enable_static_handler] =  true;$configs[document_root] = /test;$server->set($configs);
设置nginx反向代理配置例如:设置nginx反向代理127.0.0.1:9501
$ vim /usr/local/nginx/conf/nginx.conf
http {    include       mime.types;    default_type  application/octet-stream;    #log_format  main  '$remote_addr - $remote_user [$time_local] $request '    #                  '$status $body_bytes_sent $http_referer '    #                  '$http_user_agent $http_x_forwarded_for';    #access_log  logs/access.log  main;    sendfile        on;    #tcp_nopush     on;    #keepalive_timeout  0;    keepalive_timeout  65;    #gzip  on;        upstream swoole{                server 127.0.0.1:9501;                keepalive 4;        }    server {        listen       80;        server_name  www.swoole.com;        #charset koi8-r;        #access_log  logs/host.access.log  main;        location / {            proxy_pass http://swoole;            proxy_set_header connection ;            proxy_http_version 1.1;            root   html;            index  index.html index.htm;        }        #error_page  404              /404.html;        # redirect server error pages to the static page /50x.html        error_page   500 502 503 504  /50x.html;        location = /50x.html {            root   html;        }}
nginx+swoole的组合中nginx反向代理的配置
server {    root /data/wwwroot/;    server_name local.swoole.com;    location / {        proxy_http_version 1.1;        proxy_set_header connection keep-alive;        proxy_set_header x-real-ip $remote_addr;        if (!-e $request_filename) {             proxy_pass http://127.0.0.1:9501;        }    }}
请求对象
swoole_http_request请求对象保存了http客户端请求的相关信息,包括get、post、cookie、header等,请求对象$request销毁时会自动删除上传的临时文件,不要使用&符号引用$request请求对象。
var_dump($request);object(swoole\http\request)#6 (10) {  [fd]=>int(1)  [streamid]=>int(0)  [header]=>array(3) {    [host]=>string(14) 127.0.0.1:9501    [user-agent]=>string(11) curl/7.52.1    [accept]=>string(3) */*  }  [server]=>array(10) {    [request_method]=>string(3) get    [request_uri]=>string(1) /    [path_info]=>string(1) /    [request_time]=>int(1561689532)    [request_time_float]=>float(1561689533.0563)    [server_port]=>int(9501)    [remote_port]=>int(51188)    [remote_addr]=>string(9) 127.0.0.1    [master_time]=>int(1561689532)    [server_protocol]=>string(8) http/1.1  }  [request]=>null  [cookie]=>null  [get]=>null  [files]=>null  [post]=>null  [tmpfiles]=>null}
http\request->$headerhttp请求的头部信息,类型为数组,所有的键名均为小写。
$host = $request->header[host];$accept = $request->header[accept];
http\request->$server
http请求相关的服务器信息,相当于php的$_server全局数组,包含了http请求的方法、url路径、客户端ip等信息。服务器信息为关联数组,数组中的键名全部小写,并且与php的$_server数组保持一致。
$request_method = $request->server[request_method];$request_time = $request->server[request_time];$request_uri = $request->server[request_uri];
请求路径
当google的chrome浏览器访问服务器是会产生两次请求,这是因为chrome会自动请求一次favicon.ico文件,所以服务器会收到两个http请求,通过打印$request->server[request_uri]可以查看到请求url路径。如果需要屏蔽掉对favicon.ico的请求,可采用以下方式。
$uri = $request->server[request_uri];if($uri == /favicon.icon){  $respoonse->status(404);  $response->end();}
收包时间
request_time请求时间是在worker工作进程中设置的,在swoole_process多进程模式下存在dispatch分发的过程,因此可能会与实际收包时间存在偏差,尤其当请求量超过服务器处理能力时,有可能滞后于实际收包时间。
可通过server->getclientinfo()方法获取last_time以获取 准确的收包时间。
//获取客户端文件描述符$fd = $request->fd;if(!empty($fd)){    //获取连接信息    $clientinfo = $server->getclientinfo($fd);    var_dump($clientinfo);    //获取收包时间    var_dump($clientinfo[last_time]);}
客户端信息
server->getclientinfo()用于获取连接的客户端信息
bool|array server->getclientinfo(int $fd, int $extradata, bool $ignoreerror = false)
int $fd 表示客户端连接文件描述符int $extradata 表示扩展信息是保留参数目前无任何效果bool $ignoreerror 表示是否忽略错误,若设置为true表示即使连接关闭也会返回连接信息。如果传入的$fd客户端连接文件描述符存在则返回一个数组,若不存在或已关闭则返回false。
array(10) {  [server_port]=>int(9501)  [server_fd]=>int(4)  [socket_fd]=>int(12)  [socket_type]=>int(1)  [remote_port]=>int(51194)  [remote_ip]=>string(9) 127.0.0.1  [reactor_id]=>int(0)  [connect_time]=>int(1561690606)  [last_time]=>int(1561690606)  [close_errno]=>int(0)}
http\request->$get
http请求的get参数,相当于php中的$_get,格式为键值对的关联数组。为防止hash攻击,get参数最大不允许超过128个。
$get = $request->get;//获取http请求的所有get参数
http的get请求只有一个http header头,swowole底层使用固定大小的内存缓冲区为8k,而且不可修改。如果请求不是正确的http请求,将会出现错误,底层会抛出错误。
warn swreactorthead_onreceive_http_request: http header is too long.
http\request->$post
http请求携带post参数,格式为键值对的关联数组,post与header加起来的尺寸不得超过package_max_length的设置,否则会认为是恶意请求,另外post参数的个数不得超过128个。
$post = $request->post;
由于post文件上传时最大尺寸收到package_max_length配置项目的限制,默认为2mb,可以调用swoole_server->set传入新值修改尺寸。
由于swoole底层是全内存的,因此如果设置过大可能会导致大量并发请求,将服务器资源耗尽。
设置计算方法:最大内存占用 = 最大并发请求数量 * package_max_length
当使用curl发送post请求时服务器端会超时
curl在发送较大的post请求时会首先发送一个100-continue的请求,当收到服务器的回应才会发送实际的post数据。然后swoole_http_server并不支持100-continue,因此会导致curl请求超时。解决的办法时关闭curl的100-continue。
$ch = curl_init();curl_setopt($ch, curlopt_url, $url);curl_setopt($ch, curlopt_header, 0);curl_setopt($ch, curlopt_post, 1);//设置为post方式curl_setopt($ch, curlopt_httpheader, [exception:]);curl_setopt($ch, curlopt_postfields, $data);curl_setopt($ch, curlopt_returntransfer, true);
http\request->$cookie
http请求携带的cookie信息,格式为键值对的关联数组。
http\request->$files
http请求携带的文件上传信息,类型为以form表单名称为key键名的二维数组,与php原生的$_files相同,最大文件尺寸不得超过package_max_length中设置的值,不要使用swoole\http\server处理大文件上传。
$files = $request->files;var_dump($files);
array(5) {    [name] => facepalm.jpg    [type] => image/jpeg    [tmp_name] => /tmp/swoole.upfile.n3fmfr    [error] => 0    [size] => 15476}
name表示浏览器上传时传入的文件名称type表示浏览器上传时的mime类型tmp_name 表示浏览器上传的临时文件,文件名默认以/tmp/swoole.upfile开头。size表示上传文件的尺寸swoole1.9.10+版本支持is_uploaded_file和move_uploaded_file函数。当http请求对象$request对象销毁时,会自动删除上传的临时文件。
http\request->rawcontent()
rawcontent表示获取原始的post包体,用于非application/x-www-form-urlencode格式的http的post请求。等同于原生php的fopen(php://input),有时服务器不需要解析http的post请求参数。
swoole1.7.18+版本增加了http_parse_post配置用于关闭或开启post数据解析。
string http\request->rawcontent();
http\request->getdata()
getdata()方法用于获取完整的http请求报文,包括 http header和`http body消息体。
function swoole_http_request_getdata() : string
getdata需要swoole1.10.3或swoole2.1.2或更高的版本。
响应对象
swoole_http_response响应对象是进程隔离的,不能跨越进程或对象。如果是当前进程中,想使用fd文件描述符保存response响应对象、存储上下文,可使用php全局数组变量来保存。
swoole_http_response响应对象,通过调用此对象的方法实现http响应的发送,当响应对象销毁时,如果没有调用end发送http响应,底层会自动执行end方法。不要使用&符号引用$response对象。
object(swoole\http\response)#7 (4) {  [fd]=>int(1)  [header]=>null  [cookie]=>null  [trailer]=>null}
http服务器response响应对象,通过调过此对象的方法,实现http响应发送。当response对象销毁时,如果未调用则直接调用end方法,不要使用&符号引用$response对象。
http\response->header
function http\response->header(  string $key,  string $value,  bool $ucworods = true)
header方法用于设置http响应的header头信息,如果设置失败返回false,设置成功则无返回值。
string $key 表示http头的keystring $value 表示http头的valuebool $ucwords 表示是否需要对key进行http约定格式化,默认true会自动格式化。$response->header(content-type, image/jpeg, true);
跨域处理
$origin = $request->header['origin'];// access-control-allow-origin 不能使用 *,这样修改是不支持php版本低于7.0的。// $response->header('access-control-allow-origin', '*');$response->header('access-control-allow-origin', $origin);$response->header('access-control-allow-methods', 'options');$response->header('access-control-allow-headers', 'x-requested-with,session_id,content-type,token,origin');$response->header('access-control-max-age', '86400');$response->header('access-control-allow-credentials', 'true');if ($request->server['request_method'] == 'options') {  $response->status(200);  $response->end();  return;};
http\response->cookie
cookie方法用来设置http响应的cookie信息,方法参数与原生php的setcookie函数完全一致。
function  http\response->cookie(  string $key,  string $value = ,   int $expire = 0,  string $path = /,  string $domain = ,  bool  $secure = false,  bool $httponly = false)
cookie设置必须在end方法之前方才生效,swoole底层自动会对$value进行urlencode编码处理,同时允许设置多个相同的$key的cookie。
http\response->status
swoole_http_response->status(  int $http_status_code)
status方法用于发送http状态码,$http_status_code必须是合法的http状态码,如2xx、3xx、4xx、5xx等,若不是在会报错,另外status方法也必须在$response->end()之前执行方才生效。
string $url表示跳转的新地址会作为http header头中的location选项进行发送int $http_code 表示状态码,默认为302临时跳转,传入301表示永久跳转。http\response->redirect
redirect方法适用于swoole2.2.0+版本,用于发送http跳转,调用后会自动执行end方法并发送结束响应。
function http\response->redirect(  string $url,  int $http_code = 302)
例如
$server = new swoole_http_server(0.0.0.0, 9501, swoole_base);$server->on(request, function(swoole_http_request $request, swoole_http_response $response){  $url = http://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.htmlindex.html# 测试静态文件$ curl 127.0.0.1:9501/index.html# 观察http_server输出file:/home/jc/projects/swoole/chat/index.htmlext:htmlcontents:index.html# 测试动态文件$ vim index.phpget;    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.htmlext:htmlcontents:index.html
静态文件处理
$ vim mimes.php
<?phpreturn [ "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




<?phpclass 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.phpconcurrency level:      100time taken for tests:   0.480 secondscomplete requests:      1000failed requests:        0total transferred:      156000 byteshtml transferred:       9000 bytesrequests 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] receivedconnection times (ms)              min  mean[+/-sd] median   maxconnect:        0    1   3.0      0      12processing:     4   44  10.0     45      57waiting:        4   44  10.1     45      57total:         16   45   7.8     45      57percentage 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.phproot     16224  1207  0 22:41 ?        00:00:00 php http_server.phproot     16225 16224  0 22:41 ?        00:00:00 php http_server.phproot     16227 16225  0 22:41 ?        00:00:00 php http_server.phproot     16228 16225  0 22:41 ?        00:00:00 php http_server.phproot     16229 16225  0 22:41 ?        00:00:00 php http_server.phproot     16230 16225  0 22:41 ?        00:00:00 php http_server.phproot     16233  2456  0 22:42 pts/0    00:00:00 grep --color=auto http_server.php
查看后台守护进程
$ ps axuf|grep http_server.phproot     16622  0.0  0.0  21536  1044 pts/0    s+   22:46   0:00  |   |           \_ grep --color=auto http_server.phproot     16224  0.0  0.3 269036  8104 ?        ssl  22:41   0:00  \_ php http_server.phproot     16225  0.0  0.3 196756  8440 ?        s    22:41   0:00      \_ php http_server.phproot     16227  0.0  0.6 195212 14524 ?        s    22:41   0:00          \_ php http_server.phproot     16228  0.0  0.6 195212 14524 ?        s    22:41   0:00          \_ php http_server.phproot     16229  0.0  0.6 195212 14524 ?        s    22:41   0:00          \_ php http_server.phproot     16230  0.0  0.6 195212 14524 ?        s    22:41   0:00          \_ php http_server.php$ ps auxf|grep http_server.php|wc -l7
杀死后台进程
# 强杀后台进程$ 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.phpserver software:        swoole-http-serverserver hostname:        127.0.0.1server port:            9501document path:          /index.phpdocument length:        9 bytesconcurrency level:      100time taken for tests:   0.226 secondscomplete requests:      1000failed requests:        0total transferred:      156000 byteshtml transferred:       9000 bytesrequests 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] receivedconnection times (ms)              min  mean[+/-sd] median   maxconnect:        0    1   2.8      0      11processing:     4   21   7.2     20      49waiting:        1   21   7.2     20      49total:          5   22   7.6     20      56percentage 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的详细内容。
其它类似信息

推荐信息