Swoole 初步了解

Swoole 初步了解

Swoole这个名字不是一个英文单词,是由我创造的一个音近字。
我最早想到的名字是叫做sword-server,寓意是为广大PHPer创造一把锋利的剑,
后来联想到google也是凭空创造出来的,所以我就给它命名为swoole。 <韩天峰>

安装

  • PECL 一键安装
1
pecl install swoole
  • 源码安装
1
2
3
4
5
6
// 下载源码并解压
cd swoole-src-swoole-1.8.0-table/
phpize
./configure --enable-async-mysql
sudo make
sudo make install

配置 php.ini

1
extension=swoole.so

基础实例

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Server
{
private $serv;

public function __construct() {
$this->serv = new swoole_server("0.0.0.0", 9501);
$this->serv->set(array(
'worker_num' => 8,
'daemonize' => false,
'max_request' => 10000,
'dispatch_mode' => 2,
'debug_mode' => 1
));

$this->serv->on('Start', array($this, 'onStart'));
$this->serv->on('Connect', array($this, 'onConnect'));
$this->serv->on('Receive', array($this, 'onReceive'));
$this->serv->on('Close', array($this, 'onClose'));

$this->serv->start();
}

public function onStart( $serv ) {
echo "Start\n";
}

public function onConnect( $serv, $fd, $from_id ) {
$serv->send( $fd, "Hello {$fd}!" );
}

public function onReceive( swoole_server $serv, $fd, $from_id, $data ) {
echo "Get Message From Client {$fd}:{$data}\n";
}

public function onClose( $serv, $fd, $from_id ) {
echo "Client {$fd} close connection\n";
}
}

// 启动服务器
$server = new Server();

解析

从代码中可以看出,创建一个swoole_server基本分三步:

  1. 通过构造函数创建swoole_server对象
  2. 调用set函数设置swoole_server的相关配置选项
  3. 调用on函数设置相关回调函数
  • onStart回调在server运行前被调用,
  • onConnect在有新客户端连接过来时被调用,
  • onReceive函数在有数据发送到server时被调用
  • onClose在有客户端断开连接时被调用。

