swoole使用goole grpc报错

 

 

Php的swoole和grpc扩展一起使用时kill不能杀死swoole进程的问题 Grpc的fork模式问题 

  1. 我的环境是lumen+swoole
  2. 遇到的第一个问题就是、php bin/laravels reload 会把master进程搞挂
  3. 排除到是因为安装了grpc扩展影响的
  4. August 05, 2020 | 2 Minute Read

    发现swoole的一个服务,用普通的kill总是杀不死,最后还是留一个进程在那里非要用 kill -9 才能结束掉。 看了一下,进程是卡死在 grpc shutdown的信号量上面了。

    #0  0x00007f533454d48c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
    #1  0x00007f53301ada52 in gpr_cv_wait () from /lib64/libgrpc.so.11
    #2  0x00007f533015745b in ?? () from /lib64/libgrpc.so.11
    #3  0x00007f533018e060 in grpc_shutdown_internal_locked() () from /lib64/libgrpc.so.11
    #4  0x00007f533018e34c in grpc_shutdown_blocking () from /lib64/libgrpc.so.11
    #5  0x00007f53304f2665 in zm_shutdown_grpc () from  grpc.so
    #6  0x00000000007011db in module_destructor ()
    #7  0x00000000006fb86c in module_destructor_zval ()
    #8  0x000000000070bbd4 in zend_hash_graceful_reverse_destroy ()
    #9  0x00000000006fc6a3 in zend_shutdown ()
    #10 0x000000000069f21a in php_module_shutdown ()
    #11 0x0000000000469ac4 in main ()
    

    https://github.com/grpc/grpc/issues/18833 这个理由提到 要改php.ini模式启动fork模式的支持。

    grpc.enable_fork_support = 1
    grpc.poll_strategy = epoll1
    

    但这么改之后,启动的时候就死锁了,

    #  pstack 7009
    #0  0x00007f48bc31e48c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
    #1  0x00007f48b701ba52 in gpr_cv_wait () from /lib64/libgrpc.so.11
    #2  0x00007f48b6fc545b in ?? () from /lib64/libgrpc.so.11
    #3  0x00007f48b6ffc060 in grpc_shutdown_internal_locked() () from /lib64/libgrpc.so.11
    #4  0x00007f48b6ffc34c in grpc_shutdown_blocking () from /lib64/libgrpc.so.11
    #5  0x00007f48b7360841 in postfork_child () from grpc.so
    #6  0x00007f48bc639b10 in __run_fork_handlers () from /lib64/libc.so.6
    #7  0x00007f48bc5f838c in fork () from /lib64/libc.so.6
    #8  0x00007f48b812530d in swoole_fork () from swoole.so
    #9  0x00007f48b815897f in swManager_start(swServer*) () from swoole.so
    #10 0x00007f48b8160e83 in swFactoryProcess_start(swFactory*) () from swoole.so
    #11 0x00007f48b815cc0b in swServer_start(swServer*) () from swoole.so
    #12 0x00007f48b81cedd0 in zim_swoole_server_start(_zend_execute_data*, _zval_struct*) () from swoole.so
    #13 0x000000000077d7c8 in execute_ex ()
    #14 0x000000000077e323 in zend_execute ()
    #15 0x00000000006fcb24 in zend_execute_scripts ()
    #16 0x000000000069f520 in php_execute_script ()
    #17 0x00000000007803b3 in do_cli ()
    #18 0x0000000000469a68 in main ()
    

    还是卡在 postfork_child 后面的 grpc_shutdown_blocking函数上面,也就是这个fork模式grpc的工作有些问题。

    搜索了一下,这个grpc的fork模式确实很多问题: https://github.com/swoole/swoole-src/issues/2604 gRPC 跨进程使用引发的问题 https://zhuanlan.zhihu.com/p/136619485 https://github.com/grpc/grpc/blob/master/doc/fork_support.md https://github.com/grpc/grpc/issues/15334 https://github.com/grpc/grpc//src/php/README.md https://github.com/grpc/grpc/blob/master/src/php/README.md https://github.com/grpc/grpc/issues/13412 https://github.com/grpc/grpc/issues/20250 https://github.com/grpc/grpc/pull/22774

    因为swoole本身就是fork子进程的,所以刚好跟grpc有冲突吗。这个看代码,像是grpc fork后,有些条件变量状态不对了,shutdown流程死锁了。看堆栈看出来是 grpc_shutdown_internal_locked 调用的哪个子函数调用这个gpr_cv_wait。

    简单的把grpc_shutdown_blocking函数给注释掉了,是不会卡死了,但不知道有没有什么负面作用。看python那边postfork的逻辑好像也没有shutdown的代码,只是删除channel避免 子进程里面使用父进程的channel吧。不知道这么改grpc在子进程里面还能正常工作不,还有待测试。这个正确的做法是找到这个信号量然后看grpc的代码逻辑来修复,但工作量太大了。

    void postfork_child() {
      TSRMLS_FETCH();
    
      // loop through persistent list and destroy all underlying grpc_channel objs
      destroy_grpc_channels();
    
      release_persistent_locks();
      
      // clean all channels in the persistent list
      php_grpc_clean_persistent_list(TSRMLS_C);
    
      // clear completion queue
      grpc_php_shutdown_completion_queue(TSRMLS_C);
    
      // clean-up grpc_core
      /*
      grpc_shutdown_blocking();
      if (grpc_is_initialized() > 0) {
        zend_throw_exception(spl_ce_UnexpectedValueException,
                             "Oops, failed to shutdown gRPC Core after fork()",
                             1 TSRMLS_CC);
      }
       */
      // restart grpc_core
      grpc_init();
      grpc_php_init_completion_queue(TSRMLS_C);
    }
    

    20202-08-21补充: 有人开了一个帖子了,应该是类似的问题 https://github.com/grpc/grpc/issues/23833

    20202-09-07: 另外一个感觉更好的解决方法是,最开始php.ini 里面不要加载grpc.so和扩展和配置grpc.enable_fork_support ,在swoole的worker进程启动后再使用的php的dl函数动态加载grpc.so extension, 这样测试也没有出现死锁的情况。

    function onWorkerStart($server, $worker_id)
    {
        if (!extension_loaded('grpc')) {
            if (dl('grpc.so')) {
                error_log("load grpc.so successfully");
                include_once(dirname(__FILE__)."/grpc_header.php");
            } else {
                error_log("failed to load grpc.so");
            }
        } else {
            error_log("grpc.so has been loaded");
        }
        
         $server->tick(300000, function ($id) {
                G::$serv->task(TaskType::TIMER_TICK);
         });
        
    }
    

    另外swoole的定时器,不用使用在全局的地方使用 swoole_timer_tick() 这个函数来设置定时器,这个是使用alarm信号来实现的,应该用 $server->tick 的这个epoll的timer实现的。 不然也会出现 kill -15 主进程, 主进程没有处理信号退出的情况。

    2020-10-10补充,看上去 官方在 https://github.com/grpc/grpc/pull/24364/commits/8e9e895ffc530a9db678e932226d9cd8cd6436a5 这个commit 修复了这个问题。就把src/php/ext/grpc/php_grpc.c文件里面的调用grpc_shutdown_blocking 函数全部改为grpc_shutdown函数。

    Let's remove it because grpc_shutdown now can execute synchronously if it's possible. grpc_shutdown_blo

解决方案

  1. config/laravels.php
    1. 增加
    2. swoole使用goole grpc报错

       

       swoole使用goole grpc报错

       

       

  2. 将 php.ini  extension=grpc.so 注释
  3. 修改 php.ini  enable_dl = On
  4. 增加 php.ini  grpc.enable_fork_support = 1
    grpc.poll_strategy = epoll1     

     

       
上一篇:C语言课程心得(四)


下一篇:Linux下启动Tomcat无法访问8080并且执行./shutdown.sh抛出异常