问即是学
基础编程知识
阻塞
和 非阻塞
的区别?
- 同步(
Synchronous
)IO
当一个程序发起调用的时候,在程序没有处理完成返回结果时,调用方会一直等待,等待结果的返回。
- 异步(
Asynchronous
)IO
当一个程序发起调用的时候,程序立即返回调用成功的消息,但不会马上返回结果,而是需要通过回调的方式把返回结果返回给调用方。
- 阻塞(
Blocking
)IO
阻塞就是在程序调用之后,结果返回之前,当前的进程/线程是被挂起的
- 非阻塞(
non-Blocking
)IO
非阻塞是指程序调用之后,结果返回之前,不会挂起该进程/线程的。
异步的优势 ?
- 高并发,同步阻塞IO模型的并发能力依赖于进程/线程数量
- 无IO等待,同步模型无法解决
IOWait
很高的场景
同步的优势 ?
- 编码简单,同步模式编写/调试程序更轻松
- 可控性好,同步模式的程序具有良好的过载保护机制
PHP的GC机制 ?
GC机制: 垃圾回收机制
PHP5.3
: 当变量容器的ref_count
计数清0时,表示该变量容器就会被销毁,实现了内存回收PHP5.3
版本之后引入根缓冲机制,即php启动时默认设置指定zval
数量的根缓冲区(默认是10000),当php发现有存在循环引用的zval
时,就会把其投入到根缓冲区,当根缓冲区达到配置文件中的指定数量(默认是10000)后,就会进行垃圾回收,以此解决循环引用导致的内存泄漏问题
PHP面向对象编程: 对象引用机制和对象与内存之间的关系 ?
对象释放的时候调用了
__destruct()
,释放内存;
多进程编程
fpm
和 swoole
的多进程模型及其区别 ?
PHP-FPM
php-fpm
就是通过 FastCGI
协议来处理 PHP 与第三方 WEB 服务器之间的通信
比如 Nginx + php-fpm
的组合,这种方式运行的 fpm
是 Master/Worker
模式,启动一个 Master
进程监听来自 Nginx
的请求,再 fork
多个 Worker
进程处理请求。每个 Worker
进程只能处理一个请求,单一进程的生命周期大体如下:
- 初始化模块。
- 初始化请求。此处请求是请求 PHP 执行代码的意思,并非 HTTP 的请求。
- 执行 PHP 脚本。
- 结束请求。
- 关闭模块。
Swoole
Swoole
采用的也是 Master/Worker
模式,不同的是 Master
进程有多个 Reactor
线程,Master
只是一个事件发生器,负责监听 Socket
句柄的事件变化。Worker
以多进程的方式运行,接收来自 Reactor
线程的请求,并执行回调函数(PHP 编写的)。启动 Master
进程的流程大致是:
- 初始化模块。
- 初始化请求。因为 swoole 需要通过 cli 的方式运行,所以初始化请求时,不会初始化 PHP 的全局变量,如 $_SERVER, $_POST, $_GET 等。
- 执行 PHP 脚本。包括词法、语法分析,变量、函数、类的初始化等,Master 进入监听状态,并不会结束进程。
对比不同:
PHP-FPM
- Master 主进程 / Worker 多进程模式。
- 启动 Master,通过 FastCGI 协议监听来自 Nginx 传输的请求。
- 每个 Worker 进程只对应一个连接,用于执行完整的 PHP 代码。
- PHP 代码执行完毕,占用的内存会全部销毁,下一次请求需要重新再进行初始化等各种繁琐的操作。
- 只用于 HTTP Server。
Swoole
- Master 主进程(由多个 Reactor 线程组成)/ Worker 多进程(或多线程)模式
- 启动 Master,初始化 PHP 代码,由 Reactor 监听 Socket 句柄的事件变化。
- Reactor 主线程负责子多线程的均衡问题,Manager 进程管理 Worker 多进程,包括 TaskWorker 的进程。
- 每个 Worker 接受来自 Reactor 的请求,只需要执行回调函数部分的 PHP 代码。
- 只在 Master 启动时执行一遍 PHP 初始化代码,Master 进入监听状态,并不会结束进程。
- 不仅可以用于 HTTP Server,还可以建立 TCP 连接、WebSocket 连接。
进程间通信? 进程隔离? 进程信号量?
进程间通信
- 管道
- 我们知道进程直接是相互独立的,那如果在进程之间通信就需要通过特殊介质,建立管道是其中一种方式。
- 管道分无名管道(pipe)和有名管道(FIFO),无名管道只能用于父子进程或者兄弟进程这些具有亲缘关系的进程;
- 为了克服这个缺点,就有了有名管道,提供非亲缘关系直接进程间通信
- 信号
- 比如我们kill命令来杀死一个进程,就是内核生成一个信号,当系统捕获到是这个信号就会做出相应得操作。
- 信号可以作为进程间通信或修改行为的一种方式,明确地由一个进程发送给另一个进程。一个信号的产生叫生成,接收到一个信号叫捕获。
- 消息队列
- 共享内存
进程隔离
- 不同的进程中PHP变量不是共享,即使是全局变量,在A进程内修改了它的值,在B进程内是无效的
- 如果需要在不同的Worker进程内共享数据,可以用
Redis
、MySQL
、文件、Swoole\Table
、APCu
、shmget
等工具实现 - 不同进程的文件句柄是隔离的,所以在A进程创建的Socket连接或打开的文件,在B进程内是无效,即使是将它的fd发送到B进程也是不可用的
基础的TCP/UDP认知
TCP
和 UDP
的区别 ?
TCP(Transmission Control Protocol, 传输控制协议)
TCP 三次握手 四次挥手
UDP(User Data Protocol, 用户数据报协议)
区别
- 基于连接与无连接;
- 对系统资源的要求 (TCP较多,UDP少);
- UDP程序结构较简单;
- 流模式与数据报模式;
- TCP保证数据正确性,UDP可能丢包;
- TCP保证数据顺序,UDP不保证
OSI七层模型中的上四层 ?
应用层 表示层 会话层 传输层 网络层 数据链路层 物理层
协程
Swoole的协程工作模式 ?
- 内存栈
4.0
版本使用了PHP
+C
的双栈模式。创建协程时会创建一个C
栈,默认尺寸为2M
,创建一个PHP
栈,默认为8K
。
C栈主要用于保存底层函数调用的局部变量数据,用于解决call_user_func
、array_map
等C函数调用在协程切换时未能还原的问题。4.0
版本无论如何切换协程,底层总是能正确地切换回原先的C函数栈帧继续向下执行。
- 协程切换
C
栈切换使用了boost.context 1.60
汇编代码,用于保存寄存器,切换指令序列。主要是jump_fcontext
这个ASM
函数提供。PHP
栈的切换是跟随C栈切同步进行的。底层会切换EG(vm_stack)
使得PHP
恢复到正确的PHP
函数栈帧。4.0.2
版本还增加了ob
输出缓存区的切换,ob_start
等操作也可以用于协程。
- 协程调度
4.0
协程实现中,主协程即为Reactor
协程,负责整个EventLoop
的运行。主协程实现事件监听,在IO事件完成后唤醒其他工作协程。
- 协程挂起
在工作协程中执行一些IO
操作时,底层会将IO
事件注册到EventLoop
,并让出执行权。
- 协程恢复
当主协程的Reactor
接收到新的IO
事件,底层会挂起主协程,并恢复IO
事件对应的工作协程。该工作协程挂起或退出时,会再次回到主协程。
Swoole常见的”坑”
为何全局变量无法共享呢 ?
这是在于出现了进程克隆,而每个进程之间的数据都是不一致的。
echo
var_dump
无法输出到浏览器(http响应) ?
原因在于模式的变更,swoole的运行模式不再是fpm,而是cli,如果你需要把数据响应给浏览器,你只能 通过
Http request
回调中的response
对象进行响应
http请求参数获取 ?
在同
swoole
的http
服务的时候,很多人会发现$_GET
、$_POST
等常见全局变量无法使用。这是因为$_GET
、$_POST
等变量都是全局的,在swoole
当中会出现问题,如果想获取请求参数,可以用swoole
回调时提供的Request
对象来进行获取
swoole不能使用die/exit ?
phper
都习惯用die/exit
来调试代码,这是因为这个命令会直接退出当前进程,对于fpm来讲,每个请求都对应一个独立进程,退出了问题不大,但是在swoole
当中,可能一个进程中会有多个请求同时在处理,如果你exit
或者die
来退出当前进程,会导致数据丢失。
swoole下为何需要断线重连 ?
在
swoole
常驻内存的情况下, 请求结束后,该连接并不会被清理,依旧保留在内存空间内,而该连接若是长时间没有使用,或者是因为网络波动,那么就会断开。下次请求进来的时候,你没有判断连接状态,就直接去执行sql语句,那么就意味着你操作了一个断线的数据库连接,因此肯定会报错。