swoole协程

swoole协程

什么是协程

  • 进程: 是系统进行资源分配和调度的基本单位,是操作系统结构的基础(更重,系统调用)

  • 线程: 是程序执行流的最小单元(重,系统调用)

  • 协程: 是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是
    由程序所控制(轻,用户态执行)

异步

  • 异步&同步: 这两个概念的区别与消息的通知机制有关
    例如:当一个任务A的执行依赖于另外一个任务B执行的时候,A必须要等待B通知执行完成了才进行下一步就是同步,而不用等待就去执行其他任务了,只是B执行完了告诉A一声就可以了这是异步.

  • 常见的一些异步

    1. Nginx: io多路复用


注:nginx的work进程在处理io事件时, 都会注册一个epoll对象到epoll树, 就去执行其它的事件, 注册的io事件执行完后会触发注册事件再继续执行

  1. 队列: 发布者 -> 队列(redis/kafka) <- 消费者(常驻内存)拉取

  2. 协程

协程的执行流程

  • 协程容器: 协程执行的上下文环境, 所有的协成程序都必须在协程容器里面执行

  • 协程调度: 决定让 CPU 执行哪个协程的代码的过程就是协程调度

  • swoole的调度策略: 首先,在执行某个协程代码的过程中发现这行代码遇到了 Co::sleep() 或者产生了网络 IO,例如 MySQL->query(),这肯定是一个耗时的过程,Swoole 就会把这个 Mysql 连接的 Fd 放到EventLoop中。
    然后让出这个协程的 CPU 给其他协程使用:即 yield(挂起)
    等待 MySQL 数据返回后就继续执行这个协程:即** resume(恢复)**

Swoole的协程使用(最新版本4X)

  • Swoole启动程序的三种方式
  1. 调用异步风格服务端程序的 start 方法,此种启动方式会在事件回调中创建协程容器,参考enable_coroutine参数
  2. 调用 Swoole 提供的 2 个进程管理模块 Process 和 Process\Pool 的 start 方法,此种启动方式会在进程启动的时候创建协程容器,参考这两个模块构造函数的 enable_coroutine 参数
  3. 其他直接裸写协程的方式启动程序,需要先创建一个协程容器 (Co\run())



    备注:只有在协程容器里才可以使用go()/Coroutine::create创建协程或者swoole自带的协程客户端了,但Swoole 程序启动的时候大部分情况会自动创建协程容器
  • 其它一些基本的api:

    co:: getCid(): 获取当前协程的唯一 ID, 它的别名为 getUid, 是一个进程内唯一的正整数。

    co::yield(): 手动让出当前协程的执行权。而不是基于 IO 的协程调度

    co:: resume: 手动恢复某个协程,使其继续运行,不是基于 IO 的协程调度

    co:: stats(): 获取协程状态

    co:: defer(): 用于资源的释放,会在协程关闭之前 (即协程函数执行完毕时) 进行调用


    api详情地址: https://wiki.swoole.com/#/coroutine/coroutine

  • 一些协程客户端: https://wiki.swoole.com/#/coroutine_client/init

  • 协程间通信:

    channel队列, 协程间的消息队列,多个协程通过 push 和 pop 操作生产消息和消费消息,用来协程之间的通讯, 注意channel无法跨进程, 只能在开启的某个swoole进程的协程间通信

详细地址: https://wiki.swoole.com/#/coroutine/channel

  • WaitGroup功能:

    它基于channel实现的, 类似于一个记数器.

    主要方法:

    1. add 方法增加计数
    2. done 表示任务已完成
    3. wait 等待所有任务完成恢复当前协程的执行

详细地址: https://wiki.swoole.com/#/coroutine/wait_group

  • 一键协程化: Swoole\Runtime::enableCoroutine

    原因: 因为协程底层调度是异步io所以协程使用中的io也要是异步否者会阻塞

    解决1: 封装协程客户端. 但是1很难覆盖所有的io操作 ,2用户在原有项目中使用话改动也大.

    解决2: 一键协程化 采用 Hook 原生 PHP 函数的方式实现协程客户端,通过一行代码就可以让原来的同步 IO 的代
    码变成可以协程调度的异步 IO,即一键协程化

协程使用的一些注意事项

  • 多个协程之间不能共用同一个连接, 每个协程单独实例连接或者使用连接池

  • 多个协程之间不要用类静态变量或者全局变量保证上下文信息

    原因: 同步阻塞不一样,多个协程之间是并发的, 严格意义上来说是无序的, 共用连接或者全局变量保存上下文信息有时候会造成数据的混乱问题.

    解决: 使用 Coroutine::getUid 获取了协程 ID,然后隔离不同协程之间的全局变量,协程退出时清理上下文数据. 每个单独实例化连接或者使用连接池

Swoole协程的一些不足

相对于go语言:

  1. Swoole 的协程是基于单线程的, 无法利用多核CPU,同一时间只有一个在调度, 严格意义上说是串行行的, go是多线程的
  2. go在语言层面实现的原生协程, 在用过程中不需要申明协程环境, 而swoole需要
posted @ 2020-07-10 17:01  梅西西西西西西  阅读(699)  评论(0)    收藏  举报