全局变量的危险性
我们知道php-fpm模式是mast主进程->work子进程,一个http请求过来,从index.php开始加载执行,遇到io时挂起等待,直到io执行完成在继续往下执行,整个过程就是按照代码顺序执行的,fpm会通过fastCGI在请求完成时把内存释放掉,因此无论全局还是局部变量都会被销毁。而swoole是内存常驻,全局变量、全局对象在swoole程序全局期swoole生命周期即在swoole_start前声明的,会一直常驻在内存中,swoole中的每个work进程都会存在这些全局变量,因此很容易造成内存溢出,在协程的时候尤为需要注意全局变量的声明和操作。
对于协程的理解
工作原理:
上面提到php-fpm处理请求方式是遇到io时挂起等待,直到io执行完成在继续往下执行请求,在请求还没处理完成前我们上面都不能做,只能等待请求处理完成才能处理下一个请求,因此性能非常局限,例如一台机器开了8个work进程,每个请求io耗时需要0.1秒,那么你最多1秒也只能处理80个请求,单个work只能处理一个请求,cpu利用率极低。而协程的工作方式则是,遇到io时会通过子协程保存整个请求,继续处理下一个请求,如果下一个请求同样遇到io也是如此,等子协程处理完了io转回来处理请求,至此完成一个请求,所以在一个work进程中会交替执行多个请求,处理多个任务。由此可以看出,协程是从根本上提高了cpu的利用率,提升了性能。协程的优势:
开发者可以无感知的用同步的代码编写方式达到异步IO的效果和性能,避免了传统异步回调所带来的离散的代码逻辑和陷入多层回调中导致代码无法维护,也就是说,用同步的代码达到异步的效果。
未使用协程前对比:
public function onRequest($request, $response)
{
//连接redis
$client = new swoole_redis;
$client->connect('127.0.0.1', 6379, function (swoole_redis $client, $result) {
if ($result === false) {
echo "connect to redis server failed.\n";
return;
}
$client->set('test', 'swoole', function (swoole_redis $client, $result) {
var_dump($result);
});
});
$response->end("value:".$result);
}
使用协程后:
public function onRequest($request, $response)
{
//连接redis
$redis = new Swoole\Coroutine\Redis();
$redis->connect('127.0.0.1', 6379);
$val = $redis->get('test');
$response->end("value:".$val);
}
总结:协程的创建、切换、挂起、销毁全部为内存操作,消耗是非常低的。同时它是可以并发调用的,通常我们一个业务请求中有redis+MySQL的操作
传统的执行方式为是顺序的:
redis发包->redis收包->mysql发包->mysql收包
以上执行时间:time = redis+mysql
协程的执行方式是:
redis发包->mysql发包->redis收包->mysql收包
以上执行时间:MAX(redis网络IO时间, mysql网络IO时间)
因此协程的执行时间非常短,消耗也非常低