我相信在PHP的世界里,很多开发者对 “协程” 这两个字既是好奇又恐惧,听起来特别高大上,听起来全是yield、底层扩展、特别高深莫测。其实、一点都不神秘,它只是换了一种工作流程方式,而这种功能流程方式压根就不用你来过于关心。到底什么是协程?
下次面试问你协程,你就记住回答,其实总结就三个:保存、暂停、恢复
保存:就是代码执行到一半,例如查询数据库IO等待的时候,它就会把当前的执行数据 “存档” 也就是记下来。
暂停:高大上一点就说(挂起)其实就是发现数据库没有返回数据,把CPU的资源让给其他请求。
恢复:数据库返回数据了,自动切换到之前存档 暂停的代码 继续执行往下跑。
如果面试官听完也懵逼了,那他大概率也是个半桶水,应付下就得了。
协程 不等于 自动并行
很多同学听了 “协程” 这么猛,兴冲冲的就把项目改造一下,结果已测试,奶奶的,怎么跟说的不一样,300毫秒的接口还是300毫秒啊!哪里变快了。
这是你没掌握到协程的正确使用方式。
大家请看下以下的一个场景:
app的个人中心需要展示 用户信息、是否有公告弹窗、有没有优惠券、会员权限等,不要太多就假设这四个吧。
如果你的代码是这样子的话
db->getUserInfo() # 例如 300毫秒db->getCoupon() # 200毫秒db->getNotice() # 200 毫秒db->getUserVip() # 200毫秒
那协程不协程 还是总计耗时:900毫秒。
你的写法必须要更变,使用并行parallel的写法才行。去了解下
https://wiki.swoole.com/zh-cn/#/coroutine/scheduler?id=parallel
如果你使用了并行 接口就按耗时最大的来算,也就是300毫秒就行了。
并行这么好,那用并行好了。
醒醒!并行也是有代价的,所有的架构方案里,“鱼”和“熊掌”永远不可兼得。就拿上面的例子说明一下
普通模式,一个个排队执行,数据库可以从容不破,没啥压力
并行模式,N个一起冲进去。数据库压力倍增。瞬间炸开。
例如有1000个用户同时打开 个人中心
串行:每一个用户占用一个链接,总计1000连接,直接900毫秒走完释放连接,系统稳定性高,瞬间请求量也是 1000 也很平稳
并行:每个人4个连接,总计1000 * 4个连接 延迟按照最高的延迟,不累加,系统稳定性就显得比较低了,瞬间请求量4000 冲击很大。
这就是为什么,用了协程框架或者协程,很多术语就出来了,什么熔断啊、限流啊,因为如果不这么做的话很多场景很容易导致内存溢出,或者连接池占用不释放,限流 熔断就是保证 防止局部问题升级为全局。
例如你是调用第三方API、远程请求,协程本质并不能提速,这叫网络IO。
如果你要为1000个人同时发送短信通知,每个远程请求是100毫秒 单个的情况下,1000 * 0.1秒 = 100秒 才能全部通知完。这时候你肯定会想 我开并行 1000个直接全部发出去,然后以一个处理回包,快的离谱。
错!你这样子会瞬间压死下游,对方直接限流或者直接判定为DDOS攻击,封禁你,你肯定也看过很多开发文档,文档都会写清楚并发请求是多少,也就是这个原因。
正确的做法是,维持一个容量的池子,每次只并发的发送 N 条,发完一批再来一批,这样子才是正解,例如一个池子50,我发送20次就完事了,耗时 20 * 0.1 秒 = 2秒钟。
有没有必要用?
如果你是普通的CMS、后台管理系统、真没必要,不如用传统的FPM,因为不用增加复杂度,FPM简单也稳定 用完既销。
如果你是开发高并发的应用API或者长连接、微服务,就非常的有必要使用了。
用了协程就要注意避坑了,传统原生数据库pdo、sleep等等,就不能再使用了,要使用对应协程环境的客户端 还有很多不一一举例了,这就是你作为传统的开发,使用协程之后,要付出的代价。
总结
协程并不是让CPU变快了,而是让 CPU 在等待网络IO(查 DB、调 API、写 Redis)的 “垃圾时间”(也就是等待) 里,去处理别的任务。
再说一句 协程是IO的解药,但是他不是万能的,一个优秀的开发者,不应该只是会写并行来提速,要学会在性能跟稳定之间 找到一个平衡点。