为什么要使用 swoole
swoole
的强大之处就在与其进程模型的设计,既解决了异步问题,又解决了并行。
从4.0版本开始Swoole
提供了完整的协程(Coroutine)+通道(Channel)特性,带来全的CSP
编程模型。应用层可使用完全同步的编程方式,底层自动实现异步IO。
使用常驻内存模式 可以避免每次框架的初始化,节约了性能开销。
4.x 底层采用自动化协程转换,
什么是协程
Coroutine 是历史比线程的历史还要悠久,协程可以理解为纯用户态的线程,其通过协作而不是抢占来进行切换。相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低。Swoole可以为每一个请求创建对应的协程,根据IO的状态来合理的调度协程。
开发者可以无感知的用同步的代码编写方式达到异步IO的效果和性能,避免了传统异步回调所带来的离散的代码逻辑和陷入多层回调中导致代码无法维护
同时由于底层封装了协程,所以对比传统的PHP层协程框架,开发者不需要使用yield
关键词来标识一个协程IO操作,所以不再需要对yield
的语义进行深入理解以及对每一级的调用都修改为yield
,这极大的提高了开发效率
下面是一个 对比:
多进程 | 多线程 | 协程 | |
---|---|---|---|
创建 | fork | pthread_create | go |
回收 | wait | pthread_join | - |
通信方式 | IPC 进程间通 | 数据同步/锁 | array/chan |
资源消耗 | 进程切换开销 | 进程切换开销 | 非常低 |
并发能力 | 数百 | 数千 | 50万 |
编程难度 | 困难 | 非常困难 | 容易 |
协程的优点
- 用户态线程,遇到 IO 主动让出
- PHP 代码依然是串行执行的,无需加锁
- 开销极低,仅占用内存,不存在进程/线程切换开销
- 并发量大,单个进程可开启 50W 个协程
- 随时随地,只要你想并发,就调用 go 创建新协程
swoole 的协程
swoole 的协程 和 golang的调度方式完全不同,每一个进程里面的协程都是串行
执行所以无需担心访问资源加锁问题,这也符合 php 简单的特性。
那么 进程 的协程是
串行
执行如何利用 多核CPU
实现并行
呢。答案是利用多进程实现。现在 task 也可以开启协程。
这也许没有 golang 性能那么好,但是对于 IO 密集型业务
是非常适合的,协程的上下文切换非常快。切换一门语言的成本对公司来说也是非常巨大的,现在 swoole 的生态也越来越好。
4.0 底层加入 Hook
机制 使用 原生的 Mysql PDO
,Redis
操作将直接被协程化,后续将支持 Curl
扩展。更贴近传统业务代码,迁移成本也降低。
swoole
的 http server
采用优秀的 Reactor 模型
,处理速度可逼进 NGINX
处理静态页面的 速度。对于 Api
或者基础服务
来说非常的适合。性能瞬间翻几翻,在也不用担心 php-fpm 进程数过多,导致 CPU
被打满。
swoole 需要注意的地方
当然它也不是没有缺点的
无法做密集计算。当然这一点是php甚至是所有动态语言都存在的问题。写在这里是因为防止误导读者以为使用swoole
后,php
可以用来做密集计算。
更容易内存泄露。在处理全局变量,静态变量的时候一定要小心,这种不会被GC清理的变量会存在整个生命周期中,如果没有正确的处理,很容易消耗完所有的内存。而以往的php-fpm下,php代码执行完内存就会被完全释放。
CSP 有一句很经典的话:不要通过共享内存来通信,而应该通过通信来共享内存
这个”通信”你可以理解利用channel
通信。
虽然 swoole 协程是串行,但是业务有可能会交叉,比如你一个业务在添加配置一个业务在写配置,上下文一切换,可能会造成配置的不一致,你可能会好奇,本地跑跑着没事到了线上跑一段时间会出问题,这本是业务设计上的问题。