• Process
    • 使用实例

    Process

    1.7.2版本增加了一个进程管理模块,用来替代PHPpcntl

    需要注意Process进程在系统是非常昂贵的资源,创建进程消耗很大。另外创建的进程过多会导致进程切换开销大幅上升。可以使用vmstat指令查看操作系统每秒进程切换的次数。

    1. vmstat 1 1000
    2. procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
    3. r b swpd free buff cache si so bi bo in cs us sy id wa st
    4. 0 0 0 8250028 509872 4061168 0 0 10 13 88 86 1 0 99 0 0
    5. 0 0 0 8249532 509872 4061936 0 0 0 0 451 1108 0 0 100 0 0
    6. 0 0 0 8249532 509872 4061884 0 0 0 0 684 1855 1 3 95 0 0
    7. 0 0 0 8249532 509880 4061876 0 0 0 16 492 1332 0 0 99 0 0
    8. 0 0 0 8249532 509880 4061844 0 0 0 0 379 893 0 0 100 0 0
    9. 0 0 0 8249532 509880 4061844 0 0 0 0 440 1116 0 0 99 0 0

    PHP自带的pcntl,存在很多不足,如:

    • pcntl没有提供进程间通信的功能
    • pcntl不支持重定向标准输入和输出
    • pcntl只提供了fork这样原始的接口,容易使用错误
    • swoole_process提供了比pcntl更强大的功能,更易用的API,使PHP在多进程编程方面更加轻松。
      Swoole\Process提供了如下特性:

    • 基于Unix Socketsysvmsg消息队列的进程间通信,只需调用write/read或者push/pop即可

    • 支持重定向标准输入和输出,在子进程内echo不会打印屏幕,而是写入管道,读键盘输入可以重定向为管道读取数据
    • 配合Event模块,创建的PHP子进程可以异步的事件驱动模式
    • 提供了exec接口,创建的进程可以执行其他程序,与原PHP父进程之间可以方便的通信

    使用实例

    • 子进程异常退出时,自动重启
    • 主进程异常退出时,子进程会继续执行,完成所有任务后退出
    1. (new class{
    2. public $mpid=0;
    3. public $works=[];
    4. public $max_precess=1;
    5. public $new_index=0;
    6. public function __construct(){
    7. try {
    8. swoole_set_process_name(sprintf('php-ps:%s', 'master'));
    9. $this->mpid = posix_getpid();
    10. $this->run();
    11. $this->processWait();
    12. }catch (\Exception $e){
    13. die('ALL ERROR: '.$e->getMessage());
    14. }
    15. }
    16. public function run(){
    17. for ($i=0; $i < $this->max_precess; $i++) {
    18. $this->CreateProcess();
    19. }
    20. }
    21. public function CreateProcess($index=null){
    22. $process = new swoole_process(function(swoole_process $worker)use($index){
    23. if(is_null($index)){
    24. $index=$this->new_index;
    25. $this->new_index++;
    26. }
    27. swoole_set_process_name(sprintf('php-ps:%s',$index));
    28. for ($j = 0; $j < 16000; $j++) {
    29. $this->checkMpid($worker);
    30. echo "msg: {$j}\n";
    31. sleep(1);
    32. }
    33. }, false, false);
    34. $pid=$process->start();
    35. $this->works[$index]=$pid;
    36. return $pid;
    37. }
    38. public function checkMpid(&$worker){
    39. if(!swoole_process::kill($this->mpid,0)){
    40. $worker->exit();
    41. // 这句提示,实际是看不到的.需要写到日志中
    42. echo "Master process exited, I [{$worker['pid']}] also quit\n";
    43. }
    44. }
    45. public function rebootProcess($ret){
    46. $pid=$ret['pid'];
    47. $index=array_search($pid, $this->works);
    48. if($index!==false){
    49. $index=intval($index);
    50. $new_pid=$this->CreateProcess($index);
    51. echo "rebootProcess: {$index}={$new_pid} Done\n";
    52. return;
    53. }
    54. throw new \Exception('rebootProcess Error: no pid');
    55. }
    56. public function processWait(){
    57. while(1) {
    58. if(count($this->works)){
    59. $ret = swoole_process::wait();
    60. if ($ret) {
    61. $this->rebootProcess($ret);
    62. }
    63. }else{
    64. break;
    65. }
    66. }
    67. }
    68. });