详解Nginx的核心配置模块中对于请求体的接受流程

本篇文章主要会介绍nginx中请求的接收流程,包括请求头的解析和请求体的读取流程。

首先介绍一下rfc2616中定义的http请求基本格式:

Request = Request-Line
   *(( general-header
      | request-header
      | entity-header ) CRLF)
     CRLF
     [ message-body ]  </span>

第一行是请求行(request line),用来说明请求方法,要访问的资源以及所使用的HTTP版本:
Request-Line   = Method SP Request-URI SP HTTP-Version CRLF</span>

请求方法(Method)的定义如下,其中最常用的是GET,POST方法:

Method = "OPTIONS"
| "GET"
| "HEAD"
| "POST"
| "PUT"
| "DELETE"
| "TRACE"
| "CONNECT"
| extension-method
extension-method = token

要访问的资源由统一资源地位符URI(Uniform Resource Identifier)确定,它的一个比较通用的组成格式(rfc2396)如下:

<scheme>://<authority><path>?<query>

一般来说根据请求方法(Method)的不同,请求URI的格式会有所不同,通常只需写出path和query部分。

http版本(version)定义如下,现在用的一般为1.0和1.1版本:

HTTP/<major>.<minor>

请求行的下一行则是请求头,rfc2616中定义了3种不同类型的请求头,分别为general-header,request-header和entity-header,每种类型rfc中都定义了一些通用的头,其中entity-header类型可以包含自定义的头。

现在开始介绍nginx中请求头的解析,nginx的请求处理流程中,会涉及到2个非常重要的数据结构,ngx_connection_t和ngx_http_request_t,分别用来表示连接和请求,这2个数据结构在本书的前篇中已经做了比较详细的介绍,没有印象的读者可以翻回去复习一下,整个请求处理流程从头到尾,对应着这2个数据结构的分配,初始化,使用,重用和销毁。

nginx在初始化阶段,具体是在init process阶段的ngx_event_process_init函数中会为每一个监听套接字分配一个连接结构(ngx_connection_t),并将该连接结构的读事件成员(read)的事件处理函数设置为ngx_event_accept,并且如果没有使用accept互斥锁的话,在这个函数中会将该读事件挂载到nginx的事件处理模型上(poll或者epoll等),反之则会等到init process阶段结束,在工作进程的事件处理循环中,某个进程抢到了accept锁才能挂载该读事件。

static ngx_int_t
ngx_event_process_init(ngx_cycle_t *cycle)
{
  ... 

  /* 初始化用来管理所有定时器的红黑树 */
  if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
    return NGX_ERROR;
  }
  /* 初始化事件模型 */
  for (m = 0; ngx_modules[m]; m++) {
    if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
      continue;
    } 

    if (ngx_modules[m]->ctx_index != ecf->use) {
      continue;
    } 

    module = ngx_modules[m]->ctx; 

    if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
      /* fatal */
      exit(2);
    } 

    break;
  } 

  ... 

  /* for each listening socket */
  /* 为每个监听套接字分配一个连接结构 */
  ls = cycle->listening.elts;
  for (i = 0; i < cycle->listening.nelts; i++) { 

    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; 

    ... 

#if (NGX_WIN32) 

    /* windows环境下不做分析,但原理类似 */ 

#else
    /* 将读事件结构的处理函数设置为ngx_event_accept */
    rev->handler = ngx_event_accept;
    /* 如果使用accept锁的话,要在后面抢到锁才能将监听句柄挂载上事件处理模型上 */
    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 {
      if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
        return NGX_ERROR;
      }
    } 

#endif 

  } 

  return NGX_OK;
}

当一个工作进程在某个时刻将监听事件挂载上事件处理模型之后,nginx就可以正式的接收并处理客户端过来的请求了。这时如果有一个用户在浏览器的地址栏内输入一个域名,并且域名解析服务器将该域名解析到一台由nginx监听的服务器上,nginx的事件处理模型接收到这个读事件之后,会速度交由之前注册好的事件处理函数ngx_event_accept来处理。

