转载请备注来源: 《nginx与php-fpm通信原理详解》 | shuwoom.com

文章摘要

本文主要讲解Nginx和PHP-FPM之间的运行机制和交互过程。在深入讲解之前,需要先介绍CGI、FastCGI、PHP-CGI、PHP-FPM之间的关系,然后我们会通过源码角度分析PHP-FPM的运行原理以及PHP-FPM和Nginx的交互过程。

Web Server

这里一般指Nginx,Apache,IIS,Lightpd,Tomcat等服务器。

Web Aplication

这里一般指PHP、Java等编程语言开发的应用程序,包括网站或移动应用等。

CGI

CGI是什么

CGI是Common Gateway Interface的缩写,即通用网关接口。CGI是外部应用程序(CGI程序)与Web server之间的接口通信标准。注意,它不是一门编程语言,而是一种协议,它可以用任何语言实现,只要这些语言能够接收输入输出信息。

CGI运行原理

CGI程序的执行流程如下:

  • 读取用户提交的表单信息
  • 处理信息(业务实现)
  • 输出返回html响应
Image result for cgi protocol

CGI架构图

cgiarch

CGI程序的缺点

CGI程序使得外部程序与web server的交互称为可能。但是,CGI程序是运行在独立的进程中,对每一个请求都会创建一个进程,处理完后再销毁进程。如果有成千上万个请求,那么久会重复成千上万次重复的创建和销毁进程操作。这种方式实现虽然容易,但是可见效率是非常差的。对于大量的请求,进程的频繁创建和销毁使得服务器性能大大下降(这是CGI最让人诟病的for-and-execute模式)。而且由于是独立进程,地址空间无法共享,也限制了资源的重用。这也是为什么后面会推出FastCGI协议,也是CGI协议的一个增强和优化(详细见下节)。

CGI实例

为了更形象理解CGI的含义,您可以参考这篇文章:Python CGI编程

FastCGI

FastCGI是Fast Common Gateway Interface的缩写,即快速通用网关接口,是一种让交互程序与web server通信的协议。这里可以理解为FastCGI是CGI增强版本。

FastCGI致力于减少网页web server和CGI程序之间交互的开销,从而使得服务器可以同时处理更多的web请求。

FastCGI和CGI的区别

FastCGI相比于CGI,它更像是一个常驻性的CGI程序,即它将CGI解释器进程一直保持在内存中运行。它可以一直运行着,不会每次收到web请求都去创建新的进程处理然后再销毁,从而获得较高的性能。

FastCGI工作流程

  • web server启动时加载FastCGI进程管理器进程管理器(如php-fpmApache Module等)
  • FastCGI进程管理器自身初始化,启动多个CGI解释器进程(如php-fpm或者spawn-fcgi)并等待来自web server的连接
  • 当客户端请求到达web server时,FastCGI进程管理器会选择并连接到其中一个CGI解释器,web server将CGI环境变量和标准输入发送到FastCGI子进程。
  • FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回web server。当FastCGI子进程关闭连接时,请求就处理完成。之后,FastCGI子进程会等待并处理来自FastCGI进程管理器的下一个连接。
fastcgi

PHP-CGI

PHP-CGI是php(web application)对web server提供的CGI协议的接口程序。PHP-CGI早期是PHP实现的自带的FastCGI管理器,虽然那时是官方自带的,但是性能却很差(详见下面的缺点部分内容)。

在php5.3以前,是采用PHP-CGI来实现FastCGI web请求。从php5.4开始,php-fpm取代了PHP-CGI,即负责进程管理。

PHP-CGI的使用

php-cgi -b 127.0.0.1:9000

PHP-CGI的缺点

  • 每一次PHP-CGI程序处理请求前,都需要加载php.ini文件、加载所有的扩展模块等许多重复的工作。
  • php.ini文件每次发生变更,就需要重启PHP-CGI程序才能让新的php.ini生效,不可以平滑启动
  • 直接杀死php-cgi进程,php就不能运行了(php-fpm则没有这个问题,守护进程会平滑地启动生成新的子进程)

PHP-CGI只是个CGI程序,本身只能处理解析请求,并返回结果,不会管理进程。所以之后就出现了一些能够调度PHP-CGI进程的程序,这就是后面PHP-FPM的出现原因。

