用swoole 写的协程 server能做到高并发,但这有一个问题必须考虑的,多个协程跑在一个进程内,某个协程把所有的内存吃光了,
其它协程怎么办?这个进程会怎么处理?
首先不要管协程这么一回事,php最初设计就是一个脚本引擎,目前php版本实现的是,遇到 内存不足,不是一个 E_RECOVERABLE_ERROR,直接就 调用shutdown 函数了。
当swoole扩展引入协程概念时,还是跑在这个脚本引擎上,
一个协程吃光内存 --触发--> 脚本shutdown --导致--> 进程内所有资源被回收(包括其它协程)
<?php
register_shutdown_function(function() {
echo "register_shutdown_function\n";
});
$var = 'clwu';
try {
new aa;
$var = @str_repeat($var, 1024*1024*1024*1024);
} catch (\Throwable $e) {
echo 'OOM',PHP_EOL;
}
上面的实验代码中,new aa 找不到这个类,php 可以catch 到这个异常,但下一行 str_repeat($var, 1024*1024*1024*1024) 会吃光所有内存,抛出一个 php 没有catch 的致命错误:
1)为什么 new aa 找不到这个类抛出的异常可以 catch ?
因为 new aa 是转换成执行php脚本引擎的代码,引擎逻辑有 判断 找不到这个类就 HANDLE_EXCEPTION()
2)为什么 str_repeat($var, 1024*1024*1024*1024) 吃光内存的异常没有 catch 呢?因为 php 脚本引擎的逻辑就没有写要去检测有没有异常抛出
php脚本引擎把 str_repeat 转换为一个可以 FAST CALL 的外部函数调用(底层libc库的原生代码),脚本引擎在调用完这个函数时,是执行
ZEND_VM_NEXT_OPCODE()
而不是
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION()
,see 脚本引擎的设计时就不知道要怎么处理 外部资源的问题。目前php版本的实现是抛出一个致命的错误然后脚本中止。
了解了问题的根源,就有了问题的解决方案了,我不要改动 php的源码,因为到时 服务器自动更新到新版本php时又会覆盖我的改动,facebook自己维护的HHVM就不能很好的跟上生态。
还是要自己在协程的最开始主动用 memory_get_usage() 检查进程的内存是否还足够,如果不足够就 拒绝服务,以免把其它协程给坑了。