在使用 hyperf 之前需要了解的几个知识点
进程,线程,协程区别
- 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。上下文进程间的切换开销比较大
- 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位,线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
- 协程是一种用户态的轻量级线程,协程的调度完全由用户控制。所以上下文的切换非常快。
线程进程都是同步机制,而协程则是异步
协程与线程主要区别是它将不再被内核调度,协程是用户程序控制,线程是将自己交给内核调度
swoole 生命周期
当一个请求到来,会由某个Reactor线程发生了连接,然后告诉Master进程,Master进程告诉Manager进程,由Manage进程负责分配给Worker进程和Task进程,最后将返回结果告诉Reactor进程发送给客户端
swoole中的角色有以下四种
Reactor线程:
- 负责维护客户端连接,处理网络IO、处理协议、收发数据。
- 完全是异步非阻塞的模式
- 以多线程的方式运行
Master进程:
- 分配请求到 Reactor线程
- 通知 Manager进程分配任务
Manager进程:
- fork 出Work和Task进程,回收子进程
Worker进程:
- 负责业务处理逻辑,可以异步也可以同步模式
- 多进程的方式运行
- 与Reactor交互,接受数据并处理返回给Reactor
Task进程:
- 执行异步业务
- 同步阻塞模式
- 以多进程的方式运行
hyperf 容器内的类都是单例
避免协程间数据混淆
在传统的 PHP-FPM 的框架里,会习惯提供一个 AbstractController 或其它命名的 Controller 抽象父类,然后定义的 Controller 需要继承它用于获取一些请求数据或进行一些返回操作,但是因为Hyperf 内绝大部分的对象包括 Controller 都是以 单例(Singleton) 形式存在的,所以在编写代码时请务必注意 不要 将单个请求相关的数据储存在类属性内,包括非静态属性。,我们可以注意到我们获取 请求(Request) 与 响应(Response) 对象时是通过注入 Hyperf\HttpServer\Contract\RequestInterface 和 Hyperf\HttpServer\Contract\ResponseInterface 来获取的,
hyperf中的进程,线程,协程
我们可以在Shell里运行,使用pstree查看进程模型结构:
reactor_num:表示Master进程中,Reactor线程总共开多少个,注意,这个可不是越多越好,因为计算机的CPU是有限的,所以一般设置为与CPU核心数量相同,或者两倍即可。
worker_num:表示启动多少个Worker进程,同样,Worker进程数量不是越多越好,仍然设置为与CPU核心数量相同,或者两倍即可。
task_worker_num :Task进程是同步阻塞的,配置方式与Worker同步模式一致
swoole中线程和协程的关系
Swoole的协程在底层实现上是单线程的,因此同一时间只有一个协程在工作,协程的执行是串行的。这与线程不同,多个线程会被操作系统调度到多个CPU并行执行。
一个协程正在运行时,其他协程会停止工作。当前协程执行阻塞IO操作时会挂起,底层调度器会进入事件循环。当有IO完成事件时,底层调度器恢复事件对应的协程的执行。
对CPU多核的利用,仍然依赖于Swoole引擎的多进程机制。
一个协程正在运行时,其他协程会停止工作 意思是一个 cpu 核心上, 只能运行一个协程么? 同一时刻, 线程一个 cpu 核心上也只能运行一个线程呀? 区别是协程会执行完自己让出 cpu, 线程是让 cpu 自动调度么?
对CPU多核的利用,仍然依赖于Swoole引擎的多进程机制 意思是请求分发到不同的 worker 进程上, worker 进程被调度到不同的 cpu 核心, 每个 cpu 核心当前都是可以执行一个协程的, 从而达到并行执行多个协程的效果?