PHP-FPM

PHP-FPM是PHP FastCGI Process Manager的缩写,是FastCGI进程管理器。

PHP-FPM的特点

  • 支持平滑停止 / 启动的高级进程管理功能;
  • 可以工作于不同的 uid/gid/chroot 环境下,并监听不同的端口和使用不同的 php.ini 配置文件(可取代 safe_mode 的设置);
  • stdout 和 stderr 日志记录;
  • 在发生意外情况的时候能够重新启动并缓存被破坏的 opcode;
  • 文件上传优化支持;
  • “慢日志” – 记录脚本(不仅记录文件名,还记录 PHP backtrace 信息,可以使用 ptrace 或者类似工具读取和分析远程进程的运行数据)运行所导致的异常缓慢;
  • fastcgi_finish_request () – 特殊功能:用于在请求完成和刷新数据后,继续在后台执行耗时的工作(录入视频转换、统计处理等);
  • 动态/静态子进程产生;
  • 基本 SAPI 运行状态信息(类似 Apache 的 mod_status);
  • 基于 php.ini 的配置文件。

PHP-FPM工作流程

php-fpm是基于master/worker的多进程架构模式,与nginx的设计风格很类似。master进程主要负责CGI、PHP环境初始化、事件监听、子进程状态,worker进程负责处理php请求。

FPM的master通过共享内存获取worker进程的信息,包括worker进程当前状态、已处理请求数等,当master进程要杀掉一个worker进程时则通过发送信号的方式通知worker进程。

FPM的实现首先是创建一个master进程,在master进程中创建并监听socket,然后fork出多个子进程,这些子进程各自accept请求,子进程的处理很简单,它在启动后阻塞在accept上,有请求到达后开始读取请求数据,读取完后开始处理然后再返回,在这期间不会接收其他请求,也就是说fpm的子进程同时只能响应一个请求,只有把这个请求处理完成后才会accept下一个请求。跟nginx的事件驱动模型是不同的,nginx是非阻塞的模型,如果一个请求数据还未发送完则会处理下一个请求,也就是说一个进程会同时连接多个请求。

PHP-FPM运行原理

备注:以下源码是php7版本,本节内容转载自:https://github.com/pangudashu/php7-internal/blob/master/1/fpm.md

FPM的初始化

首先从fpm的入口main开始分析:

//sapi/fpm/fpm/fpm_main.c
int main(int argc, char *argv[])
{
    ...
    //注册SAPI:将全局变量sapi_module设置为cgi_sapi_module
    sapi_startup(&cgi_sapi_module);
    ...
    //执行php_module_starup()
    if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
        return FPM_EXIT_SOFTWARE;
    }
    ...
    //初始化
    if(0 > fpm_init(...)){
        ...
    }
    ...
    fpm_is_running = 1;
    //请求处理
    fcgi_fd = fpm_run(&max_requests);//后面都是worker进程的操作,master进程不会走到下面
    parent = 0;
    ...
}

这里最关键的是fpm_init和fpm_run两个函数。先说说fpm_init,fpm_init主要有以下几个关键操作:

(1)fpm_conf_init_main():

解析php-fpm.conf配置文件,分配worker pool内存结构并保存到全局变量中:fpm_worker_all_pools,各worker pool配置解析到fpm_worker_pool_s->config中。

(2)fpm_scoreboard_init_main():

分配用于记录worker进程运行信息的共享内存,按照worker pool的最大worker进程数分配,每个worker pool分配一个fpm_scoreboard_s结构,pool下对应的每个worker进程分配一个fpm_scoreboard_proc_s结构,各结构的对应关系如下图。

img

fpm可以同时监听多个端口,每个端口对应一个worker pool,而每个pool下对应多个worker进程,如下图所示:

img

(3)fpm_signal_init_main():

static int sp[2];

