`
simohayha
  • 浏览: 1386299 次
  • 性别: Icon_minigender_1
  • 来自: 火星
社区版块
存档分类
最新评论

nginx的进程模型

阅读更多
nginx采用的也是大部分http服务器的做法,就是master,worker模型,一个master进程管理站个或者多个worker进程,基本的事件处理都是放在woker中,master负责一些全局初始化,以及对worker的管理。

在nginx中master和worker的通信是通过socketpair来实现的,每次fork完一个子进程之后,将这个子进程的socketpaire句柄传递给前面已经存在的子进程,这样子进程之间也就可以通信了。


nginx中fork子进程是在ngx_spawn_process中进行的:

第一个参数是全局的配置,第二个参数是子进程需要执行的函数,第三个参数是proc的参数。第四个类型。
ngx_pid_t
ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
    char *name, ngx_int_t respawn)


这个函数主要的任务就是:

1 有一个ngx_processes全局数组,包含了所有的存货的子进程,这里会fork出来的子进程放入到相应的位置。并设置这个进程的相关属性。

2 创建socketpair,并设置相关属性。


3 在子进程中执行传递进来的函数。

在看详细代码之前,我们先来看几个主要的数据结构:

首先是进程结构,这个结构体表示了一个进程。包含了它的id状态,channel等等。
typedef struct {
///进程id
    ngx_pid_t           pid;
///进程的退出状态(主要在waitpid中进行处理).
    int                 status;
///进程channel(也就是通过socketpair创建的两个句柄)
    ngx_socket_t        channel[2];

///进程的执行函数(也就是每次spawn,子进程所要执行的那个函数).
    ngx_spawn_proc_pt   proc;
    void               *data;
    char               *name;
///进程的几个状态。
    unsigned            respawn:1;
    unsigned            just_respawn:1;
    unsigned            detached:1;
    unsigned            exiting:1;
    unsigned            exited:1;
} ngx_process_t;


下面我们来看详细的代码。
先来看第一部分:

 
//全局的进程表,保存了存活的子进程。
ngx_process_t    ngx_processes[NGX_MAX_PROCESSES];
...................................
u_long     on;
    ngx_pid_t  pid;
///表示将要fork的子进程在ngx_processes中的位置,
    ngx_int_t  s;
///首先,如果传递进来的类型大于0,则就是已经确定这个进程已经退出,我们就可以直接确定slot。
    if (respawn >= 0) {
        s = respawn;

    } else {
///遍历ngx_processess,从而找到空闲的slot,从而等会fork完毕后,将子进程信息放入全局进程信息表的相应的slot。
        for (s = 0; s < ngx_last_process; s++) {
            if (ngx_processes[s].pid == -1) {
                break;
            }
        }
///到达最大进程限制报错。
        if (s == NGX_MAX_PROCESSES) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                          "no more than %d processes can be spawned",
                          NGX_MAX_PROCESSES);
            return NGX_INVALID_PID;
        }
    }



接下来新建一对socketpair句柄,然后初始化相关属性。


///如果类型为NGX_PROCESS_DETACHED,则说明是热代码替换(热代码替换也是通过这个函数进行处理的),因此不需要新建socketpair。
if (respawn != NGX_PROCESS_DETACHED) {

        /* Solaris 9 still has no AF_LOCAL */
///建立socketpair
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)
        {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "socketpair() failed while spawning \"%s\"", name);
            return NGX_INVALID_PID;
        }
        。。。。。。。。。。。。。。。。。。。。。。。。。。。。
///设置非阻塞模式
        if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) {
........................................................
        }

        if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) {
........................................
        }

///打开异步模式
        on = 1;
        if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1) {
.................................................
        }
///设置异步io的所有者
        if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) {

..............................................
        }
///当exec后关闭句柄。
        if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) {................................................
        }

        if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) {
  。。。。。。。。。。。。。。。。。。。。。。。。。。
        }
///设置当前的子进程的句柄
        ngx_channel = ngx_processes[s].channel[1];

    } else {
        ngx_processes[s].channel[0] = -1;
        ngx_processes[s].channel[1] = -1;
    }




接下来就是fork子进程,并设置进程相关参数。
///设置进程在进程表中的slot。
ngx_process_slot = s;


    pid = fork();

    switch (pid) {

    case -1:
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "fork() failed while spawning \"%s\"", name);
        ngx_close_channel(ngx_processes[s].channel, cycle->log);
        return NGX_INVALID_PID;

    case 0
///子进程,因此执行传递进来的子进程的函数
        ngx_pid = ngx_getpid();
        proc(cycle, data);
        break;

    default:
        break;
    }

    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start %s %P", name, pid);

    ngx_processes[s].pid = pid;
    ngx_processes[s].exited = 0;

///如果大于0,则说明我们确定了重启的子进程,因此下面的初始化就用已死的子进程的就够了。
    if (respawn >= 0) {
        return pid;
    }
///开始初始化进程结构。
    ngx_processes[s].proc = proc;
    ngx_processes[s].data = data;
    ngx_processes[s].name = name;
    ngx_processes[s].exiting = 0;


///设置相关状态。
    switch (respawn) {

    case NGX_PROCESS_RESPAWN:
        ngx_processes[s].respawn = 1;
        ngx_processes[s].just_respawn = 0;
        ngx_processes[s].detached = 0;
        break;

    case NGX_PROCESS_JUST_RESPAWN:
        ngx_processes[s].respawn = 1;
        ngx_processes[s].just_respawn = 1;
        ngx_processes[s].detached = 0;
        break;

    case NGX_PROCESS_DETACHED:
        ngx_processes[s].respawn = 0;
        ngx_processes[s].just_respawn = 0;
        ngx_processes[s].detached = 1;
        break;
    }

    if (s == ngx_last_process) {
        ngx_last_process++;
    }

return pid;


这里有个问题,那就是后面fork的子进程如何来让前面已经fork的子进程得到自己的进程相关信息呢。在nginx中是每次新的子进程fork完毕后,然后父进程此时将这个子进程id,以及流管道的句柄channel[0]传递给前面的子进程。这样子进程之间也可以通信了。

先来看相关的数据结构:
///封装了父子进程之间传递的信息。
typedef struct {
///对端将要做得命令。
     ngx_uint_t  command;
///当前的子进程id
     ngx_pid_t   pid;
///在全局进程表中的位置
     ngx_int_t   slot;
///传递的fd
     ngx_fd_t    fd;
} ngx_channel_t;



接下来来看代码:

static void
ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
{
    ngx_int_t      i, s;
    ngx_channel_t  ch;

....................................
///传递给其他子进程的命令
    ch.command = NGX_CMD_OPEN_CHANNEL;

///这里n,就是从配置文件中读取的,需要几个子进程。
    for (i = 0; i < n; i++) {

        cpu_affinity = ngx_get_cpu_affinity(i);

///这个函数刚才介绍过了。就是fork子进程。
        ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL,
                          "worker process", type);
///初始化channel,ngx_process_slot这个我们在上面的spawn函数中已经赋值完毕,就是当前子进程的位置。
        ch.pid = ngx_processes[ngx_process_slot].pid;
        ch.slot = ngx_process_slot;
        ch.fd = ngx_processes[ngx_process_slot].channel[0];

///遍历整个进程表
        for (s = 0; s < ngx_last_process; s++) {
///遇到非存活的进程就跳过。
            if (s == ngx_process_slot
                || ngx_processes[s].pid == -1
                || ngx_processes[s].channel[0] == -1)
            {
                continue;
            }

            ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0,
                          "pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d",
                          ch.slot, ch.pid, ch.fd,
                          s, ngx_processes[s].pid,
                          ngx_processes[s].channel[0]);

            /* TODO: NGX_AGAIN */
///然后传递这个channel给其他子进程(主要是传递句柄)。
            ngx_write_channel(ngx_processes[s].channel[0],
                              &ch, sizeof(ngx_channel_t), cycle->log);
        }
    }
}


而在子进程中是如何处理的呢,子进程的管道可读事件捕捉函数是ngx_channel_handler(ngx_event_t *ev),在这个函数中,会读取mseeage,然后解析,并根据不同的命令做不同的处理,来看它的代码片断:

///这里ch为读取的channel。

        switch (ch.command) {

        case NGX_CMD_QUIT:
            ngx_quit = 1;
            break;

        case NGX_CMD_TERMINATE:
            ngx_terminate = 1;
            break;

        case NGX_CMD_REOPEN:
            ngx_reopen = 1;
            break;

        case NGX_CMD_OPEN_CHANNEL:
///可以看到操作很简单,就是对ngx_processes全局进程表进行赋值。
            ngx_processes[ch.slot].pid = ch.pid;
            ngx_processes[ch.slot].channel[0] = ch.fd;
            break;

        case NGX_CMD_CLOSE_CHANNEL:
.....................................................

            if (close(ngx_processes[ch.slot].channel[0]) == -1) {
                ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
                              "close() channel failed");
            }

            ngx_processes[ch.slot].channel[0] = -1;
            break;
        }


接下来详细的来看worker和master如何进行交互,以及master如何同外部进行交互(比如热代码替换,reconfig这些操作)。

在nginx中,worker和master的交互,我们前面已经提过了,就是通过流管道以及信号,而master与外部的交互是通过信号来进行的。

在看master得主循环之前,我们先来看信号处理和函数,在nginx中,父子进程的信号处理函数是相同的,只不过有一个变量在master和worker中赋值不同,以此来区分。

在信号处理中,通过设置相应的标志变量,从而在主循环中,判断这些变量,从而做相应的操作。

///定义的信号值。
#define NGX_SHUTDOWN_SIGNAL      QUIT
#define NGX_TERMINATE_SIGNAL     TERM
#define NGX_NOACCEPT_SIGNAL      WINCH
#define NGX_RECONFIGURE_SIGNAL   HUP

#if (NGX_LINUXTHREADS)
#define NGX_REOPEN_SIGNAL        INFO
#define NGX_CHANGEBIN_SIGNAL     XCPU
#else
#define NGX_REOPEN_SIGNAL        USR1
#define NGX_CHANGEBIN_SIGNAL     USR2
#endif


void
ngx_signal_handler(int signo)
{
    char            *action;
    ngx_int_t        ignore;
    ngx_err_t        err;
    ngx_signal_t    *sig;

    ignore = 0;

    err = ngx_errno;

///首先得到当前的信号值
    for (sig = signals; sig->signo != 0; sig++) {
        if (sig->signo == signo) {
            break;
        }
    }

    ngx_time_update(0, 0);

    action = "";

///这里ngx_process在master和worker中赋值不同。
    switch (ngx_process) {
///master中。
    case NGX_PROCESS_MASTER:
    case NGX_PROCESS_SINGLE:
        switch (signo) {

        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
///如果接受到quit信号,则准备退出进程。
            ngx_quit = 1;
            action = ", shutting down";
            break;

        case ngx_signal_value(NGX_TERMINATE_SIGNAL):
        case SIGINT:
///sigint信号,则
            ngx_terminate = 1;
            action = ", exiting";
            break;

        case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
///winch信号,停止接受accept。
            ngx_noaccept = 1;
            action = ", stop accepting connections";
            break;

        case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
///sighup信号用来reconfig
            ngx_reconfigure = 1;
            action = ", reconfiguring";
            break;

        case ngx_signal_value(NGX_REOPEN_SIGNAL):
///用户信号,用来reopen
            ngx_reopen = 1;
            action = ", reopening logs";
            break;
///热代码替换.
        case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
            if (getppid() > 1 || ngx_new_binary > 0) {

                /*
                 * Ignore the signal in the new binary if its parent is
                 * not the init process, i.e. the old binary's process
                 * is still running.  Or ignore the signal in the old binary's
                 * process if the new binary's process is already running.
                 */
///上面注释很详细,我就不解释了。。
                action = ", ignoring";
                ignore = 1;
                break;
            }
///正常情况下,需要热代码替换。设置标志位
            ngx_change_binary = 1;
            action = ", changing binary";
            break;

        case SIGALRM:
            break;

        case SIGIO:
            ngx_sigio = 1;
            break;

        case SIGCHLD:
///子进程已退出,设置标记。
            ngx_reap = 1;
            break;
        }

        break;
///worker的信号处理。worker的比较简单。
    case NGX_PROCESS_WORKER:
        switch (signo) {

        case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
            ngx_debug_quit = 1;
        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
            ngx_quit = 1;
            action = ", shutting down";
            break;

        case ngx_signal_value(NGX_TERMINATE_SIGNAL):
        case SIGINT:
            ngx_terminate = 1;
            action = ", exiting";
            break;

        case ngx_signal_value(NGX_REOPEN_SIGNAL):
            ngx_reopen = 1;
            action = ", reopening logs";
            break;

 ...............................................
        }

        break;
    }

................................................

///最终如果信号是sigchld,我们收割僵尸进程(用waitpid)。
    if (signo == SIGCHLD) {
        ngx_process_get_status();
    }

    ngx_set_errno(err);
}


先来看master的主循环,处理其实很简单,就是在循环过程中判断相应的条件,然后进入相应的处理。这里的相关标志位基本都是在上面的信号处理函数中赋值的。:


for ( ;; ) {
///delay用来等待子进程退出的时间,由于我们接受到SIGINT信号后,我们需要先发送信号给子进程,而子进程的退出需要一定的时间,超时时如果子进程已退出,我们父进程就直接退出,否则发送sigkill信号给子进程(强制退出),然后再退出。
        if (delay) {
            delay *= 2;
..............................................

            itv.it_interval.tv_sec = 0;
            itv.it_interval.tv_usec = 0;
            itv.it_value.tv_sec = delay / 1000;
            itv.it_value.tv_usec = (delay % 1000 ) * 1000;
///设置定时器。
            if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                              "setitimer() failed");
            }
        }
///延时,等待定时器。

        sigsuspend(&set);

        ngx_time_update(0, 0);

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "wake up");

///ngx_reap为1,说明有子进程已经退出。
        if (ngx_reap) {
            ngx_reap = 0;
            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
///这个里面处理退出的子进程(有的worker异常退出,这时我们就需要重启这个worker ),如果所有子进程都退出则会返回0.
            live = ngx_reap_children(cycle);
        }

///如果没有存活的子进程,并且收到了ngx_terminate或者ngx_quit信号,则master退出。
        if (!live && (ngx_terminate || ngx_quit)) {
            ngx_master_process_exit(cycle);
        }
///收到了sigint信号。
        if (ngx_terminate) {
///设置延时。
            if (delay == 0) {
                delay = 50;
            }

            if (delay > 1000) {
///如果超时,则强制杀死worker
                ngx_signal_worker_processes(cycle, SIGKILL);
            } else {
///负责发送sigint给worker,让它退出。
                ngx_signal_worker_processes(cycle,
                                       ngx_signal_value(NGX_TERMINATE_SIGNAL));
            }

            continue;
        }

///收到quit信号。
        if (ngx_quit) {
///发送给worker quit信号
            ngx_signal_worker_processes(cycle,
                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));

            ls = cycle->listening.elts;
            for (n = 0; n < cycle->listening.nelts; n++) {
                if (ngx_close_socket(ls[n].fd) == -1) {
                    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
                                  ngx_close_socket_n " %V failed",
                                  &ls[n].addr_text);
                }
            }
            cycle->listening.nelts = 0;

            continue;
        }

///收到需要reconfig的信号
        if (ngx_reconfigure) {
            ngx_reconfigure = 0;
///判断是否热代码替换后的新的代码还在运行中(也就是还没退出当前的master)。如果还在运行中,则不需要重新初始化config。
            if (ngx_new_binary) {
                ngx_start_worker_processes(cycle, ccf->worker_processes,
                                           NGX_PROCESS_RESPAWN);
                ngx_start_cache_manager_process(cycle, NGX_PROCESS_RESPAWN);
                ngx_noaccepting = 0;

                continue;
            }

            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");
///重新初始化config,并重新启动新的worker
            cycle = ngx_init_cycle(cycle);
            if (cycle == NULL) {
                cycle = (ngx_cycle_t *) ngx_cycle;
                continue;
            }

            ngx_cycle = cycle;
            ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
                                                   ngx_core_module);
            ngx_start_worker_processes(cycle, ccf->worker_processes,
                                       NGX_PROCESS_JUST_RESPAWN);
            ngx_start_cache_manager_process(cycle, NGX_PROCESS_JUST_RESPAWN);
            live = 1;
            ngx_signal_worker_processes(cycle,
                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
        }
///这个标志没弄懂有什么意义。代码里面是当热代码替换后,如果ngx_noacceptig被设置了,则设置这个标志位(难道意思是热代码替换前要先停止当前的accept连接?)
        if (ngx_restart) {
            ngx_restart = 0;
            ngx_start_worker_processes(cycle, ccf->worker_processes,
                                       NGX_PROCESS_RESPAWN);
            ngx_start_cache_manager_process(cycle, NGX_PROCESS_RESPAWN);
            live = 1;
        }
///重新打开log
        if (ngx_reopen) {
            ngx_reopen = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
            ngx_reopen_files(cycle, ccf->user);
            ngx_signal_worker_processes(cycle,
                                        ngx_signal_value(NGX_REOPEN_SIGNAL));
        }

///热代码替换
        if (ngx_change_binary) {
            ngx_change_binary = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary");
///进行热代码替换,这里是调用execve来执行新的代码。
            ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);
        }
///接受到停止accept连接,其实也就是worker退出(有区别的是,这里master不需要退出).。
        if (ngx_noaccept) {
            ngx_noaccept = 0;
            ngx_noaccepting = 1;
///给worker发送信号。
            ngx_signal_worker_processes(cycle,
                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
        }
    }
}



然后来看worker的主循环,worker的比较简单。逻辑和master的很相似:

  
 for ( ;; ) {
///ngx_exiting是当收到master的quit命令后,设置为1,然后等待其他资源退出。
        if (ngx_exiting) {

            c = cycle->connections;
.............................................
///定时器超时则退出worker
            if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)
            {
                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");

                ngx_worker_process_exit(cycle);
            }
        }

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");

        ngx_process_events_and_timers(cycle);

///收到shutdown命令则worker直接退出
        if (ngx_terminate) {
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");

            ngx_worker_process_exit(cycle);
        }

///收到quit命令
        if (ngx_quit) {
            ngx_quit = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
                          "gracefully shutting down");
            ngx_setproctitle("worker process is shutting down");

            if (!ngx_exiting) {
///关闭socket,然后设置退出标志。
                ngx_close_listening_sockets(cycle);
                ngx_exiting = 1;
            }
        }

///收到master重新打开log的命令。
        if (ngx_reopen) {
            ngx_reopen = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
            ngx_reopen_files(cycle, -1);
        }
    }


8
8
分享到:
评论
2 楼 singolang 2013-06-09  
我必定登陆来加油
1 楼 Spike 2009-09-14  
感谢博主分享

相关推荐

    从nginx角度看服务器多进程模型设计

    从nginx角度看服务器多进程模型设计,这是一些用来分析nginx的设计思路的资料,从中可以得到很多启发。

    深入剖析Nginx

    然后,分别深入分析了Nginx的进程模型、数据结构、配置指令、主要功能模块、I/O事件处理、变量机制、客户端请求过程、Filter模块实例、负载均衡策略以及Handler模块等。附录部分提供了Nginx的编译模块、运行配置等...

    2022版Nginx教程(进阶高级,架构师必备)百度链接.rar

    核心技术篇:Nginx快速上手 Nginx安装部署,配合大量在线实操,搞定Nginx七大核心应用场景:反向...├──09-基本使用-Nginx多进程模型和基本请求流程.mp4 25.58M ├──10-基本使用-Nginx.conf 最小配置解析.mp4 319.

    nginx v1.5.9 for windows 源程序

    Nginx是一个很强大的高性能Web和反向代理服务器 它具有很多非常优越的特性 Nginx可以在大多数 Unix like OS 上编译运行 在Linux和unix上分别采用epoll和kqueue网络模型 能够支持高达 50 000 个并发连接数的响应...

    nginx v1.5.9 for windows

    Nginx是一个很强大的高性能Web和反向代理服务器 它具有很多非常优越的特性 Nginx可以在大多数 Unix like OS 上编译运行 在Linux和unix上分别采用epoll和kqueue网络模型 能够支持高达 50 000 个并发连接数的响应...

    nginx服务器

    与apache使用面向进程或线程的方式处理请求不同,nginx使用异步事件驱动模型在负载下性能更突出。 虽然nginx能高效地服务静态文件,但也有人认为nginx处理动态内容并不理想。不像apache服务器,nginx没用使用内嵌...

    深入理解Nginx 模块开发与架构解析

    现今nginx的最新版本是nginx-1.13.6,代码量也日渐庞大,但是由于其核心思想并没改变,为了降低阅读难度,我选择的是nginx-1.0.15版本,并且由于时间和水平有限,重点关注的是nginx的启动以及进程模型、事件模块中的epoll...

    Nginx源码剖析

    本书详细介绍了Nginx的进程模型,内存管理,request请求的解析,handler的处理等

    Nginx笔记-Nginx中进程结构及使用Linux信号量管理

    下面是Nginx处理请求的过程: Nginx有点及特点 热部署、平滑升级、开源及二次开发、高并发高性能、扩展性好、异步非阻塞的事件驱动模型 Nginx是多进程结构: 主进程Master Process管理如下几个进程: CM(Cacah ...

    高并发Nginx+lua是如何抗住的

    本篇顺序:1、Nginx如何抗住的高并发,工作模式是怎样的,利用了哪些技术2、常见的IO模型及异步非阻塞IO的优势3、epoll相对于其他模型为何这么强大Nginx...,并不会每一个新进程都会起一个新的进程或者线程来处请求...

    Nginx+Tomcat负载均衡

    另外我把实现过程中遇到的知识点都总结好了,一起提供给大家学习。 步骤: 注:本例程以一台win7机器为例子,即同一台机器上装一个nginx和2个Tomcat。 且安装了JDK。 便于管理将用到的资料放在一个文件夹下 ...

    Ngwsx(Nginx for windows) v1.0.4.0

    *) 支持单进程和主从进程(主进程/工作进程)两种工作模式, 启用IOCP事件模块只支持单个工作进程,启用Select事件模块可支持多个工作进程。*) 使用AcceptEx和ConnectEx等WinSock扩展函数。*) 静态链接PCRE和ZLIB库...

    Nginx配置优化手册.docx

    Nginx配置优化手册 1. Nginx优化配置 1 ...1.2 事件模型优化 1 1.3 连接数优化 1 1.4 进程最大打开文件数优化 2 1.5 文件传输优化 2 1.6 超时时间配置 2 1.7 开启GZIP压缩 3 1.8 配置日志异步写入 4

    利用nginx与ffmpeg搭建流媒体服务器过程详解

    通过nginx+ffmpeg还可以实现推流、拉流、转推甚至利用FFmpeg实时切片、视频处理等,实现一套直播服务模型。 环境 系统环境:CentOS release 6.7 (Final) 步骤 安装ffmpeg 安装过程参考官方文档:...

    nginx中共享内存的使用详解

    在nginx的进程模型下,类似流量统计、流量控制、数据共享、等需要多个工作进程共同配合完成任务,共享内存是一个重要的进程通讯的方案。本文介绍在nginx的代码中与共享内存相关的功能,包括ngx_shmem与ngx_slab的...

    nginx_lua_framework:Siva 是一个(非常)简单且轻量级的基于 ngx-openresty 的 Web 框架

    打造高性能Web平台Nginx定制开发实战-Siva由 Lua 粘合的 Nginx 生态环境AJAX 化和 Service 化的趋势让所有东西开始讲 –RESTful天下武功,无坚不破,唯快不...nginx 是单线程模型事件驱动的机制I/O 多路复用Nginx进程模

    从零开始设计并构建“金科云盾”网站防护系统(全国云计算应用创新大赛一等奖作品),使用Nginx实现反向代理,负载均衡,统一日志

    完成深度学习平台的搭建,并导入相关恶意访问识别算法LSTM,完成对于样本数据的模型训练以及功能测试,能够查看到训练的过程以及测试的结果。通过脚本,每半分钟“巡逻”一次,让模型对数据实时地进行识别预测,得到...

    Angular项目从新建、打包到nginx部署全过程记录

    前言 当前,AngularJS作为Javascript的MVC(也有...当用户通过点击或者敲击键盘和应用交互时,controller通过改变模型中的数据进行响应。最终,view得到了发生在model中的变化这个通知,从而它能更新展示的内容。 最近

Global site tag (gtag.js) - Google Analytics