配置选项说明

  • worker_num: 指定启动的worker进程数。

  • max_request: 每个worker进程允许处理的最大任务数。

  • max_conn : 服务器允许维持的最大TCP连接数

  • ipc_mode : 设置进程间的通信方式。

    • 1 => 使用unix socket通信
    • 2 => 使用消息队列通信
    • 3 => 使用消息队列通信,并设置为争抢模式
  • dispatch_mode : 指定数据包分发策略。

    • 1 => 轮循模式,收到会轮循分配给每一个worker进程
    • 2 => 固定模式,根据连接的文件描述符分配worker。这样可以保证同一个连接发来的数据只会被同一个worker处理
    • 3 => 抢占模式,主进程会根据Worker的忙闲状态选择投递,只会投递给处于闲置状态的Worker
  • task_worker_num : 服务器开启的task进程数。

    设置此参数后,必须要给swoole_server设置onTask/onFinish两个回调函数,否则启动服务器会报错。

  • task_max_request : 每个task进程允许处理的最大任务数。

  • task_ipc_mode : 设置task进程与worker进程之间通信的方式。

  • daemonize : 设置程序进入后台作为守护进程运行。

    说明:长时间运行的服务器端程序必须启用此项。如果不启用守护进程,当ssh终端退出后,程序将被终止运行。
    启用守护进程后,标准输入和输出会被重定向到 log_file,如果 log_file未设置,则所有输出会被丢弃。

  • log_file: 指定日志文件路径

    说明:在swoole运行期发生的异常信息会记录到这个文件中。默认会打印到屏幕。
    注意 log_file 不会自动切分文件,所以需要定期清理此文件。

  • heartbeat_check_interval : 设置心跳检测间隔

    说明:此选项表示每隔多久轮循一次,单位为秒。每次检测时遍历所有连接,
    如果某个连接在间隔时间内没有数据发送,则强制关闭连接(会有onClose回调)。

  • heartbeat_idle_time: 设置某个连接允许的最大闲置时间。

    说明:该参数配合heartbeat_check_interval使用。每次遍历所有连接时,如果某个连接在heartbeat_idle_time时间内没有数据发送,
    则强制关闭连接。默认设置为heartbeat_check_interval * 2

  • open_eof_check: 打开eof检测功能

    说明:与 package_eof 配合使用。此选项将检测客户端连接发来的数据,当数据包结尾是指定的package_eof 字符串时
    才会将数据包投递至Worker进程,否则会一直拼接数据包直到缓存溢出或超时才会终止。
    一旦出错,该连接会被判定为恶意连接,数据包会被丢弃并强制关闭连接。

  • package_eof: 设置EOF字符串

    说明:package_eof最大只允许传入8个字节的字符串

  • open_length_check : 打开包长检测

    说明:包长检测提供了固定包头+包体这种格式协议的解析,。
    启用后,可以保证Worker进程onReceive每次都会收到一个完整的数据包。

  • package_length_offset : 包头中第几个字节开始存放了长度字段

    说明:配合open_length_check使用,用于指明长度字段的位置。

  • package_body_offset: 从第几个字节开始计算长度

    说明:配合open_length_check使用,用于指明包头的长度。

  • package_length_type : 指定包长字段的类型

    • s => int16_t 机器字节序
    • S => uint16_t 机器字节序
    • n => uint16_t 大端字节序
    • N => uint32_t 大端字节序
    • L => uint32_t 机器字节序
    • l => int 机器字节序

      说明:配合open_length_check使用,指定长度字段的类型,参数如下:

  • package_max_length : 设置最大数据包尺寸

    说明:该值决定了数据包缓存区的大小。如果缓存的数据超过了该值,
    则会引发错误。具体错误处理由开启的协议解析的类型决定。

  • open_cpu_affinity : 启用CPU亲和性设置

    说明:在多核的硬件平台中,启用此特性会将swoolereactor线程/worker进程绑定到固定的一个核上。
    可以避免进程/线程的运行时在多个核之间互相切换,提高CPU Cache的命中率。

  • open_tcp_nodelay : 启用open_tcp_nodelay

    说明:开启后TCP连接发送数据时会无关闭Nagle合并算法,
    立即发往客户端连接。在某些场景下,如http服务器,可以提升响应速度。

  • tcp_defer_accept: 启用tcp_defer_accept特性

    说明:启动后,只有一个TCP连接有数据发送时才会触发accept

  • ssl_cert_file/ssl_key_file : 设置SSL隧道加密

    说明:设置值为一个文件名字符串,指定cert证书和key的路径。

  • open_tcp_keepalive : 打开TCPKEEP_ALIVE选项

    说明:使用TCP内置的keep_alive属性,用于保证连接不会因为长时闲置而被关闭。

  • tcp_keepidle : 指定探测间隔。

    说明:配合open_tcp_keepalive使用,如果某个连接在tcp_keepidle内没有任何数据来往,则进行探测。

  • tcp_keepinterval : 指定探测时的发包间隔

    说明:配合open_tcp_keepalive使用

  • tcp_keepcount : 指定探测的尝试次数

    说明:配合open_tcp_keepalive使用,若tcp_keepcount次尝试后仍无响应,则判定连接已关闭

  • backlog : 指定Listen队列长度

    说明:此参数将决定最多同时有多少个等待accept的连接。

  • reactor_num: 指定Reactor线程数

    说明:设置主进程内事件处理线程的数量,默认会启用CPU核数相同的数量,
    一般设置为CPU核数的1-4倍,最大不得超过CPU核数*4。

  • task_tmpdir: 设置task的数据临时目录

    说明:在swoole_server中,如果投递的数据超过8192字节,
    将启用临时文件来保存数据。这里的task_tmpdir就是用来设置临时文件保存的位置。

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
class Client
{
private $client;

public function __construct() {
$this->client = new swoole_client(SWOOLE_SOCK_TCP);
}

public function connect() {
if( !$this->client->connect("127.0.0.1", 9501 , 1) ) {
echo "Error: {$fp->errMsg}[{$fp->errCode}]\n";
}
$message = $this->client->recv();
echo "Get Message From Server:{$message}\n";

fwrite(STDOUT, "请输入消息:");
$msg = trim(fgets(STDIN));
$this->client->send( $msg );
}
}

$client = new Client();
$client->connect();

这里,通过swoole_client创建一个基于TCP的客户端实例,
并调用connect函数向指定的IP及端口发起连接请求。随后即可通过recv()send()两个函数来接收和发送请求。
需要注意的是,这里我使用了默认的同步阻塞客户端,因此recvsend操作都会产生网络阻塞。