int fpm_signals_init_main()
{
    struct sigaction act;

    //创建一个全双工管道
    if (0 > socketpair(AF_UNIX, SOCK_STREAM, 0, sp)) {
        return -1;
    }
    //注册信号处理handler
    act.sa_handler = sig_handler;
    sigfillset(&act.sa_mask);
    if (0 > sigaction(SIGTERM,  &act, 0) ||
        0 > sigaction(SIGINT,   &act, 0) ||
        0 > sigaction(SIGUSR1,  &act, 0) ||
        0 > sigaction(SIGUSR2,  &act, 0) ||
        0 > sigaction(SIGCHLD,  &act, 0) ||
        0 > sigaction(SIGQUIT,  &act, 0)) {
        return -1;
    }
    return 0;
}

这里会通过socketpair()创建一个管道,这个管道并不是用于master与worker进程通信的,它只在master进程中使用,具体用途在稍后介绍event事件处理时再作说明。另外设置master的信号处理handler,当master收到SIGTERM、SIGINT、SIGUSR1、SIGUSR2、SIGCHLD、SIGQUIT这些信号时将调用sig_handler()处理:

static void sig_handler(int signo)
{
    static const char sig_chars[NSIG + 1] = {
        [SIGTERM] = 'T',
        [SIGINT]  = 'I',
        [SIGUSR1] = '1',
        [SIGUSR2] = '2',
        [SIGQUIT] = 'Q',
        [SIGCHLD] = 'C'
    };
    char s;
    ...
    s = sig_chars[signo];
    //将信号通知写入管道sp[1]端
    write(sp[1], &s, sizeof(s));
    ...
}

(4)fpm_sockets_init_main()

创建每个worker pool的socket套接字。

(5)fpm_event_init_main()

启动master的事件管理,fpm实现了一个事件管理器用于管理IO、定时事件,其中IO事件通过kqueue、epoll、poll、select等管理,定时事件就是定时器,一定时间后触发某个事件。

fpm_init()初始化完成后接下来就是最关键的fpm_run()操作了,此环节将fork子进程,启动进程管理器,另外master进程将不会再返回,只有各worker进程会返回,也就是说fpm_run()之后的操作均是worker进程的。

int fpm_run(int *max_requests)
{
    struct fpm_worker_pool_s *wp;
    for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
        //调用fpm_children_make() fork子进程
        is_parent = fpm_children_create_initial(wp);

        if (!is_parent) {
            goto run_child;
        }
    }
    //master进程将进入event循环,不再往下走
    fpm_event_loop(0);

run_child: //只有worker进程会到这里

    *max_requests = fpm_globals.max_requests;
    return fpm_globals.listening_socket; //返回监听的套接字
}

在fork后worker进程返回了监听的套接字继续main()后面的处理,而master将永远阻塞在fpm_event_loop(),接下来分别介绍master、worker进程的后续操作。

FPM的请求处理

fpm_run()执行后将fork出worker进程,worker进程返回main()中继续向下执行,后面的流程就是worker进程不断accept请求,然后执行PHP脚本并返回。整体流程如下:

  • 等待请求: worker进程阻塞在fcgi_accept_request()等待请求;
  • 解析请求: fastcgi请求到达后被worker接收,然后开始接收并解析请求数据,直到request数据完全到达;
  • 请求初始化: 执行php_request_startup(),此阶段会调用每个扩展的:PHP_RINIT_FUNCTION();
  • 编译、执行: 由php_execute_script()完成PHP脚本的编译、执行;
  • 关闭请求: 请求完成后执行php_request_shutdown(),此阶段会调用每个扩展的:PHP_RSHUTDOWN_FUNCTION(),然后进入步骤(1)等待下一个请求。
int main(int argc, char *argv[])
{
    ...
    fcgi_fd = fpm_run(&max_requests);
    parent = 0;

    //初始化fastcgi请求
    request = fpm_init_request(fcgi_fd);

    //worker进程将阻塞在这,等待请求
    while (EXPECTED(fcgi_accept_request(request) >= 0)) {
        SG(server_context) = (void *) request;
        init_request_info();

        //请求开始
        if (UNEXPECTED(php_request_startup() == FAILURE)) {
            ...
        }
        ...

        fpm_request_executing();
        //编译、执行PHP脚本
        php_execute_script(&file_handle);
        ...
        //请求结束
        php_request_shutdown((void *) 0);
        ...
    }
    ...
    //worker进程退出
    php_module_shutdown();
    ...
}

