当前位置: 首页 > news >正文

企业网站建设的背景和目的韩国ps教程网站

企业网站建设的背景和目的,韩国ps教程网站,网站设计创意,swf网站cms摘要 Nginx 是俄罗斯人编写的十分轻量级的 HTTP 服务器,是一个高性能的HTTP和反向代理服务器#xff0c;同时也是一个 IMAP/POP3/SMTP 代理服务器。Nginx 是由俄罗斯人 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的#xff0c;它已经在该站点运行超过两年半了。…摘要 Nginx 是俄罗斯人编写的十分轻量级的 HTTP 服务器,是一个高性能的HTTP和反向代理服务器同时也是一个 IMAP/POP3/SMTP 代理服务器。Nginx 是由俄罗斯人 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的它已经在该站点运行超过两年半了。Igor Sysoev 在建立的项目时,使用基于 BSD 许可。到 2013 年目前有很多国内网站采用 Nginx 作为 Web 服务器如国内知名的新浪、163、腾讯、Discuz、豆瓣等。据 netcraft 统计Nginx 排名第 3约占 15% 的份。 Nginx 以事件驱动的方式编写所以有非常好的性能同时也是一个非常高效的反向代理、负载均衡服务器。其拥有匹配 Lighttpd 一个开源web服务器软件以性能著称的性能同时还没有 Lighttpd 的内存泄漏问题而且 Lighttpd 的 mod_proxy 也有一些问题并且很久没有更新。 现在Igor 将源代码以类 BSD 许可证的形式发布。Nginx 因为它的稳定性、丰富的模块库、灵活的配置和低系统资源的消耗而闻名业界一致认为它是 Apache2.2mod_proxy_balancer 的轻量级代替者不仅是因为响应静态页面的速度非常快而且它的模块数量达到 Apache 的近 2/3。对 proxy 和 rewrite 模块的支持很彻底还支持 mod_fcgi、ssl、vhosts 适合用来做 mongrel clusters 的前端 HTTP 响应。 一、Nginx 的特点 Nginx 做为 HTTP 服务器有以下几项基本特性 处理静态文件索引文件以及自动索引打开文件描述符缓冲 无缓存的反向代理加速简单的负载均衡和容错 FastCGI简单的负载均衡和容错 模块化的结构。包括 gzipping, byte ranges, chunked responses,以及 SSI-filter 等 filter。如果由 FastCGI 或其它代理服务器处理单页中存在的多个 SSI则这项处理可以并行运行而不需要相互等待。 支持 SSL 和 TLSSNI Nginx 专为性能优化而开发性能是其最重要的考量,实现上非常注重效率 。它支持内核 Poll 模型能经受高负载的考验,有报告表明能支持高达 50,000 个并发连接数。 Nginx 具有很高的稳定性。其它 HTTP 服务器当遇到访问的峰值或者有人恶意发起慢速连接时很可能会导致服务器物理内存耗尽频繁交换失去响应只能重启服务器。例如当前 apache 一旦上到 200 个进程以上web响应速度就明显非常缓慢了。而 Nginx 采取了分阶段资源分配技术使得它的 CPU 与内存占用率非常低。Nginx 官方表示在保持 10,000 个无活动连接时它只占 2.5M 内存所以类似 DOS 这样的攻击对 Nginx 来说基本上是毫无用处的。就稳定性而言,Nginx 比 lighthttpd 更胜一筹。 Nginx 支持热部署。它的启动特别容易, 并且几乎可以做到 7*24 不间断运行即使运行数个月也不需要重新启动。你还能够在不间断服务的情况下对软件版本进行升级。 Nginx 采用 master-slave 模型主从模型一种优化阻塞的模型,能够充分利用 SMP 对称多处理一种并行处理技术的优势且能够减少工作进程在磁盘 I/O 的阻塞延迟。当采用 select()/poll() 调用时还可以限制每个进程的连接数。 Nginx 代码质量非常高代码很规范手法成熟模块扩展也很容易。特别值得一提的是强大的 Upstream 与 Filter 链。Upstream 为诸如 reverse proxy,与其他服务器通信模块的编写奠定了很好的基础。而 Filter 链最酷的部分就是各个 filter 不必等待前一个 filter 执行完毕。它可以把前一个 filter 的输出做为当前 filter 的输入这有点像 Unix 的管线。这意味着一个模块可以开始压缩从后端服务器发送过来的请求且可以在模块接收完后端服务器的整个请求之前把压缩流转向客户端。 Nginx 采用了一些 os 提供的最新特性。如对 sendfile (Linux2.2)accept-filter (FreeBSD4.1)TCP_DEFER_ACCEPT (Linux 2.4)的支持从而大大提高了性能。 二、Nginx 架构原理 Nginx 性能高而 Nginx 的高性能与其架构是分不开的。Nginx 在启动后在 unix 系统中会以 daemon 守护进程的方式在后台运行后台进程包含一个 master 进程和多个 worker 进程。我们也可以手动地关掉后台模式让 Nginx 在前台运行并且通过配置让 Nginx 取消 master 进程从而可以使 Nginx 以单进程方式运行。我们可以看到Nginx 是以多进程的方式来工作的当然 Nginx 也是支持多线程的方式的只是我们主流的方式还是多进程的方式也是 Nginx 的默认方式。Nginx 采用多进程的方式有诸多好处。 Nginx在启动后会有一个master 进程和多个 worker 进程。master 进程主要用来管理worker进程包含接收来自外界的信号向各 worker 进程发送信号监控 worker 进程的运行状态当 worker 进程退出后(异常情况下)会自动重新启动新的 worker 进程。而基本的网络事件则是放在 worker 进程中来处理了。多个 worker 进程之间是对等的他们同等竞争来自客户端的请求各进程互相之间是独立的。一个请求只可能在一个 worker 进程中处理一个 worker 进程不可能处理其它进程的请求。worker 进程的个数是可以设置的一般我们会设置与机器cpu核数一致这里面的原因与 Nginx 的进程模型以及事件处理模型是分不开的。 master 来管理 worker 进程所以我们只需要与master进程通信就行了。master进程会接收来自外界发来的信号再根据信号做不同的事情。所以我们要控制Nginx只需要通过kill 向master进程发送信号就行了。比如kill -HUP pid则是告诉Nginx从容地重启 Nginx我们一般用这个信号来重启Nginx或重新加载配置因为是从容地重启因此服务是不中断的。 master 进程在接收到 HUP 信号后是怎么做的呢首先 master 进程在接到信号后会先重新加载配置文件然后再启动新的 worker 进程并向所有老的 worker 进程发送信号告诉他们可以光荣退休了。新的 worker 在启动后就开始接收新的请求而老的 worker 在收到来自 master 的信号后就不再接收新的请求并且在当前进程中的所有未处理完的请求处理完成后再退出。 当然直接给 master 进程发送信号这是比较老的操作方式Nginx 在 0.8 版本之后引入了一系列命令行参数来方便我们管理。比如./nginx -s reload就是来重启 Nginx./nginx -s stop就是来停止 Nginx 的运行。 我们看到执行命令时我们是启动一个新的 Nginx 进程而新的 Nginx 进程在解析到 reload 参数后就知道我们的目的是控制 Nginx 来重新加载配置文件了它会向 master 进程发送信号然后接下来的动作就和我们直接向 master 进程发送信号一样了。 worker 进程之间是平等的每个进程处理请求的机会也是一样的。当我们提供 80 端口的 http 服务时一个连接请求过来每个进程都有可能处理这个连接怎么做到的呢 首先每个 worker 进程都是从 master 进程 fork 过来在master进程里面先建立好需要 listen 的 socketlistenfd之后然后再 fork 出多个 worker 进程。所有 worker 进程的 listenfd 会在新连接到来时变得可读为保证只有一个进程处理该连接所有 worker 进程在注册 listenfd 读事件前抢 accept_mutex抢到互斥锁的那个进程注册 listenfd 读事件在读事件里调用 accept 接受该连接。当一个 worker 进程在 accept 这个连接之后就开始读取请求解析请求处理请求产生数据后再返回给客户端最后才断开连接这样一个完整的请求就是这样的了。我们可以看到一个请求完全由 worker 进程来处理而且只在一个 worker 进程中处理。 那么Nginx 采用这种进程模型有什么好处呢当然好处肯定会很多了。首先对于每个 worker 进程来说独立的进程不需要加锁所以省掉了锁带来的开销同时在编程以及问题查找时也会方便很多。其次采用独立的进程可以让互相之间不会影响一个进程退出后其它进程还在工作服务不会中断master 进程则很快启动新的 worker 进程。当然worker 进程的异常退出肯定是程序有 bug 了异常退出会导致当前 worker 上的所有请求失败不过不会影响到所有请求所以降低了风险。 Nginx 采用多 worker 的方式来处理请求每个 worker 里面只有一个主线程那能够处理的并发数很有限啊多少个 worker 就能处理多少个并发何来高并发呢 Nginx 采用了异步非阻塞的方式来处理请求也就是说Nginx 是可以同时处理成千上万个请求的。想想 apache 的常用工作方式apache 也有异步非阻塞版本但因其与自带某些模块冲突所以不常用每个请求会独占一个工作线程当并发数上到几千时就同时有几千的线程在处理请求了。这对操作系统来说是个不小的挑战线程带来的内存占用非常大线程的上下文切换带来的 cpu 开销很大自然性能就上不去了而这些开销完全是没有意义的。 为什么 Nginx 可以采用异步非阻塞的方式来处理呢或者异步非阻塞到底是怎么回事呢 看看一个请求的完整过程。首先请求过来要建立连接然后再接收数据接收数据后再发送数据。具体到系统底层就是读写事件而当读写事件没有准备好时必然不可操作如果不用非阻塞的方式来调用那就得阻塞调用了事件没有准备好那就只能等了等事件准备好了你再继续吧。阻塞调用会进入内核等待cpu 就会让出去给别人用了对单线程的 worker 来说显然不合适当网络事件越多时大家都在等待呢cpu 空闲下来没人用cpu利用率自然上不去了更别谈高并发了。好吧你说加进程数这跟apache的线程模型有什么区别注意别增加无谓的上下文切换。所以在 Nginx 里面最忌讳阻塞的系统调用了。不要阻塞那就非阻塞喽。非阻塞就是事件没有准备好马上返回EAGAIN告诉你事件还没准备好呢你慌什么过会再来吧。好吧你过一会再来检查一下事件直到事件准备好了为止在这期间你就可以先去做其它事情然后再来看看事件好了没。虽然不阻塞了但你得不时地过来检查一下事件的状态你可以做更多的事情了但带来的开销也是不小的。所以才会有了异步非阻塞的事件处理机制具体到系统调用就是像 select/poll/epoll/kqueue 这样的系统调用。它们提供了一种机制让你可以同时监控多个事件调用他们是阻塞的但可以设置超时时间在超时时间之内如果有事件准备好了就返回。这种机制正好解决了我们上面的两个问题拿 epoll 为例(在后面的例子中我们多以 epoll 为例子以代表这一类函数)当事件没准备好时放到 epoll 里面事件准备好了我们就去读写当读写返回 EAGAIN 时我们将它再次加入到 epoll 里面。这样只要有事件准备好了我们就去处理它只有当所有事件都没准备好时才在 epoll 里面等着。这样我们就可以并发处理大量的并发了当然这里的并发请求是指未处理完的请求线程只有一个所以同时能处理的请求当然只有一个了只是在请求间进行不断地切换而已切换也是因为异步事件未准备好而主动让出的。这里的切换是没有任何代价你可以理解为循环处理多个准备好的事件事实上就是这样的。与多线程相比这种事件处理方式是有很大的优势的不需要创建线程每个请求占用的内存也很少没有上下文切换事件处理非常的轻量级。并发数再多也不会导致无谓的资源浪费上下文切换。更多的并发数只是会占用更多的内存而已。 我之前有对连接数进行过测试在 24G 内存的机器上处理的并发请求数达到过 200 万。现在的网络服务器基本都采用这种方式这也是nginx性能高效的主要原因。 我们之前说过推荐设置 worker 的个数为 cpu 的核数在这里就很容易理解了更多的 worker 数只会导致进程来竞争 cpu 资源了从而带来不必要的上下文切换。而且nginx为了更好的利用多核特性提供了 cpu 亲缘性的绑定选项我们可以将某一个进程绑定在某一个核上这样就不会因为进程的切换带来 cache 的失效。像这种小的优化在 Nginx 中非常常见同时也说明了 Nginx 作者的苦心孤诣。比如Nginx 在做 4 个字节的字符串比较时会将 4 个字符转换成一个 int 型再作比较以减少 cpu 的指令数等等。 知道了 Nginx 为什么会选择这样的进程模型与事件模型了。对于一个基本的 Web 服务器来说事件通常有三种类型网络事件、信号、定时器。从上面的讲解中知道网络事件通过异步非阻塞可以很好的解决掉。如何处理信号与定时器 首先信号的处理。对Nginx来说有一些特定的信号代表着特定的意义。信号会中断掉程序当前的运行在改变状态后继续执行。如果是系统调用则可能会导致系统调用的失败需要重入。关于信号的处理大家可以学习一些专业书籍这里不多说。对于 Nginx 来说如果nginx正在等待事件epoll_wait 时如果程序收到信号在信号处理函数处理完后epoll_wait 会返回错误然后程序可再次进入 epoll_wait 调用。 另外再来看看定时器。由于 epoll_wait 等函数在调用的时候是可以设置一个超时时间的所以 Nginx 借助这个超时时间来实现定时器。nginx里面的定时器事件是放在一颗维护定时器的红黑树里面每次在进入 epoll_wait前先从该红黑树里面拿到所有定时器事件的最小时间在计算出 epoll_wait 的超时时间后进入 epoll_wait。所以当没有事件产生也没有中断信号时epoll_wait 会超时也就是说定时器事件到了。这时nginx会检查所有的超时事件将他们的状态设置为超时然后再去处理网络事件。由此可以看出当我们写 Nginx 代码时在处理网络事件的回调函数时通常做的第一个事情就是判断超时然后再去处理网络事件。 我们可以用一段伪代码来总结一下 Nginx 的事件处理模型 while (true) {for t in run_tasks:t.handler();update_time(now);timeout ETERNITY;for t in wait_tasks: /* sorted already */if (t.time now) {t.timeout_handler();} else {timeout t.time - now;break;}nevents poll_function(events, timeout);for i in nevents:task t;if (events[i].type READ) {t.handler read_handler;} else { /* events[i].type WRITE */t.handler write_handler;}run_tasks_add(t);} 二、Nginx 基础概念 2.1 connection 在 Nginx 中 connection 就是对 tcp 连接的封装其中包括连接的 socket读事件写事件。利用 Nginx 封装的 connection我们可以很方便的使用 Nginx 来处理与连接相关的事情比如建立连接发送与接受数据等。而 Nginx 中的 http 请求的处理就是建立在 connection之上的所以 Nginx 不仅可以作为一个web服务器也可以作为邮件服务器。当然利用 Nginx 提供的 connection我们可以与任何后端服务打交道。 结合一个 tcp 连接的生命周期我们看看 Nginx 是如何处理一个连接的。首先Nginx 在启动时会解析配置文件得到需要监听的端口与 ip 地址然后在 Nginx 的 master 进程里面先初始化好这个监控的 socket(创建 socket设置 addrreuse 等选项绑定到指定的 ip 地址端口再 listen)然后再 fork 出多个子进程出来然后子进程会竞争 accept 新的连接。此时客户端就可以向 Nginx 发起连接了。当客户端与服务端通过三次握手建立好一个连接后Nginx 的某一个子进程会 accept 成功得到这个建立好的连接的 socket然后创建 Nginx 对连接的封装即 ngx_connection_t 结构体。接着设置读写事件处理函数并添加读写事件来与客户端进行数据的交换。最后Nginx 或客户端来主动关掉连接到此一个连接就寿终正寝了。 当然Nginx 也是可以作为客户端来请求其它 server 的数据的如 upstream 模块此时与其它 server 创建的连接也封装在 ngx_connection_t 中。作为客户端Nginx 先获取一个 ngx_connection_t 结构体然后创建 socket并设置 socket 的属性 比如非阻塞。然后再通过添加读写事件调用 connect/read/write 来调用连接最后关掉连接并释放 ngx_connection_t。 在 Nginx 中每个进程会有一个连接数的最大上限这个上限与系统对 fd 的限制不一样。在操作系统中通过 ulimit -n我们可以得到一个进程所能够打开的 fd 的最大数即 nofile因为每个 socket 连接会占用掉一个 fd所以这也会限制我们进程的最大连接数当然也会直接影响到我们程序所能支持的最大并发数当 fd 用完后再创建 socket 时就会失败。Nginx 通过设置 worker_connectons 来设置每个进程支持的最大连接数。如果该值大于 nofile那么实际的最大连接数是 nofileNginx 会有警告。Nginx 在实现时是通过一个连接池来管理的每个 worker 进程都有一个独立的连接池连接池的大小是 worker_connections。这里的连接池里面保存的其实不是真实的连接它只是一个 worker_connections 大小的一个 ngx_connection_t 结构的数组。并且Nginx 会通过一个链表 free_connections 来保存所有的空闲 ngx_connection_t每次获取一个连接时就从空闲连接链表中获取一个用完后再放回空闲连接链表里面。 在这里很多人会误解 worker_connections 这个参数的意思认为这个值就是 Nginx 所能建立连接的最大值。其实不然这个值是表示每个 worker 进程所能建立连接的最大值所以一个 Nginx 能建立的最大连接数应该是worker_connections * worker_processes。当然这里说的是最大连接数对于 HTTP 请求本地资源来说能够支持的最大并发数量是worker_connections * worker_processes而如果是 HTTP 作为反向代理来说最大并发数量应该是worker_connections * worker_processes/2。因为作为反向代理服务器每个并发会建立与客户端的连接和与后端服务的连接会占用两个连接。 那么我们前面有说过一个客户端连接过来后多个空闲的进程会竞争这个连接很容易看到这种竞争会导致不公平如果某个进程得到 accept 的机会比较多它的空闲连接很快就用完了如果不提前做一些控制当 accept 到一个新的 tcp 连接后因为无法得到空闲连接而且无法将此连接转交给其它进程最终会导致此 tcp 连接得不到处理就中止掉了。很显然这是不公平的有的进程有空余连接却没有处理机会有的进程因为没有空余连接却人为地丢弃连接。那么如何解决这个问题呢首先Nginx 的处理得先打开 accept_mutex 选项此时只有获得了 accept_mutex 的进程才会去添加accept事件也就是说Nginx会控制进程是否添加 accept 事件。Nginx 使用一个叫 ngx_accept_disabled 的变量来控制是否去竞争 accept_mutex 锁。在第一段代码中计算 ngx_accept_disabled 的值这个值是 Nginx 单进程的所有连接总数的八分之一减去剩下的空闲连接数量得到的这个 ngx_accept_disabled 有一个规律当剩余连接数小于总连接数的八分之一时其值才大于 0而且剩余的连接数越小这个值越大。再看第二段代码当 ngx_accept_disabled 大于 0 时不会去尝试获取 accept_mutex 锁并且将 ngx_accept_disabled 减 1于是每次执行到此处时都会去减 1直到小于 0。不去获取 accept_mutex 锁就是等于让出获取连接的机会很显然可以看出当空余连接越少时ngx_accept_disable 越大于是让出的机会就越多这样其它进程获取锁的机会也就越大。不去 accept自己的连接就控制下来了其它进程的连接池就会得到利用这样Nginx 就控制了多进程间连接的平衡了。 ngx_accept_disabled ngx_cycle-connection_n / 8- ngx_cycle-free_connection_n;if (ngx_accept_disabled 0) {ngx_accept_disabled--;} else {if (ngx_trylock_accept_mutex(cycle) NGX_ERROR) {return;}if (ngx_accept_mutex_held) {flags | NGX_POST_EVENTS;} else {if (timer NGX_TIMER_INFINITE|| timer ngx_accept_mutex_delay){timer ngx_accept_mutex_delay;}}} 2.2 request 在 Nginx 中我们指的是 http 请求具体到 Nginx 中的数据结构是ngx_http_request_t。ngx_http_request_t 是对一个 http 请求的封装。 我们知道一个 http 请求包含请求行、请求头、请求体、响应行、响应头、响应体。 http 请求是典型的请求-响应类型的的网络协议而 http 是文本协议所以我们在分析请求行与请求头以及输出响应行与响应头往往是一行一行的进行处理。如果我们自己来写一个 http 服务器通常在一个连接建立好后客户端会发送请求过来。然后我们读取一行数据分析出请求行中包含的 method、uri、http_version 信息。然后再一行一行处理请求头并根据请求 method 与请求头的信息来决定是否有请求体以及请求体的长度然后再去读取请求体。得到请求后我们处理请求产生需要输出的数据然后再生成响应行响应头以及响应体。在将响应发送给客户端之后一个完整的请求就处理完了。当然这是最简单的 webserver 的处理方式其实 Nginx 也是这样做的只是有一些小小的区别比如当请求头读取完成后就开始进行请求的处理了。Nginx 通过 ngx_http_request_t 来保存解析请求与输出响应相关的数据。 那接下来简要讲讲 Nginx 是如何处理一个完整的请求的。对于 Nginx 来说一个请求是从ngx_http_init_request 开始的在这个函数中会设置读事件为 ngx_http_process_request_line也就是说接下来的网络事件会由 ngx_http_process_request_line 来执行。从ngx_http_process_request_line 的函数名我们可以看到这就是来处理请求行的正好与之前讲的处理请求的第一件事就是处理请求行是一致的。通过 ngx_http_read_request_header 来读取请求数据。然后调用 ngx_http_parse_request_line 函数来解析请求行。Nginx 为提高效率采用状态机来解析请求行而且在进行 method 的比较时没有直接使用字符串比较而是将四个字符转换成一个整型然后一次比较以减少 cpu 的指令数这个前面有说过。很多人可能很清楚一个请求行包含请求的方法uri版本却不知道其实在请求行中也是可以包含有 host 的。比如一个请求 GET 淘宝网 - 淘我喜欢 HTTP/1.0 这样一个请求行也是合法的而且 host 是 www.taobao.com这个时候Nginx 会忽略请求头中的 host 域而以请求行中的这个为准来查找虚拟主机。另外对于对于 http0.9 版来说是不支持请求头的所以这里也是要特别的处理。所以在后面解析请求头时协议版本都是 1.0 或 1.1。整个请求行解析到的参数会保存到 ngx_http_request_t 结构当中。 在解析完请求行后Nginx 会设置读事件的 handler 为 ngx_http_process_request_headers然后后续的请求就在 ngx_http_process_request_headers 中进行读取与解析。ngx_http_process_request_headers 函数用来读取请求头跟请求行一样还是调用 ngx_http_read_request_header 来读取请求头调用 ngx_http_parse_header_line 来解析一行请求头解析到的请求头会保存到 ngx_http_request_t 的域 headers_in 中headers_in 是一个链表结构保存所有的请求头。而 HTTP 中有些请求是需要特别处理的这些请求头与请求处理函数存放在一个映射表里面即 ngx_http_headers_in在初始化时会生成一个 hash 表当每解析到一个请求头后就会先在这个 hash 表中查找如果有找到则调用相应的处理函数来处理这个请求头。比如:Host 头的处理函数是 ngx_http_process_host。 当 Nginx 解析到两个回车换行符时就表示请求头的结束此时就会调用 ngx_http_process_request 来处理请求了。ngx_http_process_request 会设置当前的连接的读写事件处理函数为 ngx_http_request_handler然后再调用 ngx_http_handler 来真正开始处理一个完整的http请求。这里可能比较奇怪读写事件处理函数都是ngx_http_request_handler其实在这个函数中会根据当前事件是读事件还是写事件分别调用 ngx_http_request_t 中的 read_event_handler 或者是 write_event_handler。由于此时我们的请求头已经读取完成了之前有说过Nginx 的做法是先不读取请求 body所以这里面我们设置 read_event_handler 为 ngx_http_block_reading即不读取数据了。刚才说到真正开始处理数据是在 ngx_http_handler 这个函数里面这个函数会设置 write_event_handler 为 ngx_http_core_run_phases并执行 ngx_http_core_run_phases 函数。ngx_http_core_run_phases 这个函数将执行多阶段请求处理Nginx 将一个 http 请求的处理分为多个阶段那么这个函数就是执行这些阶段来产生数据。因为 ngx_http_core_run_phases 最后会产生数据所以我们就很容易理解为什么设置写事件的处理函数为 ngx_http_core_run_phases 了。在这里我简要说明了一下函数的调用逻辑我们需要明白最终是调用 ngx_http_core_run_phases 来处理请求产生的响应头会放在 ngx_http_request_t 的 headers_out 中这一部分内容我会放在请求处理流程里面去讲。Nginx 的各种阶段会对请求进行处理最后会调用 filter 来过滤数据对数据进行加工如 truncked 传输、gzip 压缩等。这里的 filter 包括 header filter 与 body filter即对响应头或响应体进行处理。filter 是一个链表结构分别有 header filter 与 body filter先执行 header filter 中的所有 filter然后再执行 body filter 中的所有 filter。在 header filter 中的最后一个 filter即 ngx_http_header_filter这个 filter 将会遍历所有的响应头最后需要输出的响应头在一个连续的内存然后调用 ngx_http_write_filter 进行输出。ngx_http_write_filter 是 body filter 中的最后一个所以 Nginx 首先的 body 信息在经过一系列的 body filter 之后最后也会调用 ngx_http_write_filter 来进行输出(有图来说明)。 这里要注意的是Nginx 会将整个请求头都放在一个 buffer 里面这个 buffer 的大小通过配置项 client_header_buffer_size 来设置如果用户的请求头太大这个 buffer 装不下那 Nginx 就会重新分配一个新的更大的 buffer 来装请求头这个大 buffer 可以通过 large_client_header_buffers 来设置这个 large_buffer 这一组 buffer比如配置 48k就是表示有四个 8k 大小的 buffer 可以用。注意为了保存请求行或请求头的完整性一个完整的请求行或请求头需要放在一个连续的内存里面所以一个完整的请求行或请求头只会保存在一个 buffer 里面。这样如果请求行大于一个 buffer 的大小就会返回 414 错误如果一个请求头大小大于一个 buffer 大小就会返回 400 错误。在了解了这些参数的值以及 Nginx 实际的做法之后在应用场景我们就需要根据实际的需求来调整这些参数来优化我们的程序了。 2.3 keepalive 当然在 Nginx 中对于 http1.0 与 http1.1 也是支持长连接的。什么是长连接呢我们知道http 请求是基于 TCP 协议之上的那么当客户端在发起请求前需要先与服务端建立 TCP 连接而每一次的 TCP 连接是需要三次握手来确定的如果客户端与服务端之间网络差一点这三次交互消费的时间会比较多而且三次交互也会带来网络流量。当然当连接断开后也会有四次的交互当然对用户体验来说就不重要了。而 http 请求是请求应答式的如果我们能知道每个请求头与响应体的长度那么我们是可以在一个连接上面执行多个请求的这就是所谓的长连接但前提条件是我们先得确定请求头与响应体的长度。对于请求来说如果当前请求需要有body如 POST 请求那么 Nginx 就需要客户端在请求头中指定 content-length 来表明 body 的大小否则返回 400 错误。也就是说请求体的长度是确定的那么响应体的长度呢先来看看 http 协议中关于响应 body 长度的确定 对于 http1.0 协议来说如果响应头中有 content-length 头则以 content-length 的长度就可以知道 body 的长度了客户端在接收 body 时就可以依照这个长度来接收数据接收完后就表示这个请求完成了。而如果没有 content-length 头则客户端会一直接收数据直到服务端主动断开连接才表示 body 接收完了。 而对于 http1.1 协议来说如果响应头中的 Transfer-encoding 为 chunked 传输则表示 body 是流式输出body 会被分成多个块每块的开始会标识出当前块的长度此时body 不需要通过长度来指定。如果是非 chunked 传输而且有 content-length则按照 content-length 来接收数据。否则如果是非 chunked并且没有 content-length则客户端接收数据直到服务端主动断开连接。 从上面我们可以看到除了 http1.0 不带 content-length 以及 http1.1 非 chunked 不带 content-length 外body 的长度是可知的。此时当服务端在输出完 body 之后会可以考虑使用长连接。能否使用长连接也是有条件限制的。如果客户端的请求头中的 connection为close则表示客户端需要关掉长连接如果为 keep-alive则客户端需要打开长连接如果客户端的请求中没有 connection 这个头那么根据协议如果是 http1.0则默认为 close如果是 http1.1则默认为 keep-alive。如果结果为 keepalive那么Nginx 在输出完响应体后会设置当前连接的 keepalive 属性然后等待客户端下一次请求。当然Nginx 不可能一直等待下去如果客户端一直不发数据过来岂不是一直占用这个连接所以当 Nginx 设置了 keepalive 等待下一次的请求时同时也会设置一个最大等待时间这个时间是通过选项 keepalive_timeout 来配置的如果配置为 0则表示关掉 keepalive此时http 版本无论是 1.1 还是 1.0客户端的 connection 不管是 close 还是 keepalive都会强制为 close。 如果服务端最后的决定是 keepalive 打开那么在响应的 http 头里面也会包含有 connection 头域其值是Keep-Alive否则就是Close。如果 connection 值为 close那么在 Nginx 响应完数据后会主动关掉连接。所以对于请求量比较大的 Nginx 来说关掉 keepalive 最后会产生比较多的 time-wait 状态的 socket。一般来说当客户端的一次访问需要多次访问同一个 server 时打开 keepalive 的优势非常大比如图片服务器通常一个网页会包含很多个图片。打开 keepalive 也会大量减少 time-wait 的数量。 博文参考 初探 Nginx 架构_w3cschool
http://www.dnsts.com.cn/news/106800.html