Swoole 异步任务Task

Task简介

Swoole的业务逻辑部分是同步阻塞运行的,如果遇到一些耗时较大的操作,例如访问数据库、广播消息等,就会影响服务器的响应速度。
因此Swoole提供了Task功能,将这些耗时操作放到另外的进程去处理,当前进程继续执行后面的逻辑。

开启Task功能

开启Task功能只需要在swoole_server的配置项中添加task_worker_num一项即可,如下:

1
2
3
$serv->set(array(
'task_worker_num' => 8
));

即可开启task功能。此外,必须给swoole_server绑定两个回调函数:onTaskonFinish
这两个回调函数分别用于执行Task任务处理Task任务的返回结果。

使用Task首先是发起一个Task,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
public function onReceive (swoole_server $serv, $fd, $from_id, $data ) {
echo "Get Message From Client {$fd}:{$data}\n";
// send a task to task worker
$parm = array(
'fd' => $fd
);

// start a task
$serv->task(json_encode($param));

echo "Continue Handle Worker\n";
}

可以看到,发起一个任务时,只需通过swoole_server对象调用task函数即可发起一个任务。
swoole内部会将这个请求投递给task_worker,而当前Worker进程会继续执行。

当一个任务发起后,task_worker进程会响应onTask回调函数,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public function onTask($serv, $task_id, $from_id, $data)
{
echo "This Task {$task_id} from Worker {$from_id}\n";
echo "Data: {$data}\n";

for($i = 0; $i < 10; $i ++) {
sleep(1);
echo "Task {$task_id} Handle {$i} times...\n";
}
$fd = json_decode($data, true)['fd'];
$serv->send($fd, "Data in Task {$task_id}");
return "Task {$task_id}'s result";
}

这里我用sleep函数和循环模拟了一个长耗时任务。在onTask回调中,我们通过task_idfrom_id(也就是worker_id)来区分不同进程投递的不同task
当一个task执行结束后,通过return一个字符串将执行结果返回给Worker进程。
Worker进程将通过onFinish回调函数接收这个处理结果。

1
2
3
4
5
public function onFinish($serv, $task_id, $data)
{
echo "Task {$task_id} finish\n";
echo "Result: {$data}\n";
}

onFinish回调中,会接收到Task任务的处理结果$data。在这里处理这个返回结果即可。

swoole_client

在这里讲解如何使用swoole_client是因为,在写服务端代码的时候,不可避免的需要用到客户端来进行测试。
swoole提供了swoole_client用于编写测试客户端,下面我将讲解如何使用这个工具。

swoole_client有两种工作模式:同步阻塞模式异步回调模式

其中,同步阻塞模式在上一章中已经给出示例,其使用和一般的socket基本无异。
因此,我将重点讲解swoole_client异步回调模式

  • 创建一个异步client的代码如下:
1
$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);

其中,SWOOLE_SOCK_ASYNC选项即表明创建一个异步client。

既然是异步,那当然需要回调函数。swoole_client一共有四个回调函数,如下:

1
2
3
4
5
6
7
8
9
10
11
12
$client->on("connect", function ($cli) {
$cli->send("hello world\n");
});
$client->on("receive", function ($cli, $data) {
echo "Received: ".$data."\n";
});
$client->on("error", function ($cli) {
echo "Connect failed\n";
});
$client->on("close", function ($cli) {
echo "Connection close\n";
});

这几个回调函数的作用基本和swoole_server类似,只有参数不同,因此不再赘述。

Timer计时器、心跳检测、Task进阶

Timer定时器

在实际应用中,往往会遇到需要每隔一段时间重复做一件事,比如心跳检测、订阅消息、数据库备份等工作。
通常,我们会借助PHP的time()以及相关函数自己实现一个定时器,或者使用crontab工具来实现。
但是,自定义的定时器容易出错,而使用crontab则需要编写额外的脚本文件,无论是迁移还是调试都比较麻烦。

因此,Swoole提供了一个内置的Timer定时器功能,通过函数addtimer即可在Swoole中添加一个定时器,
该定时器会在建立之后,按照预先设定好的时间间隔,每到对应的时间就会调用一次回调函数onTimer通知Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

$this->serv->on('Timer', array($this, 'onTimer'));