在ngx_event_accept函数中,nginx调用accept函数,从已连接队列得到一个连接以及对应的套接字,接着分配一个连接结构(ngx_connection_t),并将新得到的套接字保存在该连接结构中,这里还会做一些基本的连接初始化工作:
首先给该连接分配一个内存池,初始大小默认为256字节,可通过connection_pool_size指令设置;
分配日志结构,并保存在其中,以便后续的日志系统使用;
初始化连接相应的io收发函数,具体的io收发函数和使用的事件模型及操作系统相关;
分配一个套接口地址(sockaddr),并将accept得到的对端地址拷贝在其中,保存在sockaddr字段;
将本地套接口地址保存在local_sockaddr字段,因为这个值是从监听结构ngx_listening_t中可得,而监听结构中保存的只是配置文件中设置的监听地址,但是配置的监听地址可能是通配符*,即监听在所有的地址上,所以连接中保存的这个值最终可能还会变动,会被确定为真正的接收地址;
将连接的写事件设置为已就绪,即设置ready为1,nginx默认连接第一次为可写;
如果监听套接字设置了TCP_DEFER_ACCEPT属性,则表示该连接上已经有数据包过来,于是设置读事件为就绪;
将sockaddr字段保存的对端地址格式化为可读字符串,并保存在addr_text字段;
最后调用ngx_http_init_connection函数初始化该连接结构的其他部分。

ngx_http_init_connection函数最重要的工作是初始化读写事件的处理函数:将该连接结构的写事件的处理函数设置为ngx_http_empty_handler,这个事件处理函数不会做任何操作,实际上nginx默认连接第一次可写,不会挂载写事件,如果有数据需要发送,nginx会直接写到这个连接,只有在发生一次写不完的情况下,才会挂载写事件到事件模型上,并设置真正的写事件处理函数,这里后面的章节还会做详细介绍;读事件的处理函数设置为ngx_http_init_request,此时如果该连接上已经有数据过来(设置了deferred accept),则会直接调用ngx_http_init_request函数来处理该请求,反之则设置一个定时器并在事件处理模型上挂载一个读事件,等待数据到来或者超时。当然这里不管是已经有数据到来,或者需要等待数据到来,又或者等待超时,最终都会进入读事件的处理函数-ngx_http_init_request。

ngx_http_init_request函数主要工作即是初始化请求,由于它是一个事件处理函数,它只有唯一一个ngx_event_t *类型的参数,ngx_event_t 结构在nginx中表示一个事件,事件处理的上下文类似于一个中断处理的上下文,为了在这个上下文得到相关的信息,nginx中一般会将连接结构的引用保存在事件结构的data字段,请求结构的引用则保存在连接结构的data字段,这样在事件处理函数中可以方便的得到对应的连接结构和请求结构。进入函数内部看一下,首先判断该事件是否是超时事件,如果是的话直接关闭连接并返回;反之则是指之前accept的连接上有请求过来需要处理,ngx_http_init_request函数首先在连接的内存池中为该请求分配一个ngx_http_request_t结构,这个结构将用来保存该请求所有的信息。分配完之后,这个结构的引用会被包存在连接的hc成员的request字段,以便于在长连接或pipelined请求中复用该请求结构。在这个函数中,nginx根据该请求的接收端口和地址找到一个默认虚拟服务器配置(listen指令的default_server属性用来标识一个默认虚拟服务器,否则监听在相同端口和地址的多个虚拟服务器,其中第一个定义的则为默认),因为在nginx配置文件中可以设置多个监听在不同端口和地址的虚拟服务器(每个server块对应一个虚拟服务器),另外还根据域名(server_name指令可以配置该虚拟服务器对应的域名)来区分监听在相同端口和地址的虚拟服务器,每个虚拟服务器可以拥有不同的配置内容,而这些配置内容决定了nginx在接收到一个请求之后如何处理该请求。找到之后,相应的配置被保存在该请求对应的ngx_http_request_t结构中。注意这里根据端口和地址找到的默认配置只是临时使用一下,最终nginx会根据域名找到真正的虚拟服务器配置,随后的初始化工作还包括:

