Swoole HTTP를 살펴보겠습니다.

coldplay.xixi
풀어 주다: 2021-04-09 19:24:36
앞으로
2925명이 탐색했습니다.

Swoole HTTP를 살펴보겠습니다.

목표

  • swoole의 http_server 사용법 이해
  • swoole의 tcp 서비스 개발 이해
  • sticky packet 처리, 프록시 핫 업데이트, 사용자 인증 등 실제 프로젝트에서의 문제점
  • Swoole은 기존 프레임워크와 결합됩니다

Style

  • Basic Heavy Code

Environment

  • PHP 버전:
  • Swoole 버전: https://github.com/swoole/swoole-src
  • zphp 개발 프레임워크: https://github.com/shenzhe/zphp

HTTP 서버

  • 정적 파일 처리
  • 프레임워크와 결합된 동적 요청
# 查看SWOOLE版本 $ php -r 'echo SWOOLE_VERSION;' 4.3.1
로그인 후 복사

권장(무료):swoole

Basic 개념

HTTP 메시지

HTTP 요청 메시지 구조에 대하여




Swoole 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 응답 메시지 구성 구조




Swoole 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 모드에서는 많은rootshell을 실행할 수 없지만 Swoole 서버를 사용하면 좋은 제어가 가능합니다.rsync,git,svn등을 통해rootshell是无法执行的,而使用Swoole服务器就能很好的控制rsyncgitsvn等。

使用Swoole的API,构建HTTP服务器需要4个步骤

  1. 创建Server对象
  2. 设置运行时参数
  3. 注册事件回调函数
  4. 启动服务器
# 创建应用 $ 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
로그인 후 복사

使用注意

  • echovar_dumpprint_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协议,再加上requestresponse类库去实现请求数据和获取数据。与PHP-FPM不同的是,Web服务器收到请求后会传递给Swoole的HTTP服务器,直接返回请求。




Swoole HTTP를 살펴보겠습니다.

swoole_http_server

HttpServer

SwooleHTTPServer继承自Server,是一个HTTP服务器实现,支持同步与有异步两种模式。无论是同步模式还是异步模式,HTTP服务器都可以维持大量的TCP客户端连接,同步与异步仅仅提现在对请求的处理方式。

  • 同步模式

同步模式等同于Nginx+PHP-FPM/Apache,需要设置大量Worker工作进程来完成并发请求处理,Worker工作进程可以使用同步阻塞IO,编程方式与普通的PHP的Web程序完全一致。与PHP-FPM/Apache

Swoole의 API를 사용하면 HTTP 서버를 구축하는 데 4단계가 소요됩니다.
    서버 객체 생성런타임 매개변수 설정이벤트 콜백 기능 등록서버 시작
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 }
로그인 후 복사
로그인 후 복사
$configs = []; $configs["upload_tmp_dir"] = "/data/uploads/"; $server->set($configs);
로그인 후 복사
로그인 후 복사
사용 참고 사항 echo, var_dump, print_r의 내용이 서버에 출력됩니다. 브라우저에 출력하려면 를 사용해야 합니다. $rp-> end(string $contents), end()메서드는 한 번만 호출할 수 있습니다. 클라이언트에 메시지를 여러 번 보내야 하는 경우 $rp->write(string $content)메서드를 사용할 수 있습니다.전체 HTTP 프로토콜 요청은 다음에서 구문 분석되고 캡슐화됩니다. swoole_http_request 개체의 모든 HTTP 프로토콜 응답은 캡슐화되어 <code>swoole_http_response개체를 통해 전송됩니다. HTTP 서버의 본질 swoole_http_server는 <code> swoole_server를 기반으로 하므로 swoole_server아래의 메소드는 swoole_http_server에서 사용할 수 있지만 swoole_http_server는 클라이언트에 의해서만 호출됩니다. 간단히 말해서, swoole_http_serverswoole_serverHTTP프로토콜, 요청응답을 기반으로 합니다. 코드>데이터 요청 및 데이터 획득을 구현하는 클래스 라이브러리입니다. PHP-FPM과 달리 웹 서버가 요청을 받은 후 Swoole의 HTTP서버로 전달되어 요청을 직접 반환합니다. 4933 7 01-986cef92ece579b3. pngswoole_http_server

HttpServer

SwooleHTTPServer는 Server에서 상속되며 동기 및 비동기 모드를 모두 지원하는 HTTP 서버 구현입니다. 동기 모드이든 비동기 모드이든 HTTP 서버는 많은 수의 TCP 클라이언트 연결을 유지할 수 있습니다. 동기화 및 비동기성은 요청이 처리되는 방식만을 나타냅니다. 동기 모드동기 모드는 Nginx+ PHP-FPM/Apache와 동일합니다. 동시 작업을 완료하려면 다수의 작업자 작업자 프로세스를 설정해야 합니다. 요청 처리 작업자 작업자 프로세스는 동기식 차단 IO를 사용하여 프로그래밍 방법은 일반 PHP 웹 프로그램과 완전히 동일합니다. PHP-FPM/Apache와 달리 클라이언트 연결은 프로세스를 독점하지 않으며 서버는 여전히 많은 수의 동시 연결을 처리할 수 있습니다.
  • 异步模式

异步模式下整个HTTP服务器是异步非阻塞的,服务器可以应答大规模的并发连接和并发请求,编程方式需要完全使用异步API,如MySQL、Redis、HTTP客户端、file_get_contentssleep等阻塞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支持gzipbr(需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_rootenable_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_handletrue后,底层收到HTTP请求会像判断document_root路径下是否存在目标文件,若存在则会直接发送文件给客户端,不再触发onRequest回调。

  1. 设置服务器运行时环境
$ vim server.php
로그인 후 복사
$configs = []; $configs["enable_static_handler"] = true; $configs["document_root"] = "/test"; $server->set($configs);
로그인 후 복사
  1. 设置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客户端请求的相关信息,包括GETPOSTCOOKIEHeader等,请求对象$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->$header

HTTP请求的头部信息,类型为数组,所有的键名均为小写。

$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参数,格式为键值对的关联数组,POSTHeader加起来的尺寸不得超过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_filemove_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头的Key
  • string $value表示HTTP头的Value
  • bool $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不接受onConnectonReceive回调设置,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 $requestHTTP请求信息对象,包含了Header/GET/POST/Cookie等信息。
  • swoole_http_response $responseHTTP响应信息对象,支持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를 살펴보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:csdn.net
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!