public function onWorkerStart($serv, $worker_id)
{
// 在Worker进程开启时绑定定时器
// 只有当worker_id为0时才添加定时器,避免重复添加
if ($worker_id == 0) {
$serv->addTimer(500);
$serv->addTimer(1000);
$serv->addTimer(1500);
}
}

public function onTimer($serv, $interval)
{
switch ($interval) {
case 500: {
echo "Do Thing A at interval 500\n";
break;
}
case 1000: {
echo "Do Thing B at interval 1000\n";
break;
}
case 1500: {
echo "Do Thing C at interval 1500\n";
break;
}
}
}

可以看到,在onWorkerStart回调函数中,通过addtimer添加了三个定时器,时间间隔分别为50010001500
而在onTimer回调中,正好通过间隔的不同来区分不同的定时器回调,从而执行不同的操作。

需要注意的是,在上述示例中,当1000ms的定时器被触发时,500ms的定时器同样会被触发
但是不能保证会在1000ms定时器前触发还是后触发,因此需要注意,定时器中的操作不能依赖其他定时器的执行结果。

心跳检测

使用Timer定时器功能可以实现发送心跳包的功能。事实上,Swoole已经内置了心跳检测功能,能自动close掉长时间没有数据来往的连接。
而开启心跳检测功能,只需要设置heartbeat_check_intervalheartbeat_idle_time即可。如下:

1
2
3
4
5
6
$this->serv->set(
array(
'heartbeat_check_interval' => 60,
'heartbeat_idle_time' => 600,
)
);

Task进阶:MySQL连接池

PHP中,访问MySQL数据库往往是性能提升的瓶颈。而MySQL连接池我想大家都不陌生,这是一个很好的提升数据库访问性能的方式。
传统的MySQL连接池,是预先申请一定数量的连接,每一个新的请求都会占用其中一个连接,请求结束后再将连接放回池中,
如果所有连接都被占用,新来的连接则会进入等待状态。

知道了MySQL连接池的实现原理,那我们来看如何使用Swoole实现一个连接池。
首先,Swoole允许开启一定量的Task Worker进程,我们可以让每个进程都拥有一个MySQL连接,并保持这个连接,这样,我们就创建了一个连接池。

其次,设置swooledispatch_mode抢占模式(主进程会根据Worker的忙闲状态选择投递,
只会投递给处于闲置状态的Worker)。这样,每个task都会被投递给闲置的Task Worker
这样,我们保证了每个新的task都会被闲置的Task Worker处理,如果全部Task Worker都被占用,则会进入等待队列。

下面直接上关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public function onWorkerStart($serv, $worker_id)
{
echo "onWorkerStart\n";

// 判定是否为Task Worker进程
if ($worker_id >= $serv->setting['worker_num']) {
$this->pdo = new PDO(
"mysql:host=localhost;port=3306;dbname=Test",
'root',
'123456',
array(
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8';",
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => true
)
);
}
}

首先,在每个Task Worker进程中,创建一个MySQL连接。这里我选用了PDO扩展。

1
2
3
4
5
6
7
8
9
public function onReceive(swoole_server $serv, $fd, $from_id, $data) {
$sql = array(
'sql' => 'select * from Test where pid > ?',
'parm' => array(0),
'fd' => $fd
);

$serv->task(json_encode($sql));
}

其次,在需要的时候,通过task函数投递一个任务(也就是发起一次SQL请求)

1
2
3
4
5
6
7
8
9
10
11
12
public function onTask($serv,$task_id,$from_id, $data)
{
$sql = json_decode( $data , true );

$statement = $this->pdo->prepare($sql['sql']);
$statement->execute($sql['param']);

$result = $statement->fetchAll(PDO::FETCH_ASSOC);
$serv->send( $sql['fd'],json_encode($result));

return true;
}

最后,在onTask回调中,根据请求过来的SQL语句以及相应的参数,发起一次MySQL请求,
并将获取到的结果通过send发送给客户端(或者通过return返回给Worker进程)。
而且,这样的一次MySQL请求还不会阻塞Worker进程,Worker进程可以继续处理其他的逻辑。

Swoole多端口监听、热重启以及Timer进阶:简单crontab

多端口监听

1
2
3
4
$serv = new swoole_server("192.168.1.1", 9501);     // 监听外网的9501端口
$serv->addlistener("127.0.0.1", 9502 , SWOOLE_TCP); // 监听本地的9502端口

$serv->start(); // addlistener必须在start前调用