将连接的读事件的处理函数设置为ngx_http_process_request_line函数,这个函数用来解析请求行,将请求的read_event_handler设置为ngx_http_block_reading函数,这个函数实际上什么都不做(当然在事件模型设置为水平触发时,唯一做的事情就是将事件从事件模型监听列表中删除,防止该事件一直被触发),后面会说到这里为什么会将read_event_handler设置为此函数;
为这个请求分配一个缓冲区用来保存它的请求头,地址保存在header_in字段,默认大小为1024个字节,可以使用client_header_buffer_size指令修改,这里需要注意一下,nginx用来保存请求头的缓冲区是在该请求所在连接的内存池中分配,而且会将地址保存一份在连接的buffer字段中,这样做的目的也是为了给该连接的下一次请求重用这个缓冲区,另外如果客户端发过来的请求头大于1024个字节,nginx会重新分配更大的缓存区,默认用于大请求的头的缓冲区最大为8K,最多4个,这2个值可以用large_client_header_buffers指令设置,后面还会说到请求行和一个请求头都不能超过一个最大缓冲区的大小;
同样的nginx会为这个请求分配一个内存池,后续所有与该请求相关的内存分配一般都会使用该内存池,默认大小为4096个字节,可以使用request_pool_size指令修改;
为这个请求分配响应头链表,初始大小为20;
创建所有模块的上下文ctx指针数组,变量数据;
将该请求的main字段设置为它本身,表示这是一个主请求,nginx中对应的还有子请求概念,后面的章节会做详细的介绍;
将该请求的count字段设置为1,count字段表示请求的引用计数;
将当前时间保持在start_sec和start_msec字段,这个时间是该请求的起始时刻,将被用来计算一个请求的处理时间(request time),nginx使用的这个起始点和apache略有差别,nginx中请求的起始点是接收到客户端的第一个数据包开始,而apache则是接收到客户端的整个request line后开始算起;
初始化请求的其他字段,比如将uri_changes设置为11,表示最多可以将该请求的uri改写10次,subrequests被设置为201,表示一个请求最多可以发起200个子请求;
做完所有这些初始化工作之后,ngx_http_init_request函数会调用读事件的处理函数来真正的解析客户端发过来的数据,也就是会进入ngx_http_process_request_line函数中处理。

ngx_http_process_request_line函数的主要作用即是解析请求行,同样由于涉及到网络IO操作,即使是很短的一行请求行可能也不能被一次读完,所以在之前的ngx_http_init_request函数中,ngx_http_process_request_line函数被设置为读事件的处理函数,它也只拥有一个唯一的ngx_event_t *类型参数,并且在函数的开头,同样需要判断是否是超时事件,如果是的话,则关闭这个请求和连接;否则开始正常的解析流程。先调用ngx_http_read_request_header函数读取数据。

由于可能多次进入ngx_http_process_request_line函数,ngx_http_read_request_header函数首先检查请求的header_in指向的缓冲区内是否有数据,有的话直接返回;否则从连接读取数据并保存在请求的header_in指向的缓存区,而且只要缓冲区有空间的话,会一次尽可能多的读数据,读到多少返回多少;如果客户端暂时没有发任何数据过来,并返回NGX_AGAIN,返回之前会做2件事情:1,设置一个定时器,时长默认为60s,可以通过指令client_header_timeout设置,如果定时事件到达之前没有任何可读事件,nginx将会关闭此请求;2,调用ngx_handle_read_event函数处理一下读事件-如果该连接尚未在事件处理模型上挂载读事件,则将其挂载上;如果客户端提前关闭了连接或者读取数据发生了其他错误,则给客户端返回一个400错误(当然这里并不保证客户端能够接收到响应数据,因为客户端可能都已经关闭了连接),最后函数返回NGX_ERROR;

