关于swoole的一些理解

196 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

进程、协程

  • 进程 > 线程 > 协程
  • 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+版本支持一键协程化。只需要按往常写法就行

官方示例:

Co::set(['hook_flags' => SWOOLE_HOOK_TCP]);

$http = new Swoole\Http\Server("0.0.0.0", 9501);
$http->set(['enable_coroutine' => true]);

$http->on('request', function ($request, $response) {
      $redis = new Redis();
      $redis->connect('127.0.0.1', 6379);//此处产生协程调度,cpu切到下一个协程(下一个请求),不会阻塞进程
      $redis->get('key');//此处产生协程调度,cpu切到下一个协程(下一个请求),不会阻塞进程
});

$http->start();

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

父子协程

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

public function test3() {

    Coroutine::create(function ()  {
      for($i=0;$i<10;$i++){
        Coroutine::create(function () use ($i) {
          sleep(1);
          echo $i . PHP_EOL;
        });
      }
      echo 'parent';
    });
    echo 'hello';
    return Coroutine::id();
}

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

输出结果:

parenthello0
9
8
7
6
5
4
3
2
1

传统框架对比

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