worker进程一次请求的处理被划分为5个阶段:

  • FPM_REQUEST_ACCEPTING: 等待请求阶段
  • FPM_REQUEST_READING_HEADERS: 读取fastcgi请求header阶段
  • FPM_REQUEST_INFO: 获取请求信息阶段,此阶段是将请求的method、query stirng、request uri等信息保存到各worker进程的fpm_scoreboard_proc_s结构中,此操作需要加锁,因为master进程也会操作此结构
  • FPM_REQUEST_EXECUTING: 执行请求阶段
  • FPM_REQUEST_END: 没有使用
  • FPM_REQUEST_FINISHED: 请求处理完成

worker处理到各个阶段时将会把当前阶段更新到fpm_scoreboard_proc_s->request_stage,master进程正是通过这个标识判断worker进程是否空闲的。

FPM进程管理

root     27792     1  0 Jun14 ?        00:01:19 php-fpm: master process (/usr/local/services/php-1.0/etc/php-fpm.conf)
php-fpm  27793 27792  0 Jun14 ?        00:10:38 php-fpm: pool www
php-fpm  27794 27792  0 Jun14 ?        00:10:38 php-fpm: pool www
php-fpm  27795 27792  0 Jun14 ?        00:10:39 php-fpm: pool www
php-fpm  27796 27792  0 Jun14 ?        00:10:38 php-fpm: pool www
php-fpm  27797 27792  0 Jun14 ?        00:10:39 php-fpm: pool www
php-fpm  27798 27792  0 Jun14 ?        00:10:39 php-fpm: pool www
php-fpm  27799 27792  0 Jun14 ?        00:10:39 php-fpm: pool www
php-fpm  27800 27792  0 Jun14 ?        00:10:39 php-fpm: pool www

这一节我们来看下master是如何管理worker进程的,首先介绍下三种不同的进程管理方式:

  • static: 这种方式比较简单,在启动时master按照pm.max_children配置fork出相应数量的worker进程,即worker进程数是固定不变的
  • dynamic: 动态进程管理,首先在fpm启动时按照pm.start_servers初始化一定数量的worker,运行期间如果master发现空闲worker数低于pm.min_spare_servers配置数(表示请求比较多,worker处理不过来了)则会fork worker进程,但总的worker数不能超过pm.max_children,如果master发现空闲worker数超过了pm.max_spare_servers(表示闲着的worker太多了)则会杀掉一些worker,避免占用过多资源,master通过这4个值来控制worker数
  • ondemand: 这种方式一般很少用,在启动时不分配worker进程,等到有请求了后再通知master进程fork worker进程,总的worker数不超过pm.max_children,处理完成后worker进程不会立即退出,当空闲时间超过pm.process_idle_timeout后再退出

前面介绍到在fpm_run()master进程将进入fpm_event_loop()

void fpm_event_loop(int err)
{
    //创建一个io read的监听事件,这里监听的就是在fpm_init()阶段中通过socketpair()创建管道sp[0]
    //当sp[0]可读时将回调fpm_got_signal()
    fpm_event_set(&signal_fd_event, fpm_signals_get_fd(), FPM_EV_READ, &fpm_got_signal, NULL);
    fpm_event_add(&signal_fd_event, 0);

    //如果在php-fpm.conf配置了request_terminate_timeout则启动心跳检查
    if (fpm_globals.heartbeat > 0) {
        fpm_pctl_heartbeat(NULL, 0, NULL);
    }
    //定时触发进程管理
    fpm_pctl_perform_idle_server_maintenance_heartbeat(NULL, 0, NULL);

    //进入事件循环,master进程将阻塞在此
    while (1) {
        ...
        //等待IO事件
        ret = module->wait(fpm_event_queue_fd, timeout);
        ...
        //检查定时器事件
        ...
    }
}

这就是master整体的处理,其进程管理主要依赖注册的几个事件,接下来我们详细分析下这几个事件的功能。

(1)sp[1]管道可读事件:

fpm_init()阶段master曾创建了一个全双工的管道:sp,然后在这里创建了一个sp[0]可读的事件,当sp[0]可读时将交由fpm_got_signal()处理,向sp[1]写数据时sp[0]才会可读,那么什么时机会向sp[1]写数据呢?前面已经提到了:当master收到注册的那几种信号时会写入sp[1]端,这个时候将触发sp[0]可读事件。