如果ngx_http_read_request_header函数正常的读取到了数据,ngx_http_process_request_line函数将调用ngx_http_parse_request_line函数来解析,这个函数根据http协议规范中对请求行的定义实现了一个有限状态机,经过这个状态机,nginx会记录请求行中的请求方法(Method),请求uri以及http协议版本在缓冲区中的起始位置,在解析过程中还会记录一些其他有用的信息,以便后面的处理过程中使用。如果解析请求行的过程中没有产生任何问题,该函数会返回NGX_OK;如果请求行不满足协议规范,该函数会立即终止解析过程,并返回相应错误号;如果缓冲区数据不够,该函数返回NGX_AGAIN。在整个解析http请求的状态机中始终遵循着两条重要的原则:减少内存拷贝和回溯。内存拷贝是一个相对比较昂贵的操作,大量的内存拷贝会带来较低的运行时效率。nginx在需要做内存拷贝的地方尽量只拷贝内存的起始和结束地址而不是内存本身,这样做的话仅仅只需要两个赋值操作而已,大大降低了开销,当然这样带来的影响是后续的操作不能修改内存本身,如果修改的话,会影响到所有引用到该内存区间的地方,所以必须很小心的管理,必要的时候需要拷贝一份。这里不得不提到nginx中最能体现这一思想的数据结构,ngx_buf_t,它用来表示nginx中的缓存,在很多情况下,只需要将一块内存的起始地址和结束地址分别保存在它的pos和last成员中,再将它的memory标志置1,即可表示一块不能修改的内存区间,在另外的需要一块能够修改的缓存的情形中,则必须分配一块所需大小的内存并保存其起始地址,再将ngx_bug_t的temprary标志置1,表示这是一块能够被修改的内存区域。

再回到ngx_http_process_request_line函数中,如果ngx_http_parse_request_line函数返回了错误,则直接给客户端返回400错误;
如果返回NGX_AGAIN,则需要判断一下是否是由于缓冲区空间不够,还是已读数据不够。如果是缓冲区大小不够了,nginx会调用ngx_http_alloc_large_header_buffer函数来分配另一块大缓冲区,如果大缓冲区还不够装下整个请求行,nginx则会返回414错误给客户端,否则分配了更大的缓冲区并拷贝之前的数据之后,继续调用ngx_http_read_request_header函数读取数据来进入请求行自动机处理,直到请求行解析结束;
如果返回了NGX_OK,则表示请求行被正确的解析出来了,这时先记录好请求行的起始地址以及长度,并将请求uri的path和参数部分保存在请求结构的uri字段,请求方法起始位置和长度保存在method_name字段,http版本起始位置和长度记录在http_protocol字段。还要从uri中解析出参数以及请求资源的拓展名,分别保存在args和exten字段。

丢弃请求体

一个模块想要主动的丢弃客户端发过的请求体,可以调用nginx核心提供的ngx_http_discard_request_body()接口,主动丢弃的原因可能有很多种,如模块的业务逻辑压根不需要请求体 ,客户端发送了过大的请求体,另外为了兼容http1.1协议的pipeline请求,模块有义务主动丢弃不需要的请求体。总之为了保持良好的客户端兼容性,nginx必须主动丢弃无用的请求体。下面开始分析ngx_http_discard_request_body()函数:

ngx_int_t
ngx_http_discard_request_body(ngx_http_request_t *r)
{
  ssize_t    size;
  ngx_event_t *rev; 

  if (r != r->main || r->discard_body) {
    return NGX_OK;
  } 

  if (ngx_http_test_expect(r) != NGX_OK) {
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
  } 

  rev = r->connection->read; 

  ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); 

  if (rev->timer_set) {
    ngx_del_timer(rev);
  } 

  if (r->headers_in.content_length_n <= 0 || r->request_body) {
    return NGX_OK;
  } 

  size = r->header_in->last - r->header_in->pos; 

  if (size) {
    if (r->headers_in.content_length_n > size) {
      r->header_in->pos += size;
      r->headers_in.content_length_n -= size; 

    } else {
      r->header_in->pos += (size_t) r->headers_in.content_length_n;
      r->headers_in.content_length_n = 0;
      return NGX_OK;
    }
  } 

  r->read_event_handler = ngx_http_discarded_request_body_handler; 

  if (ngx_handle_read_event(rev, 0) != NGX_OK) {
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
  } 

  if (ngx_http_read_discarded_request_body(r) == NGX_OK) {
    r->lingering_close = 0; 

  } else {
    r->count++;
    r->discard_body = 1;
  } 

  return NGX_OK;
}

