小坚的技术博客

swoole学习笔记

本文作者:陈进坚
个人博客:https://jian1098.github.io
CSDN博客:https://blog.csdn.net/c_jian
简书:https://www.jianshu.com/u/8ba9ac5706b6
联系方式:jian1098@qq.com

环境依赖

  • 仅支持Linux,FreeBSD,MacOS,3类操作系统
  • Linux内核版本2.3.32以上
  • PHP5.3.10以上版本,包括PHP7
  • gcc4.4以上版本或者clang
  • cmake2.4+,编译为libswoole.so作为C/C++库时需要使用cmake

下载

安装

方法一:pecl install swoole

方法二:依次执行下面的命令

1
2
3
4
5
cd swoole
phpize
./configure
make
sudo make install

安装成功后需要添加php扩展,修改php.ini加入

1
extension=swoole.so

通过phpinfo()查看到的信息

安装报错解决

  • make或make install无法执行或编译错误

    1
    2
    3
    4
    5
    NOTICE: PHP message: PHP Warning: PHP Startup: swoole: Unable to initialize module
    Module compiled with module API=20090626
    PHP compiled with module API=20121212
    These options need to match
    in Unknown on line 0

    原因:php版本和编译时使用的phpize和php-config不对应,需要使用绝对路径来进行编译。使用绝对路径执行PHP。

    1
    2
    3
    local/php-5.4.17/bin/phpize
    ./configure --with-php-config=/usr/local/php-5.4.17/bin/php-config
    /usr/local/php-5.4.17/bin/php server.php
  • 缺少pcre.h头文件

    1
    fatal error: pcre.h: No such file or directory

    原因:缺少pcre,需要安装libpcre

  • Cannot find autoconf

    原因:没有安装autoconf

创建服务端实例

示例:

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
42
43
44
45
46
47
// Server
class Server
{
private $serv;

public function __construct() {

//TCP 服务器:new Swoole\Server('127.0.0.1', 9501);
//UDP 服务器:new Swoole\Server('127.0.0.1', 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP);
//HTTP 服务器:new Swoole\Http\Server('0.0.0.0', 9501);
//WebSocket 服务器:new Swoole\WebSocket\Server('0.0.0.0', 9502);

$this->serv = new Swoole\Server("0.0.0.0", 9501);
$this->serv->set(array(
'worker_num' => 8, //worker进程数
'daemonize' => false, //作为守护进程运行
'max_request' => 10000, //每个worker进程允许处理的最大任务数
'dispatch_mode' => 2, //数据包分发worker策略 1 => 轮循模式2 => 固定模式3 => 抢占模式
'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基本分三步:

  • 创建swoole_server对象

  • 设置swoole_server的相关配置

  • 调用on函数设置相关回调函数

    onStart回调在server运行前被调用,onConnect在有新客户端连接过来时被调用,onReceive函数在有数据发送到server时被调用,onClose在有客户端断开连接时被调用

配置选项请参考:https://www.w3cschool.cn/swoole/swoole-setting.html

创建客户端实例

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();

异步任务

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
   /**
* 接收到数据时回调此函数,发生在worker进程中
* $server,swoole_server对象
* $fd,TCP客户端连接的文件描述符
* $from_id,TCP连接所在的Reactor线程ID
* $data,收到的数据内容,可能是文本或者二进制内容
*/
public function onReceive($serv, $fd, $from_id, $data ) {
$str = "=========== onReceive ============ \n";
$str .= "Get Message From Client $fd:$data \n";
error_log($str, 3, $this->logFile);
$serv->task( $data ); //调用 $serv->task() 后,程序立即返回,继续向下执行代码。onTask 回调函数 Task 进程池内被异步执行。执行完成后调用 $serv->finish() 返回结果。
}

/**
* 在task_worker进程内被调用。worker进程可以使用swoole_server_task函数向task_worker进程投递新的任务。当前的Task进程在调用onTask回调函数时会将进程状态切换为忙碌,
* 这时将不再接收新的Task,当onTask函数返回时会将进程状态切换为空闲然后继续接收新的Task。
* $task_id是任务ID,由swoole扩展内自动生成,用于区分不同的任务。$task_id和$src_worker_id组合起来才是全局唯一的,不同的worker进程投递的任务ID可能会有相同
* $src_worker_id来自于哪个worker进程
* $data 是任务的内容
*/
public function onTask($serv, $task_id, $src_worker_id, $data) {
$data = trim($data);  // 删除EOF
$array = json_decode( $data , true );
$str = "=========== onTask ============ \n";
$str .= var_export($array, 1);
error_log($str, 3 , $this->logFile);
return $array;
}

/**
* 当worker进程投递的任务在task_worker中完成时,task进程会通过swoole_server->finish()方法将任务处理的结果发送给worker进程
* $task_id是任务的ID
* $data是任务处理的结果内容(也就是onTask()函数,中return的值)
*/
public function onFinish($serv, $task_id, $data) {
$str = "=========== onFinish ============ \n";
$str .= "Task $task_id finish ! \n";
$str .= var_export($data, 1);
error_log($str, 3, $this->logFile);
}

编程注意事项

  • 不要在代码中执行sleep以及其他睡眠函数,这样会导致整个进程阻塞
  • exit/die是危险的,会导致worker进程退出
  • 可通过register_shutdown_function来捕获致命错误,在进程异常退出时做一些请求工作
  • PHP代码中如果有异常抛出,必须在回调函数中进行try/catch捕获异常,否则会导致工作进程退出
  • Worker进程不得共用同一个Redis或MySQL等网络服务客户端,Redis/MySQL创建连接的相关代码可以放到onWorkerStart回调函数中。

参考文章

https://www.w3cschool.cn/swoole

-------------本文结束感谢您的阅读-------------
🐶 您的支持将鼓励我继续创作 🐶