img

这个事件是master用于处理信号的,我们根据master注册的信号逐个看下不同用途:

  • SIGINT/SIGTERM/SIGQUIT: 退出fpm,在master收到退出信号后将向所有的worker进程发送退出信号,然后master退出
  • SIGUSR1: 重新加载日志文件,生产环境中通常会对日志进行切割,切割后会生成一个新的日志文件,如果fpm不重新加载将无法继续写入日志,这个时候就需要向master发送一个USR1的信号
  • SIGUSR2: 重启fpm,首先master也是会向所有的worker进程发送退出信号,然后master会调用execvp()重新启动fpm,最后旧的master退出
  • SIGCHLD: 这个信号是子进程退出时操作系统发送给父进程的,子进程退出时,内核将子进程置为僵尸状态,这个进程称为僵尸进程,它只保留最小的一些内核数据结构,以便父进程查询子进程的退出状态,只有当父进程调用wait或者waitpid函数查询子进程退出状态后子进程才告终止,fpm中当worker进程因为异常原因(比如coredump了)退出而非master主动杀掉时master将受到此信号,这个时候父进程将调用waitpid()查下子进程的退出,然后检查下是不是需要重新fork新的worker

具体处理逻辑在fpm_got_signal()函数中,这里不再罗列。

(2)fpm_pctl_perform_idle_server_maintenance_heartbeat():

这是进程管理实现的主要事件,master启动了一个定时器,每隔1s触发一次,主要用于dynamic、ondemand模式下的worker管理,master会定时检查各worker pool的worker进程数,通过此定时器实现worker数量的控制,处理逻辑如下:

static void fpm_pctl_perform_idle_server_maintenance(struct timeval *now)
{
    for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
        struct fpm_child_s *last_idle_child = NULL; //空闲时间最久的worker
        int idle = 0; //空闲worker数
        int active = 0; //忙碌worker数

        for (child = wp->children; child; child = child->next) {
            //根据worker进程的fpm_scoreboard_proc_s->request_stage判断
            if (fpm_request_is_idle(child)) {
                //找空闲时间最久的worker
                ...
                idle++;
            }else{
                active++;
            }
        }
        ...
        //ondemand模式
        if (wp->config->pm == PM_STYLE_ONDEMAND) {
            if (!last_idle_child) continue;

            fpm_request_last_activity(last_idle_child, &last);
            fpm_clock_get(&now);
            if (last.tv_sec < now.tv_sec - wp->config->pm_process_idle_timeout) {
                //如果空闲时间最长的worker空闲时间超过了process_idle_timeout则杀掉该worker
                last_idle_child->idle_kill = 1;
                fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
            } 
            continue;
        }
        //dynamic
        if (wp->config->pm != PM_STYLE_DYNAMIC) continue;
        if (idle > wp->config->pm_max_spare_servers && last_idle_child) {
            //空闲worker太多了,杀掉
            last_idle_child->idle_kill = 1;
            fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
            wp->idle_spawn_rate = 1;
            continue;
        }
        if (idle < wp->config->pm_min_spare_servers) {
            //空闲worker太少了,如果总worker数未达到max数则fork
            ...
        }
    }
}

(3)fpm_pctl_heartbeat():

这个事件是用于限制worker处理单个请求最大耗时的,php-fpm.conf中有一个request_terminate_timeout的配置项,如果worker处理一个请求的总时长超过了这个值那么master将会向此worker进程发送kill -TERM信号杀掉worker进程,此配置单位为秒,默认值为0表示关闭此机制,另外fpm打印的slow log也是在这里完成的。

static void fpm_pctl_check_request_timeout(struct timeval *now)
{   
    struct fpm_worker_pool_s *wp;

    for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
        int terminate_timeout = wp->config->request_terminate_timeout;
        int slowlog_timeout = wp->config->request_slowlog_timeout;
        struct fpm_child_s *child;

        if (terminate_timeout || slowlog_timeout) { 
            for (child = wp->children; child; child = child->next) {
                //检查当前当前worker处理的请求是否超时
                fpm_request_check_timed_out(child, now, terminate_timeout, slowlog_timeout);
            }
        }
    }
}

