与 php fpm 配合原理 · didi/rdebug Wiki

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 实现录制。


Original url: Access
Created at: 2019-06-10 15:49:29
Category: default
Tags: none

请先后发表评论
  • 最新评论
  • 总共0条评论