nginx event初始化流程

2024-08-24 16:18
文章标签 流程 nginx 初始化 event

本文主要是介绍nginx event初始化流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.在nginx.c中设置每个核心模块的index
    ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
ngx_modules[i]->index = ngx_max_module++;
}

2.进入函数ngx_init_cycle,调用每个核心模块的create_conf
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_CORE_MODULE) {
continue;
}
module = ngx_modules[i]->ctx;
if (module->create_conf) {
rv = module->create_conf(cycle);//
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
cycle->conf_ctx[ngx_modules[i]->index] = rv;
}
}

3.在函数ngx_init_cycle中调用函数ngx_conf_pararm创建一个解析配置信息用的临时buffer
ngx_conf_param(&conf)

4.调用ngx_conf_param设置解析需要的关键字
enum {
parse_file = 0,
parse_block,
parse_param
} type;

5.调用ngx_conf_parse(&conf, &cycle->conf_file)函数解析核心模块配置文件:
注意:这里的ngx_conf_parse函数确实仅仅处理了核心模块的配置文件.他每次读到一个token(ngx_conf_read_token),就解析一个token(ngx_conf_handler)
    
  rc = ngx_conf_read_token(cf); //读取token
/* 返回值:
* ngx_conf_read_token() may return
*
*    NGX_ERROR             there is error
*    NGX_OK                the token terminated by ";" was found
*    NGX_CONF_BLOCK_START  the token terminated by "{" was found
*    NGX_CONF_BLOCK_DONE   the "}" was found
*    NGX_CONF_FILE_DONE    the configuration file is done
*/
rc = ngx_conf_handler(cf, rc); //处理token 

6.进入ngx_conf_handler中,看一下关键代码
.............
cmd = ngx_modules[i]->commands;
.........
rv = cmd->set(cf, cmd, conf);
................

7.为了弄懂这几句是干什么的,我们先来看一下ngx_modules是什么
struct ngx_module_s {
ngx_uint_t            ctx_index;
ngx_uint_t            index;
............
void                 *ctx;
ngx_command_t        *commands;
ngx_uint_t            type;
.............
};

8.我们以前讲过,每个模块都会实现自己的ngx_module_s 
那么我么再来看一下event是怎么具体实现的:
ngx_module_t  ngx_events_module = {
NGX_MODULE_V1,
&ngx_events_module_ctx,                /* module context */
ngx_events_commands,                   /* module directives */
NGX_CORE_MODULE,                       /* module type */
....................
};

9.我们再来看一下cmd是什么结构体:
struct ngx_command_s {
ngx_str_t             name;
ngx_uint_t            type;
char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_uint_t            conf;
ngx_uint_t            offset;
void                 *post;
};

10.我们再来看一下event是怎么实现command的:
static ngx_command_t  ngx_events_commands[] = {
{ ngx_string("events"),
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
ngx_events_block,
........
};

11.我们在第六步中那几个语句,现在可以回头看一下,那几条语句做了什么是不是很清晰.好了我们在这里再讲一下第六步.
//将cmd指向command
cmd = ngx_modules[i]->commands;
//回调函数,对每个模块进行具体配置
rv = cmd->set(cf, cmd, conf);

12.对于核心模块event,调用ngx_events_block钩子函数,我们开始解析一下这个函数都干了什么:
static char *
ngx_events_block(ngx_conf_t *cf , ngx_command_t *cmd, void *conf)
{
char                 * rv;
void               *** ctx;
ngx_uint_t            i;
ngx_conf_t            pcf;
ngx_event_module_t   * m;
if (*(void **) conf) {
return "is duplicate" ;
}
/* count the number of the event modules and set up their indices */
//初始化所有事件模块的ctx_index
ngx_event_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i ]->type != NGX_EVENT_MODULE) {
continue;
}
ngx_modules[ i]->ctx_index = ngx_event_max_module++;
}
//创建配置需要的结构体空间
ctx = ngx_pcalloc(cf ->pool, sizeof( void *));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
* ctx = ngx_pcalloc(cf ->pool, ngx_event_max_module * sizeof(void *));
if (*ctx == NULL) {
return NGX_CONF_ERROR;
}
*( void **) conf = ctx;
//调用所有事件模块的create_conf
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i ]->type != NGX_EVENT_MODULE) {
continue;
}
m = ngx_modules[i ]->ctx;
if (m ->create_conf) {
(* ctx)[ngx_modules[i ]->ctx_index] = m->create_conf (cf-> cycle);
if ((*ctx )[ngx_modules[i]-> ctx_index] == NULL ) {
return NGX_CONF_ERROR;
}
}
}
pcf = *cf ;
cf->ctx = ctx;
cf->module_type = NGX_EVENT_MODULE;
cf->cmd_type = NGX_EVENT_CONF;
//为模块event所有模块解析配置
rv = ngx_conf_parse(cf , NULL);
* cf = pcf ;
if (rv != NGX_CONF_OK)
return rv ;
//为event所有模块调用init_conf
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i ]->type != NGX_EVENT_MODULE) {
continue;
}
m = ngx_modules[i ]->ctx;
if (m ->init_conf) {
rv = m ->init_conf( cf->cycle , (*ctx)[ngx_modules[ i]->ctx_index ]);
if (rv != NGX_CONF_OK) {
return rv ;
}
}
}
return NGX_CONF_OK;
}

