Swoole是一个为PHP用C和C++编写的,基于事件、高性能、异步、协程并行网络通信引擎。Swoole 扩展是按照 PHP Extension标准扩展构建的,安装的方式大体思路就跟安装PHP扩展类似。本节采用PHP官方提供的pecl命令进行安装。
之前多少有听过这个Swoole框架,跟我之前了解的ThinkPHP、CI、Laravel这一类框架非常不同,比较厉害的是基于事件处理,内置异步协程,重新定义PHP,扩展PHP的生命周期。当然我这里说的不是很全面或者完整,只是稍微总结一下最近看到的知识点,之后会逐步了解学习。
基于事件。网络请求是基于事件充分利用到底层epoll/kqueue实现。可以支持大请求。
异步协程。传统的PHP编程都是同步串行的,也就是从上向下,也有可能在函数处理的时候处于堵塞状态,必须执行完后才可以继续向下。不过我们知道前端有Ajax这样的请求方式异步处理,但是当事件越来越多的时候,要解决掉异步之间回调嵌套的过程,这里使用类似 Go 语言的协程。Swoole4 提供内置协程,可以用完全同步的代码来实现异步性能,底层自动调度协程,异步不会受到阻塞。 这里简单说一下进程、线程、协程特点。进程是系统中正在运行的一个程序,每个进程有独立的地址空间,上下文切换,比较耗费资源,所有内容都要换;线程,一个进程可以有多个线程。每个线程使用其所属进程的栈空间,同一个进程内的多个线程都会共享部分状态,当然每个线程也有自己的栈和寄存器。由于没有内存隔离,线程切换一般要进行加锁;协程是程序内部的调度机制,是单进程单线程处理,协程上下文切换开销非常少,而且没有变量冲突,共享资源不需要加锁,只需要判断状态。协程之间是协作式的、串行的、非抢占、无需加锁、上下文切换快。而且如果协程受到阻塞,只是会阻塞某个进程,并且会让出当前控制权,不会阻塞整个进程。
网络通信。Swoole可以提供网络通信的能力,极大地扩展了PHP脚本的生命周期。之前我们PHP开发通常需要有Web服务器的支持,例如Apache或者Nginx,这样我们打开浏览器然后便可以进行发起请求。传统这种形式我们可以看到,从浏览器到服务器之间的网络请求,只需要等候服务器将请求内容给到PHP脚本然后才去处理数据,在请求到来前和请求处理后都和PHP脚本没关系了; 而Swoole的到来,使我们无需PHP-PFM无需Nginx或者Apache便可以启动一个Web服务。并且扩展了PHP的生命周期,从服务启动前、启动后、链接进入、请求到来、请求结束、链接切断、服务中止都可以在PHP脚本的掌握之中。这也正是Swoole提供给PHP的强大的网络通信能力。很强!
其他的特点,暂时还不是很了解,不过已经想迫切尝试一下如何使用Swoole来启动一个HTTP服务,看看是否可以离开Nginx,直接用脚本启动。
当然官网文档中提供了多种安装方式,针对不同系统,都有所介绍。这里我使用Pecl(The PHP Extension Community Library PHP扩展和应用仓库)工具进行安装,当然这个Pecl命令中安装Pwoole的时间会晚于GitHub,初学者可以尝试简单的安装方式,后续可以尝试自己手动编译。
php-7.1
或更高版本gcc-4.8
或更高版本make
autoconf
【前期准备】
// 查看PHP版本 至少php-7.1以上
$ php -v
// 查看Gcc版本 至少gcc-4.8以上
$ gcc -v
// 查看make版本
$ make -v
// 查看有无 autoconf
$ rpm -qa|grep autoconf
【执行安装】
// 使用PHP扩展pecl进行安装
$ pecl install swoole
【正在安装】
// 是否启用 PHP Sockets 支持
// 如果你需要用 PHP 编写 Sockets 服务,可以启用此项。
enable sockets supports? [no] : yes
// 是否启用 OpenSSL 支持
// 是否启用 SSL 加密,如果你是通过 Swoole 提供对外的 HTTPS 服务,则需要启用此项。
// 我的 Mac 是本地开发用,所以不启用。
enable openssl support? [no] : no
// 是否启用 HTTP2 支持
// 了解 HTTP2 新特性 https://zh.wikipedia.org/wiki/HTTP/2
enable http2 support? [no] : yes
// 是否启用异步 Redis 支持
// Swoole 实现了一套支持异步的 Redis 服务端框架
// https://wiki.swoole.com/wiki/page/p-redis_server.html
enable async-redis support? [no] : yes
// 是否启用 MySQL 原生支持
enable mysqlnd support? [no] : yes
【安装通过】
// 结果显示 通过!
Build process completed successfully
Installing '/usr/lib64/php/modules/swoole.so'
Installing '/usr/include/php/ext/swoole/config.h'
install ok: channel://pecl.php.net/swoole-4.5.0
configuration option "php_ini" is not set to php.ini location
You should add "extension=swoole.so" to php.ini
【修改配置PHP.ini】
// 查看安装的版本 我安装的是swoole 4.5版
$ pecl search swoole
// 将下面两行添加扩展swoole.so到php.ini
// 有一个问题php.ini配置文件中扩展很少?
// 是因为linux编译PHP已经写道内核中,所以可以直接使用
$ vim /etc/php.ini
[swoole]
extension=swoole.so
// 重启服务PHP
$ systemctl restart php-fpm
【检测扩展】
// 查找是否有swoole扩展
$ php -m | grep swoole
执行php -m 进行查找扩展。出现报错后也是花费了一些时间,不过对php.ini的配置也更了解一些,报错内容如下。
PHP Warning: PHP Startup: Unable to load dynamic library '/usr/lib64/php/modules/swoole.so' - /usr/lib64/php/modules/swoole.so: undefined symbol: mysqlnd_cset_escape_slashes in Unknown on line 0
通过查看发现是mysqlnd的函数不能识别,那就是需要安装这个扩展。
了解一下mysqlnd。mysqlnd提供了和Zend引擎高度的集成性,更加快速的执行速度, 更少的内存消耗,利用了PHP的Stream API,以及客户端缓存机制。 由于mysqlnd是透过Zend引擎, 因此提供更多高级特性,以及有效利用Zend进行加速。
// 移除扩展
$ yum remove php-mysql
// 选择更改yum源 支持PHP7
$ rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
$ rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm
// 安装mysqlnd扩展
$ yum install php71w-mysqlnd
// 查看PHP扩展
$ php -m | grep mysql
安装之后还是不行,还是报错mysqlnd_cset_escape_slashes in Unknown on line 0。不应该阿,我重启后使用php -m可以找到这个扩展,而且在phpinfo()函数执行结果中也能发现。后来一想,这个报错也就是Swoole是依赖于这个mysqlnd扩展。那会不会是扩展加载先后顺序问题,后来上网搜索了相关博客发现确实。
PHP默认加载的扩展都在 /usr/lib64/php/modules/ 这个目录里在,这里面的 .so 文件是默认加载的,他们已经是封装到PHP内核中。关于这些扩展的配置不是在 /etc/php.ini 中,而是在 /etc/php.d/ 这个目录中,这个目录当中的所有的 .ini 文件是在 /etc/php.ini 加载完成后再加载的,而我们的 extension=swoole.so 的配置是写在 /etc/php.ini 的文件末尾,所以是先加载了 swoole.so 然后再加载 mysqlnd.so。
三步走
【解决bug】
// 更改mysqlnd.ini文件名
$ cd /ect/php.d/
$ mv mysqlnd.ini mysqlnd.ini.bak
// 更改php.ini配置
$ vim /etc/php.ini
// 文件最后加入下面扩展
[extension]
extension=mysqlnd.so
extension=swoole.so
// 重启php-fpm
$ systemctl restart php-fpm
再次执行php -m进行查看有无swoole,发现又报了新的错误。php_sockets_le_socket in Unknown on line 0。socket也出现问题,不过处理方式同上。
【解决bug】
// 更改sockets.ini文件名
$ cd /ect/php.d/
$ mv sockets.ini sockets.ini.bak
// 更改php.ini配置
$ vim /etc/php.ini
// 文件最后加入下面扩展
[extension]
extension=mysqlnd.so
extension=sockets.so
extension=swoole.so
// 重启php-fpm
$ systemctl restart php-fpm
// 大功告成
$ php -m | grep swoole
swoole
主要是想迫不及待尝试一下
# cat http_test.php
on('request', function($request, $response) {
// 当我们请求这个服务器,会打印出请求的内容
var_dump($request->get,$request->post);
// 并且响应对应的内容
$response->header("Content-Type","text/html; charset=utf-8");
$response->end("Hello Swoole! Num is #" . rand(1000,9999). "
");
});
// 执行
$http->start();
$ php http_test.php
当你执行启动服务的时候,会占用当前终端,我们重新启动一个终端,然后发送请求。
$ curl 127.0.0.1:9501
Hello Swoole! Num is #2506
发现得到响应的内容,开森!