此时,swoole_server就会同时监听两个host下的两个端口。这里要注意的是,
来自两个端口的数据会在同一个onReceive中获取到,这时就要用到swoole的另一个成员函数connection_info
通过这个函数获取到fdfrom_port,就可以判定消息的类型

1
2
3
4
5
6
7
8
9
$info = $serv->connection_info($fd, $from_id);
//来自9502的内网管理端口
if($info['from_port'] == 9502) {
$serv->send($fd, "welcome admin\n");
}
//来自外网
else {
$serv->send($fd, 'Swoole: '.$data);
}

服务器热重启

所谓热重启,就是当服务器相关代码有所变动之后,无需停止服务,而是在服务器仍然运行的状态下更新文件。

Swoole通过内置的reload函数以及两个自定义信号量实现了这一功能

Swoole可用的三个信号:SIGTERMSIGUSR1SIGUSR2

  • SIGTERM用于停止服务器
  • SIGUSR1用于重启全部的Worker进程
  • SIGUSR2用于重启全部的Task Worker进程

那要如何实现热更新代码文件呢?

Swoole的回调函数中有这个一个回调onWorkerStart;该回调会在Worker进程启动时被调用。
因此,当swoole_server收到SIGUSR1信号并重启全部Worker进程后,
onWorkerStart就会被调用。如果在onWorkerStartrequire全部的代码文件,
每次onWorkerStart后都会重新require一次php文件,这样就能实现代码文件的热更新。

1
2
3
4
5
6
7
8
9
public function onStart( $serv )
{
cli_set_process_title("reload_master");
}
public function onWorkerStart( $serv , $worker_id)
{
require_once "reload_page.php";
Test(); // reload_page.php中定义的一个函数
}

首先,在onStart回调函数中通过php的cli_set_process_title函数设置进程名。
onWorkerStart中require相关的php文件。 然后,新建一个reload.sh文件,输入如下内容:

1
2
3
4
5
echo "Reloading..."
cmd=$(pidof reload_master)

kill -USR1 "$cmd"
echo "Reloaded"

这样,就可以通过执行这个脚本重启服务器了

Timer补充:after函数

swoole-1.7.7stable版本中,Timer新增了一个函数after
该函数的作用是在指定的时间间隔后执行回调函数,并且只执行一次。

1
$serv->after( 1000 , array($this, 'onAfter') , $str );

这里指定在1000ms后,执行onAfter回调函数,函数参数为$str

Swoole的自定义协议功能的使用

为什么要提供自定义协议 ?

熟悉TCP通信的朋友都会知道,TCP是一个流式协议。客户端向服务器发送的一段数据,可能并不会被服务器一次就完整的收到;
客户端向服务器发送的多段数据,可能服务器一次就收到了全部的数据。

而实际应用中,我们希望在服务器端能一次接收一段完整的数据,不多也不少

传统的TCP服务器中,往往需要由程序员维护一个缓存区,先将读到的数据放进缓存区中,然后再通过预先设定好的协议内容,来区分一段完整数据的开头、长度和结尾,
并将一段完整的数据交给逻辑部分处理。这就是自定义协议的功能。

而在Swoole中,已经在底层实现了一个数据缓存区,并内置好了几个常用的协议类型,
直接在底层做好了数据的拆分,保证了在onReceive回调函数中,一定能收到一个(或数个)完整的数据段。
数据缓存区的大小可以通过配置选项package_max_length来控制。

下面我就将讲解如何使用这些内置协议。

EOF标记型协议

第一个比较常用的协议就是EOF标记协议。协议的内容是通过规定一个一定不会出现在正常数据中的字符或者字符串,
用这个来标记一段完整数据的结尾
这样,只要发现这个结尾,就可以认定之前的数据已经结束,可以开始接收一个新的数据段了。

Swoole中,可以通过open_eof_checkpackage_eof两个配置项来开启。
其中,open_eof_check指定开启了EOF检测,package_eof指定了具体的EOF标记

通过这两个选项,Swoole底层就会自动根据EOF标记来缓存和拆分收到的数据包

1
2
3
4
5
$this->serv->set(array(
'package_max_length => 8192,
'open_eof_check' => true,
'package_eof' => "\r\n"
));

就这样,swoole就已经开启了EOF标记协议的解析。那么让我们来测试一下效果:

  • 服务器