13.在第11步中,循环调用所有模块的command函数(set钩子),仅仅初始化每个核心模块的配置.然后每个核心模块在递归调用ngx_conf_parse函数,对子模块进行配置.对于event模块来说,必须先初始化ngx_events_module,然后初始化ngx_event_core_module,对于其他event模块就没有强烈的顺序要求.原因:第一个初始化ngx_events_module模块,会对event子模块进行配置,然后才可以使用子模块.但是为什么在event子模块中,必须先调用ngx_event_core_module模块呢?
我们来具体看一下ngx_event_core_module结构体中的ngx_event_core_commands结构:
static ngx_command_t  ngx_event_core_commands[] = {
//连接池的大小,即每个worker进程中支持的最大连接数
//他与下面的connections配置项的意义是重复的
{ ngx_string("worker_connections"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_connections,
0,
0,
NULL },
//连接池的大小,与上一项配置重复
{ ngx_string("connections"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_connections,
0,
0,
NULL },
//确定哪一个事件模块作为事件驱动机制
{ ngx_string("use"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_use,
0,
0,
NULL },
//对于epoll事件驱动模式来说,当接收一个新链接事件时候,
//调用accept尽可能多的接收连接
{ ngx_string("multi_accept"),
NGX_EVENT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_event_conf_t, multi_accept),
NULL },
//确定是否使用负载均衡锁,默认开启
{ ngx_string("accept_mutex"),
NGX_EVENT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_event_conf_t, accept_mutex),
NULL },
//启动负载均衡锁以后,延迟accept_mutex_delay毫秒以后再进行连接
{ ngx_string("accept_mutex_delay"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
0,
offsetof(ngx_event_conf_t, accept_mutex_delay),
NULL },
//对于来自指定ip的连接需要打印debug级别的日志.
{ ngx_string("debug_connection"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_debug_connection,
0,
0,
NULL },
ngx_null_command
};
我们看到了ngx_event_core_commands 定义了于配置相关的信息,他会选择各种机制进行相应配置,所以必须优先进行初始化才能保证模块的正常运行.

14.下面我们看一下该模块定义的用于存储配置项的结构体.每个event子模块的配置文件都会存储在一个叫做 ngx_event_conf_t 的结构体中.
typedef struct {
//连接池大小
ngx_uint_t    connections;
//选用的事件模块在所有事件模块的编号,即ctx_index
ngx_uint_t    use;
//标志位,若为1,则在接收一个连接事件时候,一次性尽可能建立多个连接
ngx_flag_t    multi_accept;
//标志位,为1时候采用负载均衡锁
ngx_flag_t    accept_mutex;
//负载均衡锁会使有些worker进程在拿不到锁的时候延迟一段时间
//这段时间就是用accept_mutex_delay表示的
ngx_msec_t    accept_mutex_delay;
//所选用的事件模块的名字,他与use是匹配的.
u_char       *name;
} ngx_event_conf_t;

每个事件模块 上下文(ctx)都要会指向一个ngx_event_module_t结构体(即每个event模块都要实现一个 ngx_event_module_t接口 ),用于具体定义该模块操作.
ngx_event_module_t  ngx_event_core_module_ctx = {
&event_core_name,
ngx_event_core_create_conf,            /* create configuration */
ngx_event_core_init_conf,              /* init configuration */
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};

最后我们再来看一下ngx_event_core_module 的定义.
ngx_module_t  ngx_event_core_module = {
NGX_MODULE_V1,
&ngx_event_core_module_ctx,            /* module context */
ngx_event_core_commands,               /* module directives */
NGX_EVENT_MODULE,                      /* module type */
NULL,                                  /* init master */
ngx_event_module_init,                 /* init module */
ngx_event_process_init,                /* init process */
NULL,                                  /* init thread */
NULL,                                  /* exit thread */
NULL,                                  /* exit process */
NULL,                                  /* exit master */
NGX_MODULE_V1_PADDING
};

15.在ngx_cycle_init函数中找到如下代码: 
   //调用所有模块的init_module
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->init_module) {
if (ngx_modules[i]->init_module(cycle) != NGX_OK) {
/* fatal */
exit(1);
}
}
}
对于event来说则是调用ngx_event_module_init函数:
//src/event/ngx_event.c
static ngx_int_t
ngx_event_module_init(ngx_cycle_t *cycle)
{
void              ***cf;
u_char              *shared;
size_t               size, cl;
ngx_shm_t            shm;
ngx_time_t          *tp;
ngx_core_conf_t     *ccf;
ngx_event_conf_t    *ecf;
//判断ngx_events_module是否调用过初始化conf操作
cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);
if (cf == NULL) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
"no \"events\" section in configuration");
return NGX_ERROR;
}
//获取ngx_event_core_module模块的配置结构
ecf = (*cf)[ngx_event_core_module.ctx_index];
//查看是否是event中的模块,例如use 。。。。
if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
"using the \"%s\" event method", ecf->name);
}
//获取ngx_core_module模块的配置结构
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
//从ngx_core_module模块的配置结构中获取timer_resolution参数
ngx_timer_resolution = ccf->timer_resolution;
#if !(NGX_WIN32)
{
ngx_int_t      limit;
struct rlimit  rlmt;
//获取当前进程能够打开的最大文件数     man getrlimit
if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"getrlimit(RLIMIT_NOFILE) failed, ignored");
} else {
//如果ngx_event_core_module模块连接数大于当前(软)限制
//并且ngx_core_module最大连接数无限制
//或者ngx_event_core_module连接数大于ngx_core_module最大连接数
if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur
&& (ccf->rlimit_nofile == NGX_CONF_UNSET
|| ecf->connections > (ngx_uint_t) ccf->rlimit_nofile))
{
limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ?
(ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile;
ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
"%ui worker_connections are more than "
"open file resource limit: %i",
ecf->connections, limit);
}
}
}
#endif /* !(NGX_WIN32) */
//如果关闭了master进程,就返回
//因为关闭了master进程就是单进程工作方式,
//之后的操作时创建共享内存实现锁等工作,单进程不需要。
if (ccf->master == 0) {
return NGX_OK;
}
//如果已经存在accept互斥体了,不需要再重复创建了
if (ngx_accept_mutex_ptr) {
return NGX_OK;
}
/* cl should be equal or bigger than cache line size */
cl = 128;
//这里创建size大小的共享内存,这块共享内存将被均分成三段
size = cl            /* ngx_accept_mutex */
+ cl          /* ngx_connection_counter */
+ cl;         /* ngx_temp_number */
//准备共享内存,大小为size,命名nginx_shared_zone,
shm.size = size;
shm.name.len = sizeof("nginx_shared_zone");
shm.name.data = (u_char *) "nginx_shared_zone";
shm.log = cycle->log;
//创建共享内存,起始地址保存在shm.addr
if (ngx_shm_alloc(&shm) != NGX_OK) {
return NGX_ERROR;
}
//获取起始地址保存
shared = shm.addr;
//accept互斥体取得共享内存的第一段cl大小内存
ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;
ngx_accept_mutex.spin = (ngx_uint_t) -1;
/*创建accept互斥体
accept互斥体的实现依赖是否支持原子操作,如果有相应的原子操作;
就是用取得的这段共享内存来实现accept互斥体;否则,将使用文件锁
来实现accept互斥体。
accept互斥体的作用是:避免惊群和实现worker进程的负载均衡。
*/
if (ngx_shmtx_create(&ngx_accept_mutex, shared, cycle->lock_file.data)
!= NGX_OK)
{
return NGX_ERROR;
}
//获取内存的第二段cl大小的地址
ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);
(void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"counter: %p, %d",
ngx_connection_counter, *ngx_connection_counter);
//获取内存的第三段cl大小的地址
ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);
tp = ngx_timeofday();
ngx_random_number = (tp->msec << 16) + ngx_pid;
return NGX_OK;
}

