-
Notifications
You must be signed in to change notification settings - Fork 172
与 php fpm 配合原理
Mingliang Tan edited this page Feb 15, 2019
·
1 revision
php-fpm 有三个特点
- 父子进程:多个子进程争抢对同一个fd的accept,实现负载均衡
- php 自身是完全串行的,如果写日志过多,就会拖慢业务代码
- php 进程是短进程,不会长期驻留。生产环境的配置是每一万个请求就重启进程。
对应的解决办法是
- golang 写的 so 只在子进程里加载。避免因为 fork 引起的问题。先把 c 写的 so 注入到父进程,在子进程 accept 的时候把 go 写的 so 给加载进来。c 写的 so 把 libc 拦截到的调用转发给 go 写的 so。
- 日志发送不在 php 的线程里执行。go 注入的 so 里单独起一个线程做发送。两者之间基于 channel 实现生产者消费者。如果消费者速度跟不上,则生产者丢弃数据。
- 不要求完整性。进程重启前的最后一个请求无法被录制到。也就是万分之一的流量是被丢弃的。
图示一下加载步骤
第一步:通过 LD_PRELOAD 把 koala-recorder-loader.so 注入到 php-fpm 的父进程里
这个时候 php-fpm 的所有 send/recv 等操作都是要经过这个 koala-libc.so 了。只是因为实际的 recorder 还没有加载,所有的调用是直接透传的。
第二步:php-fpm fork 子进程
fork出来的子进程继承了父进程的所有状态。当然也就继承了我们注入的 koala-libc.so。子进程的 libc 函数调用同样是被我们拦截了的。
php-fpm worker 启动之后会对父进程监听端口的 socket fd 做 accept 操作。这个 accept 只会在子进程里进行,所以当 koala-libc.so 拦截到 accept 之后就知道我们已经在子进程里了。
第三步:koala-libc.so 加载 koala-recorder.so
koala-libc.so 会在拦截到 accept 函数调用的时候用 dlopen 加载 koala-recorder.so(用 Golang 编写的)。然后所有的 send/recv 等操作都会复制一份给 koala-recorder.so 实现录制。