同步 异步 阻塞 非阻塞

灵感来源别人利用多进程写了个python 的下载器,于是想自己用php试一下

先说下多进程和单进程

lnmp请求过程

http请求到达nginx,nginx 转发给php-fpm(一个php进程管理器,~so,这个地方就是多进程啦),每个进程中实现了fast-cgi 协议(请求完毕,并不重启进程,而是等待下一个请求的到来,除非到达该线程接受http请求最大数量,会自动重启,目的是为了防止一些代码不好,导致内存泄漏),每个进程中其实就是 php index.php(请求处理完逐个往回返回), 所以我们可以把lnmp 模型理解成多进程模型,同一时刻并发的数量就是php-fpm配置的进程数量,但为什么别人说php是单进程呢,因为我们平时在开发的过程中,所有的代码都是跑在一个请求中,也就是一个进程中,所以我们可以说php就是单进程的,但php真的只是单进程的吗,不是,php有个扩展叫pcntl ,这个扩展不建议在php-fpm模式下使用,但是在php-cli 下可以正常使用,也就是说我们不建议在php-fpm的工作进程中再fork新的子进程(同理swoole 中也不建议在worker进程中new process 新的进程),既然在工作进程中不涉及fork 子进程,而php也很少操作线程,那么php就和异步搭不上边了。

同步,异步区别:

1
2
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。
异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

最常见的异步莫过于前端的ajax,

ajax 允许浏览器在发出请求的时候浏览器(一个进程)还可以去做别的事情,而不用干等着(同步就不行了,同步阻塞显然不行,同步非阻塞呢?同步虽然进程还可以去做别的事情,但是进程但过一会还得再来询问一下,请求是否完成,所以哪怕是同步非阻塞,代码流程执行到这会,始终会停住,不会继续向下走,这和异步代码能继续往下跑是不一样的,所以同步更符合我们的日常习惯。

比如异步编程,我们不知道ajax执行的函数啥时候回给我们返回内容,所以我们无法用 $a = ajax(xxx), 去获取ajax 执行的内容,大部分情况下用的是回调函数,然而为什么php中也有回调函数呢,php中的回调更类似于js中的匿名函数,他只是代码同步执行到用户定义的函数中,调用了之前定义的一个匿名函数,和js的回调存在本质上的区别。js中的回调,更类似swoole的回调,发现了嘛,都是通过事件的触发机制(但是这个应该和事件io不一样,之前看的阻塞io 非阻塞io 事件io 信号io都属于同步,只有异步io属于异步方式)

所以我们平时在看php的代码中就不要考虑异步方式了,基本没有,因为都是同步的,又因为阻塞和非阻塞都会有返回值,所以我们平时的那些函数基本都有返回值,但是有些回调形式的函数返回值有没有用,那就另说,看定义这个函数的人怎么想了。(其实看同步和异步看函数的return 也能看出来,如果return外面能接收到,那就是同步,比如php-fpm,包了那么多header头信息,还是return就能返回,你看swoole中的return ,在事件回调中,他是通过 $response 返回给别的进程的,所以属于异步)。

(可能有小伙伴看klein 或者larave中的路由函数觉得可能是回调,因为callback 中的内容总在 callback 外面先输出,首先,你在

1
2
3
4
5
6
$router->respond(['GET', 'POST'], '/test/test/[test|cry:test]/[i:id]', function ($request, $response) {

$obj = Box::getObject('out', 'controller');
return $obj->oop($request);
});
的 respond 方法中返回 值,看外面能不能接收到,能接收到就是同步

再者,他这个地方绑定了callback,并没有执行callback呀,所以这个地方并没有执行是很正常的,但是php这种没有触发机制,必须得执行呀,看后面

1
$router->dispath() // 其实是在这里面执行了

)

为什么说swoole 的task任务是异步任务,因为我们在worker进程中调用task添加异步任务的时候,当异步任务执行完的时候,我们能在之前的worker进程 中收到task返回的消息(onFinishi事件中)。(在没有swoole 之前,php中对于异步的实现都是通过第三方软件,比如把耗时任务放到消息队列中,由消费者自己去消费,消费者可以通过crontab 这种去轮训消息队列,虽然不太环保,但还是能完成任务)

swoole进程模型

master 整除ractor 线程和manager 进程,

manager 进程 生出 worker进程(异步非阻塞)和特殊worker进程(task进程,同步阻塞),负责进程重启和 回收

ractor 线程检查连接,还有可写的socket 发送给worker进程处理

swoole4之前请求过程

单个请求被单个worker处理,处理完再处理下一个

swoole4 之后

woker可以同时接受多个,遇到io,协程切换,处理下一个请求,当上一个请求io完毕,再切回来(感觉通过fd -> 运行环境),继续往下执行(注意就是这儿,虽然说协程本质上还是异步,但是他的运行方式类似同步,更好理解)

相较于异步的不容易理解,比如回调地狱,swoole 希望我们仍旧用同步的方式编写代码,但是对于一些io操作同步返回太慢了,所以就出现了异步io,redis, mysql,没有用过~~,swoole4中为了把这个异步摒弃,又引入了上述的协程,协程是比进程线程粒度更小的资源管理。

关于阻塞和非阻塞:

1
2
3
 阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。
有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。 例如,我们在socket中调用recv函数,如果缓冲区中没有数据,这个函数就会一直等待,直到有数据才返回。而此时,当前线程还会继续处理各种各样的消息。
非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。

php中虽然sleep, file_get_contents 中有同步阻塞io,但是string, arr这些函数还是同步非阻塞函数

当我们在项目在swoole的http server中,没有用上面的同步阻塞io的时候,我们整个项目代码就是异步的,但凡用了,那我们的项目代码就是同步的,所以这个同步和异步由两部分决定,一个是swoole server, 一个是我们的项目代码。

swoole 本质上是为了对标php-fpm,和php本身没有什么关系,他对项目的性能提升类似php7对项目代码的提升,我们不用修改源代码,就能提升并发量,只是php7是对php中函数的改造,而swoole 是对php-fpm的改造或者说php的运行环境的改造,他吧php-fpm 的功能也纳入到我们项目代码中。

所以总结一下:

php多进程 : 针对php-fpm, 或者php-cli 模式下的php进程而言

php单进程:针对php-fpm模式下php进程而言

同步: 针多 php-fpm 而言

异步:针对 swoole 封装的一些io操作而言,比如redis ,mysql ,还有http tcp server,

还有之前的多进程下载的实现,遍历url,每个url都放到task中执行,完美多进程,easy(不要想着在worker 在生成新的process 哦, 官网上说很占资源)

坚持原创技术分享,您的支持将鼓励我继续创作!
-------------本文结束感谢您的阅读-------------