16.进入master或者signal工作模式后才调用ngx_event_process_init函数
//src/event/ngx_event.c
static ngx_int_t
ngx_event_process_init(ngx_cycle_t *cycle)
{
ngx_uint_t           m, i;
ngx_event_t         *rev, *wev;
ngx_listening_t     *ls;
ngx_connection_t    *c, *next, *old;
ngx_core_conf_t     *ccf;
ngx_event_conf_t    *ecf;
ngx_event_module_t  *module;
//和之前一样,获取响应模块的配置结构
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
//master进程打开,worker进程大于1,已经创建了accept_mutex
//才打开accept互斥体
if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
ngx_use_accept_mutex = 1; //使用互斥体
ngx_accept_mutex_held = 0; //是否获得accept互斥体
ngx_accept_mutex_delay = ecf->accept_mutex_delay;//争抢互斥体失败后,等待下次争抢时间间隔
} else {
ngx_use_accept_mutex = 0;
}
#if (NGX_THREADS)
//线程先不讲
#endif
//初始化计数器,此处将会创建一颗红黑树,来维护计时器,之后会详细讲解
if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
return NGX_ERROR;
}
for (m = 0; ngx_modules[m]; m++) {
//这里之前讲过,跳过非NGX_EVENT_MODULE模块
if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
continue;
}
//非use配置指令指定的模块跳过,linux默认epoll
if (ngx_modules[m]->ctx_index != ecf->use) {
continue;
}
module = ngx_modules[m]->ctx;
/*调用具体时间模块的init函数
由于nginx实现了很多事件模块,比如:epoll、poll、select、dqueue、aio
(这些模块位于src/event/modules目录中),所以nginx对时间模块进行了一层抽象,
方便了不同的系统使用不同的事件模型,也便于扩展新的时间模型,我们的重点应该
放在epoll上。
此处的init回调,其实就是调用了ngx_epoll_init函数。module->actions结构封装了
epoll的所有接口函数。nginx就是通过actions结构将epoll注册到事件抽象层中。
actions的类型是ngx_event_action_t,位于src/event/ngx_event.h
这些具体的内容会在下一节中重点讲解。
*/
if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
/* fatal */
exit(2);
}
break;
}
//此处省略部分内容
//创建全局的ngx_connection_t数组,保存所有的connection
//由于这个过程是在各个worker进程中执行的,所以每个worker都有自己的connection数组
cycle->connections =
ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
if (cycle->connections == NULL) {
return NGX_ERROR;
}
c = cycle->connections;
//创建一个读事件数组
cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
cycle->log);
if (cycle->read_events == NULL) {
return NGX_ERROR;
}
rev = cycle->read_events;
for (i = 0; i < cycle->connection_n; i++) {
rev[i].closed = 1;
rev[i].instance = 1;
#if (NGX_THREADS)
rev[i].lock = &c[i].lock;
rev[i].own_lock = &c[i].lock;
#endif
}
//创建一个写事件数组
cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
cycle->log);
if (cycle->write_events == NULL) {
return NGX_ERROR;
}
wev = cycle->write_events;
for (i = 0; i < cycle->connection_n; i++) {
wev[i].closed = 1;
#if (NGX_THREADS)
wev[i].lock = &c[i].lock;
wev[i].own_lock = &c[i].lock;
#endif
}
i = cycle->connection_n;
next = NULL;
//初始化整个connection数组
do {
i--;
c[i].data = next;
c[i].read = &cycle->read_events[i];
c[i].write = &cycle->write_events[i];
c[i].fd = (ngx_socket_t) -1;
next = &c[i];
#if (NGX_THREADS)
c[i].lock = 0;
#endif
} while (i);
cycle->free_connections = next;
cycle->free_connection_n = cycle->connection_n;
/* for each listening socket */
//为每个监听套接字从connection数组中分配一个连接,即一个slot
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
//从conneciton中取得一个新的连接solt
c = ngx_get_connection(ls[i].fd, cycle->log);
if (c == NULL) {
return NGX_ERROR;
}
c->log = &ls[i].log;
c->listening = &ls[i];
ls[i].connection = c;
rev = c->read;
rev->log = c->log;
rev->accept = 1; //读时间发生,调用accept
#if (NGX_HAVE_DEFERRED_ACCEPT)
//省略
#endif
if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {
if (ls[i].previous) {
/*
* delete the old accept events that were bound to
* the old cycle read events array
*/
old = ls[i].previous->connection;
if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT)
== NGX_ERROR)
{
return NGX_ERROR;
}
old->fd = (ngx_socket_t) -1;
}
}
//注册监听套接口毒事件的回调函数 ngx_event_accept
rev->handler = ngx_event_accept;
//使用了accept_mutex,暂时不将监听套接字放入epoll中,而是
//等到worker抢到accept互斥体后,再放入epoll,避免惊群的发生
if (ngx_use_accept_mutex) {
continue;
}
if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
if (ngx_add_conn(c) == NGX_ERROR) {
return NGX_ERROR;
}
} else {
//没有使用accept互斥体,那么就将此监听套接字放入epoll中。
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
}
#endif
}
return NGX_OK;
}