1
2
3
4
5
// server
public function onReceive(swoole_serve $serv, $fd, $from_id, $data)
{
echo "Get Message From Client {$fd}:{$data}\n";
}
  • 客户端
1
2
3
4
5
6
7
$msg_eof = "This is a Msg\r\n";

$i = 0;
while($i < 100) {
$this->client->send($msg_eof);
$i ++;
}

然后运行一下,你会发现:哎不对啊,为什么还是一次收到了好多数据啊!
这是因为,在Swoole中,采用的不是遍历识别的方法,而只是简单的检查每一次接收到的数据的末尾是不是定义好的EOF标记。因此,在开启EOF检测后,onReceive回调中还是可能会一次收到多个数据包。
这要怎么办?你会发现,虽然是多个数据包,但是实际上收到的是N个完整的数据片段,那就只需要根据EOF把每个包再拆出来,一个个处理就好啦。

  • 修改后的服务器端代码如下:
1
2
3
4
5
6
7
8
9
10
// server
public function onReceive(swoole_serve $serv, $fd, $from_id, $data)
{
$data_list = explode("\r\n", $data);
foreach($data_list as $msg) {
if (!empty($msg) {
echo "Get Message From Client {$fd}:{$data}\n";
}
}
}

固定包头类型协议

固定包头协议是在实际应用中最常用的协议。
该协议的内容是规定一个固定长度的包头,在包头的固定位置有一个指定好的字段存放了后续数据的实际长度
这样,服务器端可以先读取固定长度的数据,从中提取出长度,然后再读取指定长度的数据,即可获取一段完整的数据。

在Swoole中,同样提供了固定包头的协议格式。
需要注意的是,Swoole只允许二进制形式的包头,因此,需要使用packunpack来打包、解包。

通过设置open_length_check选项,即可打开固定包头协议解析功能
。此外还有package_length_offsetpackage_body_offsetpackage_length_type三个配置项用于控制解析功能。

  • package_length_offset规定了包头中第几个字节开始是长度字段,
  • package_body_offset规定了包头的长度,
  • package_length_type规定了长度字段的类型。
1
2
3
4
5
6
7
$this->serv->set(array(
'package_max_length' => 8192,
'open_length_check' => true,
'package_length_offset' => 0,
'package_body_offset' => 4,
'package_length_type' => 'N'
));

实例

  • 服务器端
1
2
3
4
5
6
7
8
public function onReceive(swoole_server $serv, $fd, $from_id, $data)
{
$length = unpack("N", $data)[1];
echo "Length = {$length}\n";
$msg = substr($data, -$length);

echo "Get Message From Client {$fd}:{$msg}\n";
}
  • 客户端
1
2
3
4
5
6
7
$msg_lenght = pack("N", strlen($msg_normal)).$msg_normal;

$i = 0;
while($i < 100) {
$this->client->send($msg_lengt);
$i++;
}

特别篇:Http协议-Swoole内置的http_server

Swoole-1.7.7-stable开始,Swoole在内部封装并实现了一个Http服务器。
是的,没错,再也不用在PHP层缓存和解析http协议了,Swoole直接内置Http服务器了。

  • swoole_http_server
1
2
3
4
5
$http = new swoole_http_server("127.0.0.1", 9501);
$http->on('request', function (swoole_http_request $request, swoole_http_response $response) {
$response->end("<h1>Hello Swoole.</h1>");
});
$http->start();

只需创建一个swoole_http_server对象并设置onRequest回调函数,即可实现一个http服务器。

在onRequest回调中,共有两个参数。

  • 参数$request存放了来自客户端的请求,包括Http请求的头部信息、Http请求相关的服务器信息、Http请求的GETPOST参数以及HTTP请求携带的COOKIE信息。
  • 参数$response用于发送数据给客户端,可以通过该参数设置HTTP响应的Header信息、cookie信息和状态码。

此外,swoole_http_server还提供WebSocket功能,使用此功能需要设置onMessage回调函数,如下:

1
2
3
4
$http_server->on('message', function(swoole_http_request $request, swoole_http_response $response) {
echo $request->message;
$response->message(json_encode(array("data1", "data2")));
})

通过$request->message获取WebSocket发送来的消息,再通过$response->message()回复消息即可。

参考

Powered by Hexo and Hexo-theme-hiker

Copyright © 2017 - 2023 Keep It Simple And Stupid All Rights Reserved.

访客数 : | 访问量 :