由于函数不长,这里把它完整的列出来了,函数的开始同样先判断了不需要再做处理的情况:子请求不需要处理,已经调用过此函数的也不需要再处理。接着调用ngx_http_test_expect() 处理http1.1 expect的情况,根据http1.1的expect机制,如果客户端发送了expect头,而服务端不希望接收请求体时,必须返回417(Expectation Failed)错误。nginx并没有这样做,它只是简单的让客户端把请求体发送过来,然后丢弃掉。接下来,函数删掉了读事件上的定时器,因为这时本身就不需要请求体,所以也无所谓客户端发送的快还是慢了,当然后面还会将到,当nginx已经处理完该请求但客户端还没有发送完无用的请求体时,nginx会在读事件上再挂上定时器。
函数同样还会检查请求头中的content-length头,客户端如果打算发送请求体,就必须发送content-length头,同时还会查看其他地方是不是已经读取了请求体。如果确实有待处理的请求体,函数接着检查请求头buffer中预读的数据,预读的数据会直接被丢掉,当然如果请求体已经被全部预读,函数就直接返回了。

接下来,如果还有剩余的请求体未处理,该函数调用ngx_handle_read_event()在事件处理机制中挂载好读事件,并把读事件的处理函数设置为ngx_http_discarded_request_body_handler。做好这些准备之后,该函数最后调用ngx_http_read_discarded_request_body()接口读取客户端过来的请求体并丢弃。如果客户端并没有一次将请求体发过来,函数会返回,剩余的数据等到下一次读事件过来时,交给ngx_http_discarded_request_body_handler()来处理,这时,请求的discard_body将被设置为1用来标识这种情况。另外请求的引用数(count)也被加1,这样做的目的是客户端可能在nginx处理完请求之后仍未完整发送待发送的请求体,增加引用是防止nginx核心在处理完请求后直接释放了请求的相关资源。

ngx_http_read_discarded_request_body()函数非常简单,它循环的从链接中读取数据并丢弃,直到读完接收缓冲区的所有数据,如果请求体已经被读完了,该函数会设置读事件的处理函数为ngx_http_block_reading,这个函数仅仅删除水平触发的读事件,防止同一事件不断被触发。
再来看一下读事件的处理函数ngx_http_discarded_request_body_handler,这个函数每次读事件来时会被调用,先看一下它的源码:

void
ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
{
  ... 

  c = r->connection;
  rev = c->read; 

  if (rev->timedout) {
    c->timedout = 1;
    c->error = 1;
    ngx_http_finalize_request(r, NGX_ERROR);
    return;
  } 

  if (r->lingering_time) {
    timer = (ngx_msec_t) (r->lingering_time - ngx_time()); 

    if (timer <= 0) {
      r->discard_body = 0;
      r->lingering_close = 0;
      ngx_http_finalize_request(r, NGX_ERROR);
      return;
    } 

  } else {
    timer = 0;
  } 

  rc = ngx_http_read_discarded_request_body(r); 

  if (rc == NGX_OK) {
    r->discard_body = 0;
    r->lingering_close = 0;
    ngx_http_finalize_request(r, NGX_DONE);
    return;
  } 

  /* rc == NGX_AGAIN */ 

  if (ngx_handle_read_event(rev, 0) != NGX_OK) {
    c->error = 1;
    ngx_http_finalize_request(r, NGX_ERROR);
    return;
  } 

  if (timer) { 

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 

    timer *= 1000; 

    if (timer > clcf->lingering_timeout) {
      timer = clcf->lingering_timeout;
    } 

    ngx_add_timer(rev, timer);
  }
}

