🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 实现原理 [TOC] `Swoole-2.0`基于`setjmp`、`longjmp`实现,在进行协程切换时会自动保存`Zend VM`的内存状态(主要是`EG`全局内存和`vm stack`)。 * `setjmp`和`longjmp`主要是用于从`ZendVM`的`C`堆栈跳回`Swoole`的`C`回调函数 * 协程的创建、切换、挂起、销毁全部为内存操作,消耗是非常低的 `Swoole-4.0`重构了协程内核,实现了`C`栈和`PHP`栈同时保存和切换,支持所有`PHP`语法。 ## 示例代码 ~~~ $server = new Swoole\Http\Server('127.0.0.1', 9501, SWOOLE_BASE); #1 $server->on('Request', function($request, $response) { $mysql = new Swoole\Coroutine\MySQL(); #2 $res = $mysql->connect([ 'host' => '127.0.0.1', 'user' => 'root', 'password' => 'root', 'database' => 'test', ]); #3 if ($res == false) { $response->end("MySQL connect fail!"); return; } $ret = $mysql->query('show tables', 2); $response->end("swoole response is ok, result=".var_export($ret, true)); }); $server->start(); ~~~ * 此程序仅启动了一个1个进程,就可以并发处理大量请求。 * 程序的性能基本上与异步回调方式相同,但是代码完全是同步编写的 ## 运行过程 * 调用`onRequest`事件回调函数时,底层会调用C函数`coro_create`创建一个协程(#1位置),同时保存这个时间点的CPU寄存器状态和ZendVM stack信息。 * 调用`mysql->connect`时发生IO操作,底层会调用C函数`coro_save`保存当前协程的状态,包括Zend VM上下文以及协程描述信息,并调用`coro_yield`让出程序控制权,当前的请求会挂起(#2位置) * 协程让出程序控制权后,会继续进入EventLoop处理其他事件,这时Swoole会继续去处理其他客户端发来的Request * IO事件完成后,MySQL连接成功或失败,底层调用C函数`coro_resume`恢复对应的协程,恢复ZendVM上下文,继续向下执行PHP代码(#3位置) * `mysql->query`的执行过程与`mysql->connect`一致,也会进行一次协程切换调度 * 所有操作完成后,调用`end`方法返回结果,并销毁此协程 ## 协程开销 相比普通的异步回调程序,协程多增加额外的内存占用。 * `Swoole4`协程需要为每个并发保存`zend stack`栈内存并维护对应的虚拟机状态。如果程序并发很大可能会占用大量内存,取决于`C`函数、`PHP`函数调用栈深度 * 协程调度会增加额外的一些`CPU`开销,可使用官方提供的[协程切换压测脚本](https://github.com/swoole/swoole-src/blob/master/benchmark/co_switch.php)测试性能 在一台`ThinkPad T470p`笔记本电脑上进行测试切换`1000万次`,需要耗时`1.66s`,平均每次协程切换耗时`165ns~175ns`之间。 ## 压力测试 * 环境:`Ubuntu16.04 + Core I5 4核 + 8G内存 PHP7.0.10` * 脚本:`ab -c 100 -n 10000 http://127.0.0.1:9501/` 测试结果: ~~~ Server Software: swoole-http-server Server Hostname: 127.0.0.1 Server Port: 9501 Document Path: / Document Length: 348 bytes Concurrency Level: 100 Time taken for tests: 0.883 seconds Complete requests: 10000 Failed requests: 168 (Connect: 0, Receive: 0, Length: 168, Exceptions: 0) Total transferred: 4914560 bytes HTML transferred: 3424728 bytes Requests per second: 11323.69 [#/sec] (mean) Time per request: 8.831 [ms] (mean) Time per request: 0.088 [ms] (mean, across all concurrent requests) Transfer rate: 5434.67 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.2 0 2 Processing: 0 9 9.6 6 96 Waiting: 0 9 9.6 6 96 Total: 0 9 9.6 6 96 Percentage of the requests served within a certain time (ms) 50% 6 66% 9 75% 11 80% 12 90% 19 95% 27 98% 43 99% 51 100% 96 (longest request) ~~~