相关文章:

  • 优秀定制网站建设方案凤楼网站怎么做的
  • 网站开发的工作方法电商网站设计图片素材
  • 加强志鉴网站建设设计开发输入清单
  • wordpress漫画站网站建设合同属于购销吗
  • 百度网站ip地址网站标签怎么改
  • 永春县建设局网站系统优化大师下载
  • 那曲地区建设局网站小公司做网站还是微博
  • 一个网站网站建设下来要花多少钱电影网页设计素材
  • 怎么样利用一些网站开发客户网站100m空间
  • 建筑企业招聘网站中山市企业网站建立
  • 文化公司网站建设策划书wordpress显示文章列表
  • 做网站需要服务器中国做的比较好的网站
  • 内涵图网站源码xydown wordpress
  • appcan 手机网站开发wordpress收费插件
  • 音乐网站设计外国做视频图片博客网站有哪些
  • 网站系统制作易语言怎么做网站
  • php建站自适应网站建设模板
  • 电子商务网站开发的基本要求个人网站备案地址选择
  • 网站建设电话销售术语房地产公司网站建设ppt
  • 自己做的网站怎么设置关键词上海公司注册代理公司注册
  • 做著名建筑物网站简介2022网页游戏大全
  • 广东做网站哪家公司好公司官网首页设计
  • 数学网站怎么做的六安市 网站集约化建设
  • 南平购物网站开发设计免费下载设计素材网站
  • 网站推广排名怎么做wordpress漂浮框
  • 呼和浩特建设厅官方网站查询企业的软件
  • 网上书城网站建设总结怎么样做企业网站
  • 网站建设技术服务清单网站换域名做301
  • 地产公司做网站维护写代码么6A华企网络网站建设
  • 建设部质量监督官方网站wap企业网站