函数一开始就处理了读事件超时的情况,之前说到在ngx_http_discard_request_body()函数中已经删除了读事件的定时器,那么什么时候会设置定时器呢?答案就是在nginx已经处理完该请求,但是又没有完全将该请求的请求体丢弃的时候(客户端可能还没有发送过来),在ngx_http_finalize_connection()函数中,如果检查到还有未丢弃的请求体时,nginx会添加一个读事件定时器,它的时长为lingering_timeout指令所指定,默认为5秒,不过这个时间仅仅两次读事件之间的超时时间,等待请求体的总时长为lingering_time指令所指定,默认为30秒。这种情况中,该函数如果检测到超时事件则直接返回并断开连接。同样,还需要控制整个丢弃请求体的时长不能超过lingering_time设置的时间,如果超过了最大时长,也会直接返回并断开连接。
如果读事件发生在请求处理完之前,则不用处理超时事件,也不用设置定时器,函数只是简单的调用ngx_http_read_discarded_request_body()来读取并丢弃数据。

(0)

相关推荐

  • 详解Nginx服务器中map模块的配置与使用

    map指令使用ngx_http_map_module模块提供的.默认情况下,nginx有加载这个模块,除非人为的 --without-http_map_module. ngx_http_map_module模块可以创建变量,这些变量的值与另外的变量值相关联.允许分类或者同时映射多个值到多个不同值并储存到一个变量中,map指令用来创建变量,但是仅在变量被接受的时候执行视图映射操作,对于处理没有引用变量的请求时,这个模块并没有性能上的缺失. 一. ngx_http_map_module模块指令说明

  • 详解Nginx中的geo模块与利用其配置负载均衡的示例

    geo指令使用ngx_http_geo_module模块提供的.默认情况下,nginx有加载这个模块,除非人为的 --without-http_geo_module. ngx_http_geo_module模块可以用来创建变量,其值依赖于客户端IP地址. geo指令 语法: geo [$address] $variable { ... } 默认值: - 配置段: http 定义从指定的变量获取客户端的IP地址.默认情况下,nginx从$remote_addr变量取得客户端IP地址,但也可以从其他

  • Linux下用Nginx作Perl程序服务器及其中Perl模块的配置

    perl + fastcgi + nginx搭建 nginx + fastcgi是php下最流行的一套环境了,那perl会不会也有fastcgi呢,当然有,今天来搭建下nginx下perl的fastcgi.性能方面也不亚于php,但是现在web程序php的流行程度perl无法比拟了,性能再好也枉然,但是部分小功能可以考虑使用perl的fastcgi来搞定.进入正题. 1. 准备软件环境: nginx:http://www.nginx.org perl:系统自带 fastcgi:http://ww

  • 详解Nginx服务器中HTTP Headers相关的模块配置使用

    ngx_http_headers_module模块 一. 前言 ngx_http_headers_module模块提供了两个重要的指令add_header和expires,来添加 "Expires" 和 "Cache-Control" 头字段,对响应头添加任何域字段.add_header可以用来标示请求访问到哪台服务器上,这个也可以通过nginx模块nginx-http-footer-filter研究使用来实现.expires指令用来对浏览器本地缓存的控制. 二.

  • Nginx服务器基本的模块配置和使用全攻略

    1. 安装nginx 1.1 选择稳定版本 我们编译安装nginx来定制自己的模块,机器CentOS 6.2 x86_64.首先安装缺少的依赖包: # yum -y install gcc gcc-c++ make libtool zlib zlib-devel openssl openssl-devel pcre pcre-devel 这些软件包如果yum上没有的话可以下载源码来编译安装,只是要注意编译时默认安装的目录,确保下面在安装nginx时能够找到这些动态库文件(ldconfig). 从

  • 详解Nginx服务器中配置Sysguard模块预防高负载的方案

    nginx做为HTTP服务器,有以下几项基本特性: 处理静态文件,索引文件以及自动索引:打开文件描述符缓冲. 无缓存的反向代理加速,简单的负载均衡和容错. FastCGI,简单的负载均衡和容错. 模块化的结构.包括gzipping, byte ranges, chunked responses,以及 SSI-filter等filter.如果由FastCGI或其它代理服务器处理单页中存在的多个SSI,则这项处理可以并行运行,而不需要相互等待. Nginx专为性能优化而开发,性能是其最重要的考量,实

  • 使用Lua编写Nginx服务器的认证模块的方法

    过去两天里,我解决了一个非常有趣的问题.我用一个nginx服务器作为代理,需要能够向其中添加一个认证层,使其能够使用外部的认证源(比如某个web应用)来进行验证,如果用户在外部认证源有账号,就可以在代理里认证通过. 需求一览 我考虑了几种解决方案,罗列如下: 用一个简单的Python/Flask模块来做代理和验证. 一个使用subrequests做验证的nginx模块(nginx目前可以做到这一点) 使用Lua编写一个nginxren认证模块 很显然,给整个系统添加额外请求将执行的不是很好,因为

  • 记录Nginx服务器的Split Clients模块配置过程

    ngx-http-split-clients模块基于一些特定条件分开客户端连接,(例如ip地址,请求头,cookies等) 示例配置: http { split-clients "${remote-addr}AAA" $variant { 0.5% .one; 2.0% .two; - ""; } server { location / { index index${variant}.html; 可以使用$cookie--作为来源来分离请求,来源字符串使用CRC32

  • Nginx服务器中的模块编写及相关内核源码初探

    1.nginx模块 首先nginx和apache最大的不同就是nginx的模块不能够动态添加,需要在编译时,指定要添加的模块路径,与nginx源码一起编译. nginx模块的处理流程: a.客户端发送http请求到nginx服务器 b.nginx基于配置文件中的位置选择一个合适的处理模块 c.负载均衡模块选择一台后端服务器(反向代理情况下) d.处理模块进行处理并把输出缓冲放到第一个过滤模块上 e.第一个过滤模块处理后输出给第二个过滤模块 f.然后第二个过滤模块又到第三个过滤模块 g.第N个过滤

  • Nginx配置srcache_nginx模块搭配Redis建立缓存系统

    1. nginx模块 --add-module=../modules/ngx_devel_kit-0.2.18 --add-module=../modules/set-misc-nginx-module-0.22rc8 --add-module=../modules/srcache-nginx-module-0.22 --add-module=../modules/redis-nginx-module-0.3.6 --add-module=../modules/redis2-nginx-modu

  • 在Nginx中配置image filter模块来实现动态生成缩略图

    先来看一下什么是nginx的image filter模块. HttpImageFilterModule用来裁剪过大的图片到指定大小,是nginx自带模块,默认不会开启 开启HttpImageFilterModule需要在编译要带上参数 --with-http_image_filter_module 该模块主要有两个指令: 语法: image_filter (test | size | resize width height | crop width height) 默认是: 无 可出现的上下文:

  • Nginx服务器中用于生成缩略图的模块配置教程

    ngx_image_thumb模块生成缩略图 ngx_image_thumb是nginx中用来生成缩略图的模块,生存缩略图的方法很多,本nginx模块主要功能是对请求的图片进行缩略/水印处理,支持文字水印和图片水印.支持自定义字体,文字大小,水印透明度,水印位置,判断原图是否是否大于指定尺寸才处理等等. 1. 编译方法 编译前请确认您的系统已经安装了libcurl-dev libgd2-dev libpcre-dev 依赖库 1.1 Debian / Ubuntu 系统举例 # 如果你没有安装G

  • 详解Nginx服务器的nginx-http-footer-filter模块配置

    nginx-http-footer-filter想必大家都觉得很陌生,那我们就来认识一下它吧,这是淘宝开发的nginx模块. 它用于nginx在响应请求文件底部追加内容. 今天抽空研究下这个插件,希望对大家有所帮助.为什么发现了这个插件,因为这几天公司需要在所有shtml文件后面追加一个js代码用来做统计(之前统计代码没加齐全),在寻求解决方法的过程中找到了它认识了它最后喜欢上了它,你可能以为我用这个插件去实现了我要的功能,其实在认识他之前我用shell脚本替换齐全了. 不过我还是决定研究测试一

随机推荐