除了上面这几个事件外还有一个没有提到,那就是ondemand模式下master监听的新请求到达的事件,因为ondemand模式下fpm启动时是不会预创建worker的,有请求时才会生成子进程,所以请求到达时需要通知master进程,这个事件是在fpm_children_create_initial()时注册的,事件处理函数为fpm_pctl_on_socket_accept(),具体逻辑这里不再展开,比较容易理解。

到目前为止我们已经把fpm的核心实现介绍完了,事实上fpm的实现还是比较简单的。

Nginx与PHP-FPM交互过程

通过使用nginx,我们会使用LNMP的技术栈,把nginx作为反向代理将请求动态转发给后端的php-fpm解析处理。下面我们来看下从nginx到php-fpm这一整个处理流程是怎样的。

配置nginx.conf支持php-fpm

配置文件参考如下:

user  nginx;
pid /run/nginx.pid;
worker_processes  auto;
worker_rlimit_nofile 100000;

events {
    worker_connections  2048;
    multi_accept on;
}

http {
    sendfile on;

    access_log off;
    error_log  /data/log/nginx-1.0/error.log  error;


    server {
        listen 80;
        server_name localhost;
        root /usr/local/services/nginx-1.0/html/;

        location / {
         index index.php;
        }

      location ~\.php$ {
          fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
          fastcgi_pass 127.0.0.1:9000;
          fastcgi_index  index.php;
      }

    }
}

这里php-fpm监听9000端口,我们通过配置nginx,.php后缀的请求转发给php-fpm9000端口去解析处理。这里使用9000端口是因为php-fpm.conf文件默认监听的就是该端口,如果想要监听其他端口可以修改该文件配置即可。

在上面,我们看到Nginx提供了fastcgi模块来将http请求映射为对应的fastcgi请求。

img
[root@VM_16_4_centos conf]# netstat -anp|grep 9000
tcp        0      0 127.0.0.1:9000          0.0.0.0:*               LISTEN      27792/php-fpm: mast 

所以,当我们访问www.xxx.com/index.php的时候,其整个访问路径如下:

 www.example.com/index.php
        |
        |
      Nginx
        |
        |
php-fpm监听127.0.0.1:9000地址
        |
        |
www.example.com/index.php请求转发到127.0.0.1:9000
        |
        |
nginx的fastcgi模块将http请求映射为fastcgi请求
        |
        | 
  php-fpm监听fastcgi请求
        |
        | 
php-fpm接收到请求,并通过worker进程处理请求
        |
        | 
php-fpm处理完请求,返回给nginx
        |
        | 
     nginx返回结果...

Nginx与php-fpm通信的两种方式

在linux上,nginx与php-fpm有两种通信方式:tcp socket和unix socket。在上一届中,我们配置文件使用的是tcp socket的通信方式。

tcp socket

tcp socket通信方式,需要在nginx配置文件中填写php-fpm运行的ip地址和端口号,该方式支持跨服务器,即nginx和php-fpm不再统一机器上时。

location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;;
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
}

unix socket

unix socket通信方式,需要在nginx配置文件中填写php-fpm运行的pid文件地址。unix socket又叫IPC(inter process communication进程间通信)socket,用于实现统一主机上进程间通信。

location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
}

tcp socket与unix socket的区别

如下图所示,由于 Unix socket 不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。所以其效率比 tcp socket 的方式要高,可减少不必要的 tcp 开销。不过,unix socket 高并发时不稳定,连接数爆发时,会产生大量的长时缓存,在没有面向连接协议的支撑下,大数据包可能会直接出错不返回异常。而 tcp 这样的面向连接的协议,可以更好的保证通信的正确性和完整性。

所以,如果面临的是高并发业务,则考虑优先使用更可靠的tcp socket,我们可以通过负载均衡、内核优化等手段来提供效率。

Summary of Communication Mechanism between PHP-FPM and Nginx

转载请备注来源: 《nginx与php-fpm通信原理详解》 | shuwoom.com

打赏

发表评论

电子邮件地址不会被公开。