这篇关于nginx event初始化流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1102980

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

Windows下Nginx的安装及开机启动

1、将nginx-1.16.1.zip解压拷贝至D:\web\nginx目录下。 2、启动Nginx,两种方法: (1)直接双击nginx.exe,双击后一个黑色的弹窗一闪而过。 (2)打开cmd命令窗口,切换到nginx目录下,输入命令 nginx.exe 或者 start nginx ,回车即可。 3、检查nginx是否启动成功。 直接在浏览器地址栏输入网址 http://lo

nginx介绍及常用功能

什么是nginx nginx跟Apache一样,是一个web服务器(网站服务器),通过HTTP协议提供各种网络服务。 Apache:重量级的,不支持高并发的服务器。在Apache上运行数以万计的并发访问,会导致服务器消耗大量内存。操作系统对其进行进程或线程间的切换也消耗了大量的CPU资源,导致HTTP请求的平均响应速度降低。这些都决定了Apache不可能成为高性能WEB服务器  nginx:

web群集--nginx配置文件location匹配符的优先级顺序详解及验证

文章目录 前言优先级顺序优先级顺序(详解)1. 精确匹配(Exact Match)2. 正则表达式匹配(Regex Match)3. 前缀匹配(Prefix Match) 匹配规则的综合应用验证优先级 前言 location的作用 在 NGINX 中,location 指令用于定义如何处理特定的请求 URI。由于网站往往需要不同的处理方式来适应各种请求,NGINX 提供了多种匹

火语言RPA流程组件介绍--浏览网页

🚩【组件功能】:浏览器打开指定网址或本地html文件 配置预览 配置说明 网址URL 支持T或# 默认FLOW输入项 输入需要打开的网址URL 超时时间 支持T或# 打开网页超时时间 执行后后等待时间(ms) 支持T或# 当前组件执行完成后继续等待的时间 UserAgent 支持T或# User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器

nginx长连接的问题

转自: http://www.360doc.com/content/12/1108/17/1073512_246644318.shtml