进程、协程

  • 进程 > 线程 > 协程
  • fpm框架使得整个程序是依托于nginx+cgi,和php脚本没太大的关系,在程序未被访问的时候担任的角色和普通静态网站差不多。但是基于swoole的http服务使得程序有了生命周期,整个程序的允许全部依赖于php脚本,类似springboot部署时候以java -jar启动时,开启一个新的进程。
  • 传统的php-fpm框架是基于cgi开启多个fpm进程,比如设置了20个 那么这个程序只能同时拥有20个IO阻塞,其他的请求将会一直处于等待状态
  • 协程实现了用同步的写法实现异步的代码,这就导致如果有大量的并发或者IO操作他会对并发的支持非常好,只要开启一个新的协程就行了

    几个提示区

  • 在一个协程内的代码是同步阻塞的,一个协程相对于另一个协程是异步的

  • 协程的整块代码相对于别的代码是异步的
  • swoole4+实现了一键协程化,也就是说没一次对于mysql、redis…的操作都是开启了一个协程
  • 在基于swoole的协程框架中(hyperf、easyswoole等),每次的请求都是一个协程,业务方法内的代码也是协程化的,但是排除阻塞代码后其他代码都是同步执行的
  • 如果是父子协程,子协程的代码对于父协程来说也是异步的(见下文)

    协程客户端

    其实就是对于mysql、redis的操作异步化了,每一次的增删改查都会去开启一个协程。大量的回调函数会让代码非常杂乱,所以4+版本支持一键协程化。只需要按往常写法就行

官方示例:

  1. Co::set(['hook_flags' => SWOOLE_HOOK_TCP]);
  2. $http = new Swoole\Http\Server("0.0.0.0", 9501);
  3. $http->set(['enable_coroutine' => true]);
  4. $http->on('request', function ($request, $response) {
  5. $redis = new Redis();
  6. $redis->connect('127.0.0.1', 6379);//此处产生协程调度,cpu切到下一个协程(下一个请求),不会阻塞进程
  7. $redis->get('key');//此处产生协程调度,cpu切到下一个协程(下一个请求),不会阻塞进程
  8. });
  9. $http->start();

如官方所说,其实每次的IO操作,底层都会开启一个新的协程,让cpu来处理其他的协程。

父子协程

子协程的代码对于父协程来说是异步的

  1. public function test3() {
  2. Coroutine::create(function () {
  3. for($i=0;$i<10;$i++){
  4. Coroutine::create(function () use ($i) {
  5. sleep(1);
  6. echo $i . PHP_EOL;
  7. });
  8. }
  9. echo 'parent';
  10. });
  11. echo 'hello';
  12. return Coroutine::id();
  13. }

以上代码方法内首先创建了一个协程,然后在协程内部新建了50个协程,但是最后输出的结果是所有子协程执行完成才输出的

输出结果:

  1. parenthello0
  2. 9
  3. 8
  4. 7
  5. 6
  6. 5
  7. 4
  8. 3
  9. 2
  10. 1

传统框架对比

对比laravel、tp这种传统fpm框架,swoole类型的框架对于并发支持要好很多。但是如果线上使用还需要多踩坑,与传统框架比开发者有些书写习惯在swoole的框架中可能会不支持,比如die函数等。同时部署也会比以往框架麻烦点。以前只需要git拉下代码、更新composer然后配合nginx就可以访问了,swoole如果在线上部署要注意进程守护的问题,不能让进程宕掉,推荐使用docker方式部署。然后再通过nginx代理访问即可。