Java面试——Nginx

 优质博文:IT-BLOG-CN

一、为什么 Nginx可以采用异步非阻塞的方式来处理


看看一个请求的完整过程:请求过来,要建立连接,然后再接收数据,接收数据后,再发送数据。
具体到系统底层,就是读写事件,而当读写事件没有准备好时,必然不可操作,如果不用非阻塞的方式来调用,那就得阻塞调用了,事件没有准备好,那就只能等了,等事件准备好了,你再继续吧。阻塞调用会进入内核等待,cpu就会让出去给别人用了,对单线程的worker来说,显然不合适,当网络事件越多时,大家都在等待呢,cpu空闲下来没人用,cpu利用率自然上不去了,更别谈高并发了。好吧,你说加进程数,这跟 apache的线程模型有什么区别,注意,别增加无谓的上下文切换。所以,在nginx里面,最忌讳阻塞的系统调用了。不要阻塞,那就非阻塞喽。非阻塞就是,事件没有准备好,马上返回EAGAIN,告诉你,事件还没准备好呢,你慌什么,过会再来吧。好吧,你过一会,再来检查一下事件,直到事件准备好了为止,在这期间,你就可以先去做其它事情,然后再来看看事件好了没。虽然不阻塞了,但你得不时地过来检查一下事件的状态,你可以做更多的事情了,但带来的开销也是不小的。

二、Nginx 的优点


【1】速度更快:这表现在两个方面:一方面,在正常情况下,单次请求会得到更快的响应;另一方面,在高峰期(如有数以万计的并发请求),Nginx 可以比其他 Web服务器更快地响应请求。
【2】高扩展性,跨平台:Nginx 的设计极具扩展性,它完全是由多个不同功能、不同层次、不同类型且耦合度极低的模块组成。因此,当对某一个模块修复 Bug或进行升级时,可以专注于模块自身,无须在意其他。而且在 HTTP模块中,还设计了 HTTP过滤器模块:一个正常的 HTTP模块在处理完请求后,会有一串 HTTP过滤器模块对请求的结果进行再处理。这样,当我们开发一个新的 HTTP模块时,不但可以使用诸如 HTTP核心模块、events模块、log模块等不同层次或者不同类型的模块,还可以原封不动地复用大量已有的 HTTP过滤器模块。这种低耦合度的优秀设计,造就了 Nginx庞大的第三方模块,当然,公开的第三方模块也如官方发布的模块一样容易使用。
Nginx 的模块都是嵌入到二进制文件中执行的,无论官方发布的模块还是第三方模块都是如此。这使得第三方模块一样具备极其优秀的性能,充分利用 Nginx的高并发特性,因此,许多高流量的网站都倾向于开发符合自己业务特性的定制模块。
【3】高可靠性:用于反向代理,宕机的概率微乎其微。高可靠性是我们选择 Nginx的最基本条件,因为 Nginx的可靠性是大家有目共睹的,很多家高流量网站都在核心服务器上大规模使用 Nginx。Nginx 的高可靠性来自于其核心框架代码的优秀设计、模块设计的简单性;另外,官方提供的常用模块都非常稳定,每个 worker进程相对独立,master 进程在1个 worker进程出错时可以快速 “拉起” 新的 worker子进程提供服务。
【4】低内存消耗:一般情况下,10,000 个非活跃的 HTTP Keep-Alive 连接在 Nginx中仅消耗 2.5MB的内存,这是 Nginx支持高并发连接的基础。开启 10 个 Nginx 才占 150M 内存。
【5】单机支持10万以上的并发连接:这是一个非常重要的特性!随着互联网的迅猛发展和互联网用户数量的成倍增长,各大公司、网站都需要应付海量并发请求,一个能够在峰值期顶住 10万以上并发请求的 Server,无疑会得到大家的青睐。理论上,Nginx 支持的并发连接上限取决于内存,10万远未封顶。当然,能够及时地处理更多的并发请求,是与业务特点紧密相关的。
【6】热部署:Master 管理进程与 Worker工作进程的分离设计,使得 Nginx能够提供热部署功能,即可以在 7×24小时不间断服务的前提下,升级 Nginx的可执行文件。当然,它也支持不停止服务就更新配置项、更换日志文件等功能。
【7】最自由的 BSD许可协议:这是 Nginx可以快速发展的强大动力。BSD许可协议不只是允许用户免费使用 Nginx,它还允许用户在自己的项目中直接使用或修改 Nginx源码,然后发布。这吸引了无数开发者继续为 Nginx贡献自己的智慧。
以上七个特点当然不是 Nginx的全部,拥有无数个官方功能模块、第三方功能模块使得 Nginx能够满足绝大部分应用场景,这些功能模块间可以叠加以实现更加强大、复杂的功能,有些模块还支持 Nginx与 Perl、Lua等脚本语言集成工作,大大提高了开发效率。这些特点促使用户在寻找一个 Web服务器时更多考虑 Nginx。选择 Nginx的核心理由还是它能在支持高并发请求的同时保持高效的服务。
【8】高性能:处理 2-3 万并发连接数,官方监测能支持 5 万并发。

三、说说你在 Nginx中使用过的配置参数


当 Nginx安装完毕后,会有相应的安装目录,安装目录里的 nginx.confg为 Nginx的主配置文件,Nginx 主配置文件分为4部分,main(全局配置)、server(主机配置)、upstream(负载均衡服务器设置)以及 location(URL匹配特定位置的设置),这四者的关系是:server 继承main,location继承server,upstream 既不会继承其它设置也不会被继承。

########### 每个指令必须有分号结束。#################
#配置用户或者组,默认为nobody nobody。给组设置不同的权限,用户就会拥有对应的权限。
user administrator administrators;  
worker_processes 2;  #允许生成的进程数,默认为1
pid /nginx/pid/nginx.pid;   #指定nginx进程运行文件存放地址
#制定日志路径和级别。这个设置可以放入全局块,http块,server块,级别以此为:
             #  debug|info|notice|warn|error|crit|alert|emerg
error_log log/error.log debug; 
events {
    accept_mutex on;   #设置网路连接序列化,防止惊群现象发生,默认为on
    multi_accept on;  #设置一个进程是否同时接受多个网络连接,默认为off
    use epoll;      #事件驱动模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport
    worker_connections  1024;    #最大连接数,默认为512
}
http {
    include       mime.types;   #文件扩展名与文件类型映射表
    default_type  application/octet-stream; #默认文件类型,默认为text/plain
    #access_log off; #取消服务日志    
    log_format myFormat '$remote_addr–$remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for'; #自定义格式
    access_log log/access.log myFormat;  #combined为日志格式的默认值
    sendfile on;   #允许sendfile方式传输文件,默认为off,可以在http块,server块,location块。
    sendfile_max_chunk 100k;  #每个进程每次调用传输数量不能大于设定的值,默认为0,即不设上限。
    keepalive_timeout 65;  #连接超时时间,默认为75s,可以在http,server,location块。

    upstream mysvr {   
      server 127.0.0.1:7878;
      server 192.168.10.121:3333 backup;  #热备
    }
    error_page 404 https://www.baidu.com; #错误页
    server {
        keepalive_requests 120; #单连接请求上限次数。
        listen       4545;   #监听端口
        server_name  127.0.0.1;   #监听地址       
        location  ~*^.+$ {       #请求的url过滤,正则匹配,~为区分大小写,~*为不区分大小写。
           #root path;  #根目录
           #index vv.txt;  #设置默认页
           proxy_pass  http://mysvr;  #请求转向mysvr 定义的服务器列表
           deny 127.0.0.1;  #拒绝的ip
           allow 172.18.5.54; #允许的ip           
        } 
    }
}

【1】全局块:配置影响 Nginx全局的指令。一般有运行 Nginx服务器的用户组,Nginx进程 pid存放路径,日志存放路径,配置文件引入,允许生成 worker process 数等。
【2】events 块:配置影响 Nginx服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。
【3】http 块:可以嵌套多个 server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,日志自定义,是否使用 sendfile传输文件,连接超时时间,单连接请求数等。
【4】server 块:配置虚拟主机的相关参数,一个 http中可以有多个 server。
【5】location 块:配置请求的路由,以及各种页面的处理情况。

四、Nginx 与 Apache 的区别


【1】Nginx 是基于事件的 Web 服务器(select 和 epoll 函数等),Apache 是基于流程的服务器;
【2】Nginx 是所有请求都由一个线程处理,Apache 是单线程处理单个请求;
【3】Nginx 在负载均衡方面表现较好,Apache 当流量到达进程的极限时,将拒绝新的连接;
【4】Nginx 可伸缩性和性能不依赖与硬件,Apache 依赖 CPU、内存等硬件组件;
【5】Nginx 在内存消耗和连接方面比较好,Apache 在内存消耗和连接方面并没有提高;
【6】Nginx 注重速度,Apache 注重功率;
【7】Nginx 避免了子进程的概率,Apache 是基于子进程的;
【8】Nginx 处理请求是异步非阻塞的,Apache 则是阻塞型的,在高并发下Nginx 能保持低资源低消耗高性能;

五、Nginx 负载均衡算法有哪些


Nginx 的 upstream 目前支持4种方式分配:
【1】轮询(默认)每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器宕掉,会自动提出;
【2】weight:指定轮询概率,weight 和访问比率成正比,用于后端服务器不均匀的情况;
【3】ip_hash:每个请求按访问 ip 的 hash 结果分配,这样每个访客固定访问一个后端服务器。可以解决session 的问题;
【4】fair(第三方)按照后端服务器的响应时间来分配请求,响应时间块的优先分配;
【5】url_hash(第三方)根据 url 的 hash 结果分配;

六、常见状态码


499:服务端处理时间过长,客户端主动关闭了连接。
参考博客

七、静态资源配置


静态资源的缓存服务器,比如在前后端分离的项目中,为了加速前端页面的响应速度,我们可以将前端的相关资源,例如html、js、css或者图片等放到 Nginx指定的目录下,访问的时候只需要通过 IP加路径就可以实现高效快速的访问,下面说说如何在 Windows下使用 Nginx作为静态资源服务器。
【1】修改 nginx.config配置文件;
【2】主要的配置参数如下,一些无关的参数我直接去掉了,注意,里面的 location可以配置多个,这样可以根据业务的需要指定相关的路径方便后续的运维和管理:

server {
    # 访问本地绝对路径下的静态html    
    location / {
        #root   html;
        root D:/tools/nginx/2/html1;
        index  index.html index.htm;
    }

    #访问路径拼接 upload 访问本地绝对路径下的某图片 
    location /upload/ {
        alias D:/tools/nginx/2/image1/;
        autoindex on;
    }

    #访问路径拼接 /pages 访问本地绝绝对路径下的静态html    
    location /pages/ {
        alias D:/tools/nginx/2/html1/;
        autoindex on;
    }

    # 精细化 配置相关静态资源参数,优化访问静态资源文件(生产常用)
    location ~ .*\.(gif|jpg|jpeg|png)$ {
        expires 24h;  
        root D:/tools/nginx/2/image1/;#指定图片存放路径  
        proxy_store on;  
        proxy_temp_path    D:/tools/nginx/2/image1/;#图片访问路径  
        proxy_redirect     off;  
        proxy_set_header    Host 127.0.0.1;  
        client_max_body_size  10m;
        client_body_buffer_size 1280k;  
        proxy_connect_timeout  900;  
        proxy_send_timeout   900;  
        proxy_read_timeout   900;  
        proxy_buffer_size    40k;  
        proxy_buffers      40 320k;  
        proxy_busy_buffers_size 640k;  
        proxy_temp_file_write_size 640k;  
        if ( !-e $request_filename)  
          {  
             proxy_pass http://127.0.0.1;#默认80端口  
          } 

    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

八、反向代理配置


反向代理服务器可以隐藏源服务器的存在和特征。它充当互联网云和 web服务器之间的中间层。这对于安全方面来说是很好的,特别是当您使用 web托管服务时。

反向代理

九、Nginx 进程模型


Nginx 默认采用多进程工作方式,Nginx启动后,会运行一个 master进程和多个 worker进程。其中 master充当整个进程组与用户的交互接口,同时对进程进行监护,管理 worker进程来实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能。worker用来处理基本的网络事件,worker 之间是平等的,他们共同竞争来处理来自客户端的请求。Nginx的进程模型如图所示:

【1】主进程(woker process)的功能:①、读取 Nginx 配置文件并验证其有效性和正确性;②、建立、绑定和关闭 socket连接;③、按照配置生成、管理和结束工作进程;④、接受外界指令,比如重启、升级及退出服务器等指令;⑤、不中断服务,实现平滑升级,重启服务并应用新的配置;⑥、开启日志文件,获取文件描述符;⑦、不中断服务,实现平滑升级,升级失败进行回滚处理;⑧、编译和处理 perl脚本;
【2】工作进程(woker process)的功能:①、多个 worker 通过争抢的方式获取客户端的请求,而是不同通过轮询等方式,这个比较特别;②、将请求以此送入各个功能模块进行处理;③、IO调用,获取响应数据;④、与后端服务器通信,接收后端服务器的处理结果;⑤、缓存数据,访问缓存索引,查询和调用缓存数据;⑥、发送请求结果,响应客户的请求;⑦、接收主程序指令,比如重启、升级和退出等;

master 对 work 进程采用信号进行控制

十、一个 Master 和多个 Woker 有好处


【1】可以使用 nginx –s reload 热部署,利用 nginx 进行热部署操作;
【2】每个 woker 是独立的进程,如果有其中的一个 woker 出现问题,其他 woker 独立的,继续进行争抢,实现请求过程,不会造成服务中断;

十二、设置多少个 woker 合适


worker 数和服务器的 cpu 数相等是最为适宜的;

十三、发送请求,占用了 woker 的几个连接数


当访问静态资源时占2个:请求一次,返回静态资源一次;
当访问非静态资源时占4个:nginx 需要将请求发送给 Tomcat 集群中的某个节点进行处理1次,同时Tomcat 处理完之后返回结果1次;

十四、Nginx 有一个 Master,有四个 Woker,每个 Woker 支持最大的连接数 1024,支持的最大并发数是多少


普通的静态访问最大并发数是: worker_connections * worker_processes /2,而如果是 HTTP 作为反向代理来说,最大并发数量应该是 worker_connections * worker_processes/4

十五、Nginx 工作原理


Nginx 由内核和模块组成。Nginx 本身做的工作实际很少,当它接到一个 HTTP请求时,它仅仅是通过查找配置文件将此次请求映射到一个 location block,而此 location中所配置的各个指令则会启动不同的模块去完成工作,因此模块可以看做 Nginx真正的劳动工作者。通常一个 location中的指令会涉及一个 handler模块和多个 filter模块(当然,多个 location可以复用同一个模块)。handler 模块负责处理请求,完成响应内容的生成,而 filter模块对响应内容进行处理。用户根据自己的需要开发的模块都属于第三方模块。正是有了这么多模块的支撑,Nginx 的功能才会如此强大。Nginx 的模块从结构上分为核心模块、基础模块和第三方模块:
【1】核心模块:HTTP模块、EVENT模块和MAIL模块;
【2】基础模块:HTTP Access模块、HTTP FastCGI模块、HTTP Proxy模块和HTTP Rewrite模块;
【3】第三方模块:HTTP Upstream Request Hash模块、Notice模块和HTTP Access Key模块。

Nginx 的模块从功能上分为如下三类:
【1】Handlers(处理器模块)。此类模块直接处理请求,并进行输出内容和修改 headers信息等操作。Handlers处理器模块一般只能有一个;
【2】Filters (过滤器模块)。此类模块主要对其它处理器模块输出的内容进行修改操作,最后由 Nginx输出;
【3】Proxies (代理类模块)。此类模块是 Nginx的 HTTP Upstream之类的模块,这些模块主要与后端一些服务比如 FastCGI等进行交互,实现服务代理和负载均衡等功能。

十六、负载均衡配置


博客连接】:https://blog.csdn.net/zhengzhaoyang122/article/details/94287448

十七、虚拟主机配置


博客连接】:https://blog.csdn.net/zhengzhaoyang122/article/details/93793377

十八、Nginx 常用命令


【1】启动 nginx:

[root@LinuxServer sbin]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf

【2】停止 nginx -s stop 或 nginx -s quit ;
【3】重载配置 ./sbin/nginx -s reload(平滑重启) 或 service nginx reload ;
【4】重载指定配置文件, -c :使用指定的配置文件而不是 conf 目录下的 nginx.conf ;

[root@LinuxServer sbin]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf ;

【5】查看 nginx 版本 nginx -v ;
【6】检查配置文件是否正确 nginx -t ;
【7】显示帮助信息 nginx -h ;

十九、请解释什么是 C10K 问题


C10K 问题是指无法同时处理大量客户端(10,000)的网络套接字。

二十、Nginx 如何处理 Http 请求


【1】首先,Nginx 在启动时,会解析配置文件,得到需要监听的端口与 IP 地址,然后在 Nginx 的 Master 进程里面先初始化好这个监控的 Socket(创建 Socket,设置 addr、reuse 等选项,绑定到指定的 ip 地址端口,再 listen 监听)。
【2】然后,再 fork(一个现有进程可以调用 fork 函数创建一个新进程。由 fork 创建的新进程被称为子进程 )出多个子进程出来。
【3】之后,子进程会竞争 accept 新的连接。此时,客户端就可以向 nginx 发起连接了。当客户端与nginx进行三次握手,与 nginx 建立好一个连接后。此时,某一个子进程会 accept 成功,得到这个建立好的连接的 Socket ,然后创建 nginx 对连接的封装,即 ngx_connection_t 结构体。
【4】接着,设置读写事件处理函数,并添加读写事件来与客户端进行数据的交换。这里,还是有一些逻辑,继续在 「Nginx 是如何实现高并发的?」 问题中来看。
【5】Nginx 或客户端来主动关掉连接,到此,一个连接就寿终正寝了。
【6】最后,Nginx 或客户端来主动关掉连接,到此,一个连接就寿终正寝了。

二十一、fastcgi 与 cgi 的区别


FastCGI 是一个可伸缩地、高速地在 HTTP server和动态脚本语言间通信的接口。多数流行的 HTTP server都支持FastCGI,包括 Apache、Nginx 和 lighttpd 等。同时,FastCGI 也被许多脚本语言支持,其中就有 PHP。
FastCGI 是从 CGI发展改进而来的。传统 CGI接口方式的主要缺点是性能很差,因为每次 HTTP服务器遇到动态程序时都需要重新启动脚本解析器来执行解析,然后将结果返回给 HTTP服务器。这在处理高并发访问时几乎是不可用的。另外传统的 CGI接口方式安全性也很差,现在已经很少使用了。
FastCGI 接口方式采用 C/S结构,可以将 HTTP服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当 HTTP服务器每次遇到动态程序时,可以将其直接交付给 FastCGI进程来执行,然后将得到的结果返回给浏览器。这种方式可以让 HTTP服务器专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。

【1】cgi:web 服务器会根据请求的内容,然后会 fork 一个新进程来运行外部 c 程序(或 perl 脚本…), 这个进程会把处理完的数据返回给 web 服务器,最后 web 服务器把内容发送给用户,刚才 fork 的进程也随之退出。如果下次用户还请求改动态脚本,那么 web 服务器又再次 fork 一个新进程,周而复始的进行。

【2】fastcgi:web 服务器收到一个请求时,他不会重新 fork 一个进程(因为这个进程在 web 服务器启动时就开启了,而且不会退出),web 服务器直接把内容传递给这个进程(进程间通信,但 fastcgi 使用了别的方式,tcp 方式通信),这个进程收到请求后进行处理,把结果返回给 web 服务器,最后自己接着等待下一个请求的到来,而不是退出。

综上,差别在于是否重复 fork 进程,处理请求。

二十二、Nginx 是如何实现高并发的


一个主进程,多个工作进程,每个工作进程可以处理多个请求,每进来一个 request,会有一个 worker进程去处理。但不是全程的处理,处理到可能发生阻塞的地方,比如向上游(后端)服务器转发 request,并等待请求返回。那么,这个处理的 worker继续处理其他请求,而一旦上游服务器返回了,就会触发这个事件,worker才会来接手,这个 request才会接着往下走。由于web server 的工作性质决定了每个 request的大部份生命都是在网络传输中,实际上花费在 server机器上的时间片不多。这是几个进程就解决高并发的秘密所在。即 @skoo所说的 webserver刚好属于网络 IO密集型应用,不算是计算密集型。

二十三、为什么不使用多线程


Apache:创建多个进程或线程,而每个进程或线程都会为其分配 cpu和内存(线程要比进程小的多,所以worker支持比perfork高的并发),并发过大会榨干服务器资源。
Nginx:采用单线程来异步非阻塞处理请求(管理员可以配置 Nginx主进程的工作进程的数量)(epoll),不会为每个请求分配 CPU和内存资源,节省了大量资源,同时也减少了大量的 CPU的上下文切换。所以才使得 Nginx支持更高的并发。

二十四、为什么 Nginx性能这么高


得益于它的事件处理机制:异步非阻塞事件处理机制:运用了 epoll模型,提供了一个队列,排队解决;

二十五、内存池的设计


为了减少避免出现内存碎片、减少向操作系统申请内存的次数、降低各个模块的开发复杂度,Nginx采用了简单的内存池(统一申请,统一释放)。比如为每个 http请求分配一个内存池,请求结束时销毁整个内存池。

二十六、平台无关的代码实现


在核心代码都使用了与操作系统无关的代码实现,在与操作系统相关的系统调用上则分别针对各个操作系统都有独立实现,这最终造就了 Nginx的可移植性。

二十七、Nginx 进程间的通信


工作进程是由主进程生成的,主进程使用 fork()函数,在 Nginx服务器启动过程中主进程根据配置文件决定启动工作进程的数量,然后建立一张全局的工作表用于存放当前未退出的所有的工作进程,主进程生成工作进程后会将新生成的工作进程加入到工作进程表中,并建立一个单向的管道并将其传递给工作进程,该管道与普通的管道不同,它是由主进程指向工作进程的单项通道,包含了主进程想工作进程发出的指令、工作进程ID、工作进程在工作进程表中的索引和必要的文件描述符等信息。
主进程与外界通过信号机制进行通信,当接收到需要处理的信号时,它通过管道向相关的工作进程发送正确的指令,每个工作进程都有能力捕获管道中的可读事件,当管道中有可读事件的时候,工作进程就会从管道中读取并解析指令,然后采取相应的执行动作,这样就完成了主进程与工作进程的交互。

工作进程与工作进程之间的通信: 工作进程之间的通信原理基本上和主进程与工作进程之间的通信是一样的,只要工作进程之间能够取得彼此的信息,建立管道即可通信,但是由于工作进程之间是完全隔离的,因此一个进程想要直到另外一个进程的状态信息就只能通过主进程来设置了。
为了实现工作进程之间的交互,主进程在生成工作进程之后,在工作进程表中进行遍历,将该新进程的ID以及针对该进程建立的管道句柄传递给工作进程中的其它进程,为工作进程之间的通信做准备,当工作进程1向工作进程2发送指令的时候,首先在主进程给它的其他工作进程工作信息中找到2的进程ID,然后将正确的指令写入指向进程2的管道,工作进程2捕获到管道中的事件后,解析指令并进行相关操作,这样就完成了工作进程之间的通信。

二十八、事件驱动框架


Nginx事件驱动框架:所谓事件驱动架构,简单来说,就是由一些事件发生源来产生事件,由一个或多个事件收集器(epolld等)来收集、分发事件,然后许多事件处理器会注册自己感兴趣的事件,同时会“消费”这些事件。Nginx不会使用进程或线程作为事件消费者,只能是某个模块,当前进程调用模块。

传统 web服务器(如Apache)的,所谓事件局限在 TCP连接建立、关闭上,其他读写都不在是事件驱动,这时会退化成按序执行每个操作的批处理模式,这样每个请求在连接建立后都将始终占用系统资源,直到连接关闭才会释放资源。大大浪费了内存、cpu等资源。并且把一个进程或线程作为事件消费者。传统 web服务器与 Nginx间重要差别:前者每个事件消费者独占一个进程资源,后者只是被事件分发者进程短期调用而已。

二十九、请求的多阶段异步处理


请求的多阶段异步处理只能基于事件驱动框架实现,就是把一个请求的处理过程按照事件的触发方式分为多个阶段,每个阶段都可以有事件收集、分发器(epoll等)来触发。比如一个 http请求可以分为七个阶段。

三十、在 Nginx中,请说明 Rewrite模块里 break和 last的区别


官方文档的定义如下:
【1】last:停止执行当前这一轮的 ngx_http_rewrite_module指令集,然后查找匹配改变后 URI的新 location;
【2】break:停止执行当前这一轮的 ngx_http_rewrite_module指令集;
举个例子如下

location /test1.txt/ {
    rewrite /test1.txt/ /test2.txt break;
}

location ~ test2.txt {
    return 500;
}

使用 break会匹配两次 URL,如果没有满足项,就会停止匹配下面的 location,直接发起请求 www.xxx.com/test2.txt,由于不存在文件 test2.txt,则会直接显示404。
使用 last的话,会继续搜索下面是否有符合条件(符合重写后的 /test2.txt请求)的 location,匹配十次,如果十次没有得到的结果,那么就跟 break一样了。返回上面的例子,/test2.txt 刚好与面 location的条件对应上了,进入花括号{}里面的代码执行,这里会返回500。

三十一、请列举 Nginx服务器的最佳用途


Nginx 服务器的最佳用法是在网络上部署动态 HTTP内容,使用 SCGI、WSGI应用程序服务器、用于脚本的 FastCGI处理程序。它还可以作为负载均衡器。

三十二、Nginx 中 nginx.conf 配置文件的优化


nginx要开启的进程数 一般等于cpu的总核数 其实一般情况下开4个或8个就可以。
每个nginx进程消耗的内存10兆的模样
worker_cpu_affinity
仅适用于linux,使用该选项可以绑定worker进程和CPU(2.4内核的机器用不了)
假如是8 cpu 分配如下:
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000
00100000 01000000 10000000
nginx可以使用多个worker进程,原因如下:
to use SMP 
to decrease latency when workers blockend on disk I/O 
to limit number of connections per process when select()/poll() is
used The worker_processes and worker_connections from the event sections
allows you to calculate maxclients value: k max_clients = worker_processes * worker_connections
worker_rlimit_nofile 102400;
每个nginx进程打开文件描述符最大数目 配置要和系统的单进程打开文件数一致,linux 2.6内核下开启文件打开数为65535,worker_rlimit_nofile就相应应该填写65535 nginx调度时分配请求到进程并不是那么的均衡,假如超过会返回502错误。我这里写的大一点
use epoll
Nginx使用了最新的epoll(Linux 2.6内核)和kqueue(freebsd)网络I/O模型,而Apache则使用的是传统的select模型。
处理大量的连接的读写,Apache所采用的select网络I/O模型非常低效。在高并发服务器中,轮询I/O是最耗时间的操作 目前Linux下能够承受高并发
访问的Squid、Memcached都采用的是epoll网络I/O模型。
worker_connections 65535;
每个工作进程允许最大的同时连接数 (Maxclient = work_processes * worker_connections)
keepalive_timeout 75
keepalive超时时间
这里需要注意官方的一句话:
The parameters can differ from each other. Line Keep-Alive:
timeout=time understands Mozilla and Konqueror. MSIE itself shuts
keep-alive connection approximately after 60 seconds.

client_header_buffer_size 16k
large_client_header_buffers 4 32k
客户请求头缓冲大小 
nginx默认会用client_header_buffer_size这个buffer来读取header值,如果header过大,它会使用large_client_header_buffers来读取
如果设置过小HTTP头/Cookie过大 会报400 错误 nginx 400 bad request
求行如果超过buffer,就会报HTTP 414错误(URI Too Long) nginx接受最长的HTTP头部大小必须比其中一个buffer大,否则就会报400的HTTP错误(Bad Request)。
open_file_cache max 102400
使用字段:http, server, location 这个指令指定缓存是否启用,如果启用,将记录文件以下信息: ·打开的文件描述符,大小信息和修改时间. ·存在的目录信息. ·在搜索文件过程中的错误信息 -- 没有这个文件,无法正确读取,参考open_file_cache_errors 指令选项:
·max - 指定缓存的最大数目,如果缓存溢出,最长使用过的文件(LRU)将被移除
例: open_file_cache max=1000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on;
open_file_cache_errors
语法:open_file_cache_errors on | off 默认值:open_file_cache_errors off 使用字段:http, server, location 这个指令指定是否在搜索一个文件是记录cache错误.
open_file_cache_min_uses
语法:open_file_cache_min_uses number 默认值:open_file_cache_min_uses 1 使用字段:http, server, location 这个指令指定了在open_file_cache指令无效的参数中一定的时间范围内可以使用的最小文件数,如 果使用更大的值,文件描述符在cache中总是打开状态.
open_file_cache_valid
语法:open_file_cache_valid time 默认值:open_file_cache_valid 60 使用字段:http, server, location 这个指令指定了何时需要检查open_file_cache中缓存项目的有效信息.

开启gzip
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 2;
gzip_types text/plain application/x-JavaScript text/css
application/xml;
gzip_vary on;
缓存静态文件:
location ~* ^.+\.(swf|gif|png|jpg|js|css)$ {
root /usr/local/ku6/ktv/show.ku6.com/;
expires 1m;

三十三、Nginx 内核参数优化


内核参数的优化,主要是在Linux系统中针对Nginx应用而进行的系统内核参数优化。
下面给出一个优化实例以供参考。
 1. net.ipv4.tcp_max_tw_buckets = 6000 
  2. net.ipv4.ip_local_port_range = 1024 65000  
  3. net.ipv4.tcp_tw_recycle = 1 
  4. net.ipv4.tcp_tw_reuse = 1 
  5. net.ipv4.tcp_syncookies = 1 
  6. net.core.somaxconn = 262144 
  7. net.core.netdev_max_backlog = 262144 
  8. net.ipv4.tcp_max_orphans = 262144 
  9. net.ipv4.tcp_max_syn_backlog = 262144 
  10. net.ipv4.tcp_synack_retries = 1 
  11. net.ipv4.tcp_syn_retries = 1 
  12. net.ipv4.tcp_fin_timeout = 1 
  13. net.ipv4.tcp_keepalive_time = 30 
将上面的内核参数值加入/etc/sysctl.conf文件中,然后执行如下命令使之生效:
  1. [root@ localhost home]#/sbin/sysctl -p 
下面对实例中选项的含义进行介绍:
net.ipv4.tcp_max_tw_buckets :选项用来设定timewait的数量,默认是180 000,这里设为6000。
net.ipv4.ip_local_port_range:选项用来设定允许系统打开的端口范围。在高并发情况否则端口号会不够用。
net.ipv4.tcp_tw_recycle:选项用于设置启用timewait快速回收.
net.ipv4.tcp_tw_reuse:选项用于设置开启重用,允许将TIME-WAIT sockets重新用于新的TCP连接。
net.ipv4.tcp_syncookies:选项用于设置开启SYN Cookies,当出现SYN等待队列溢出时,启用cookies进行处理。
net.core.somaxconn:选项的默认值是128, 这个参数用于调节系统同时发起的tcp连接数,在高并发的请求中,默认的值可能会导致链接超时或者重传,因此,需要结合并发请求数来调节此值。
net.core.netdev_max_backlog:选项表示当每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许发送到队列的数据包的最大数目。
net.ipv4.tcp_max_orphans:选项用于设定系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。如果超过这个数字,孤立连接将立即被复位并打印出警告信息。这个限制只是为了防止简单的DoS攻击。不能过分依靠这个限制甚至人为减小这个值,更多的情况下应该增加这个值。
net.ipv4.tcp_max_syn_backlog:选项用于记录那些尚未收到客户端确认信息的连接请求的最大值。对于有128MB内存的系统而言,此参数的默认值是1024,对小内存的系统则是128。
net.ipv4.tcp_synack_retries参数的值决定了内核放弃连接之前发送SYN+ACK包的数量。
net.ipv4.tcp_syn_retries选项表示在内核放弃建立连接之前发送SYN包的数量。
net.ipv4.tcp_fin_timeout选项决定了套接字保持在FIN-WAIT-2状态的时间。默认值是60秒。正确设置这个值非常重要,有时即使一个负载很小的Web服务器,也会出现大量的死套接字而产生内存溢出的风险。
net.ipv4.tcp_syn_retries选项表示在内核放弃建立连接之前发送SYN包的数量。
如果发送端要求关闭套接字,net.ipv4.tcp_fin_timeout选项决定了套接字保持在FIN-WAIT-2状态的时间。接收端可以出错并永远不关闭连接,甚至意外宕机。
net.ipv4.tcp_fin_timeout的默认值是60秒。需要注意的是,即使一个负载很小的Web服务器,也会出现因为大量的死套接字而产生内存溢出的风险。FIN-WAIT-2的危险性比FIN-WAIT-1要小,因为它最多只能消耗1.5KB的内存,但是其生存期长些。
net.ipv4.tcp_keepalive_time选项表示当keepalive启用的时候,TCP发送keepalive消息的频度。默认值是2(单位是小时)。

三十四、利用 TCMalloc优化 Nginx的性能


TCMalloc的全称为Thread-Caching Malloc,是谷歌开发的开源工具google-perftools中的一个成员。与标准的glibc库的Malloc相比,TCMalloc库在内存分配效率和速度上要高很多,这在很大程度上提高了服务器在高并发情况下的性能,从而降低了系统的负载。下面简单介绍如何为Nginx添加TCMalloc库支持。
要安装TCMalloc库,需要安装libunwind(32位操作系统不需要安装)和google-perftools两个软件包,libunwind库为基于64位CPU和操作系统的程序提供了基本函数调用链和函数调用寄存器功能。下面介绍利用TCMalloc优化Nginx的具体操作过程。
1).安装libunwind库
可以从http://download.savannah.gnu.org/releases/libunwind下载相应的libunwind版本,这里下载的是libunwind-0.99-alpha.tar.gz。安装过程如下:
  1. [root@localhost home]#tar zxvf libunwind-0.99-alpha.tar.gz  
  2. [root@localhost home]# cd libunwind-0.99-alpha/  
  3. [root@localhost libunwind-0.99-alpha]#CFLAGS=-fPIC ./configure  
  4. [root@localhost libunwind-0.99-alpha]#make CFLAGS=-fPIC  
  5. [root@localhost libunwind-0.99-alpha]#make CFLAGS=-fPIC install 
2).安装google-perftools
可以从http://google-perftools.googlecode.com下载相应的google-perftools版本,这里下载的是google-perftools-1.8.tar.gz。安装过程如下:
 1. [root@localhost home]#tar zxvf google-perftools-1.8.tar.gz  
 2. [root@localhost home]#cd google-perftools-1.8/  
 3. [root@localhost google-perftools-1.8]# ./configure  
 4. [root@localhost google-perftools-1.8]#make && make install  
 5. [root@localhost google-perftools-1.8]#echo "/usr/
local/lib" > /etc/ld.so.conf.d/usr_local_lib.conf  
 6. [root@localhost google-perftools-1.8]# ldconfig 
至此,google-perftools安装完成。
3).重新编译Nginx
为了使Nginx支持google-perftools,需要在安装过程中添加“–with-google_perftools_module”选项重新编译Nginx。安装代码如下:
  1. [root@localhostnginx-0.7.65]#./configure \  
  2. >--with-google_perftools_module --with-http_stub_status_module  --prefix=/opt/nginx  
  3. [root@localhost nginx-0.7.65]#make  
  4. [root@localhost nginx-0.7.65]#make install 
到这里Nginx安装完成。
4).为google-perftools添加线程目录
创建一个线程目录,这里将文件放在/tmp/tcmalloc下。操作如下:
 1. [root@localhost home]#mkdir /tmp/tcmalloc  
 2. [root@localhost home]#chmod 0777 /tmp/tcmalloc 
5).修改Nginx主配置文件
修改nginx.conf文件,在pid这行的下面添加如下代码:
  1. #pid        logs/nginx.pid;  
  2. google_perftools_profiles /tmp/tcmalloc; 
接着,重启Nginx即可完成google-perftools的加载。
6).验证运行状态
为了验证google-perftools已经正常加载,可通过如下命令查看:
 1. [root@ localhost home]# lsof -n | grep tcmalloc  
 2. nginx      2395 nobody   9w  REG    8,8       0    1599440 /tmp/tcmalloc.2395  
 3. nginx      2396 nobody   11w REG   8,8       0    1599443 /tmp/tcmalloc.2396  
 4. nginx      2397 nobody   13w REG  8,8        0    1599441  /tmp/tcmalloc.2397  
 5. nginx     2398 nobody    15w REG  8,8     0    1599442 /tmp/tcmalloc.2398 
由于在Nginx配置文件中设置worker_processes的值为4,因此开启了4个Nginx线程,每个线程会有一行记录。每个线程文件后面的数字值就是启动的Nginx的pid值。
至此,利用TCMalloc优化Nginx的操作完成。

三十五、编译安装过程优化


1).减小Nginx编译后的文件大小
在编译Nginx时,默认以debug模式进行,而在debug模式下会插入很多跟踪和ASSERT之类的信息,编译完成后,一个Nginx要有好几兆字节。而在编译前取消Nginx的debug模式,编译完成后Nginx只有几百千字节。因此可以在编译之前,修改相关源码,取消debug模式。具体方法如下:
在Nginx源码文件被解压后,找到源码目录下的auto/cc/gcc文件,在其中找到如下几行:
  1. # debug  
  2. CFLAGS=”$CFLAGS -g” 
注释掉或删掉这两行,即可取消debug模式。
2.为特定的CPU指定CPU类型编译优化
在编译Nginx时,默认的GCC编译参数是“-O”,要优化GCC编译,可以使用以下两个参数:
 1. --with-cc-opt='-O3' 
 2. --with-cpu-opt=CPU  #为特定的 CPU 编译,有效的值包括:pentium, pentiumpro, pentium3, # pentium4, athlon, opteron, amd64, sparc32, sparc64, ppc64

要确定CPU类型,可以通过如下命令:
  1. [root@localhost home]#cat /proc/cpuinfo | grep "model name" 

三十六、Nginx 支持的事件模型


Nginx 支持如下处理连接的方法(I/O复用方法),这些方法可以通过 use指令指定。
【1】select– 标准方法。 如果当前平台没有更有效的方法,它是编译时默认的方法。你可以使用配置参数 –with-select_module 和 –without-select_module 来启用或禁用这个模块。
【2】poll– 标准方法。 如果当前平台没有更有效的方法,它是编译时默认的方法。你可以使用配置参数 –with-poll_module 和 –without-poll_module 来启用或禁用这个模块。
【3】kqueue– 高效的方法,使用于 FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0 和 MacOS X. 使用双处理器的MacOS X系统使用kqueue可能会造成内核崩溃。
【4】epoll – 高效的方法,使用于Linux内核2.6版本及以后的系统。在某些发行版本中,如SuSE 8.2, 有让2.4版本的内核支持epoll的补丁。
【5】rtsig – 可执行的实时信号,使用于Linux内核版本2.2.19以后的系统。默认情况下整个系统中不能出现大于1024个POSIX实时(排队)信号。这种情况 对于高负载的服务器来说是低效的;所以有必要通过调节内核参数 /proc/sys/kernel/rtsig-max 来增加队列的大小。可是从Linux内核版本2.6.6-mm2开始, 这个参数就不再使用了,并且对于每个进程有一个独立的信号队列,这个队列的大小可以用 RLIMIT_SIGPENDING 参数调节。当这个队列过于拥塞,nginx就放弃它并且开始使用 poll 方法来处理连接直到恢复正常。
【6】/dev/poll – 高效的方法,使用于 Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+ 和 Tru64 UNIX 5.1A+.
【7】eventport – 高效的方法,使用于 Solaris 10. 为了防止出现内核崩溃的问题, 有必要安装这个 安全补丁。

三十七、Nginx+FastCGI运行原理


Nginx 不支持对外部程序的直接调用或者解析,所有的外部程序(包括 PHP)必须通过 FastCGI接口来调用。FastCGI 接口在Linux 下是 socket(这个socket可以是文件socket,也可以是ip socket)。
wrapper:为了调用 CGI程序,还需要一个 FastCGI的 wrapper(wrapper可以理解为用于启动另一个程序的程序),这个wrapper 绑定在某个固定 socket上,如端口或者文件socket。当 Nginx将CGI请求发送给这个 socket的时候,通过 FastCGI接口,wrapper 接收到请求,然后 Fork(派生)出一个新的线程,这个线程调用解释器或者外部程序处理脚本并读取返回数据;接着,wrapper 再将返回的数据通过 FastCGI接口,沿着固定的 socket传递给 Nginx;最后,Nginx 将返回的数据(html页面或者图片)发送给客户端。这就是 Nginx+FastCGI的整个运作过程,如下图所示。

所以,我们首先需要一个 wrapper,这个 wrapper需要完成的工作:
【1】通过调用 fastcgi(库)的函数通过socket和ningx通信(读写socket是fastcgi内部实现的功能,对wrapper是非透明的);
【2】调度 thread,进行 fork和 kill;
【3】和 application(php)进行通信;

三十八、Nginx多进程事件模型:异步非阻塞


虽然 Nginx采用多 worker的方式来处理请求,每个 worker里面只有一个主线程,那能够处理的并发数很有限啊,多少个 worker就能处理多少个并发,何来高并发呢?非也,这就是 Nginx的高明之处,Nginx采用了异步非阻塞的方式来处理请求,也就是说,Nginx是可以同时处理成千上万个请求的。一个 worker进程可以同时处理的请求数只受限于内存大小,而且在架构设计上,不同的 worker进程之间处理并发请求时几乎没有同步锁的限制,worker进程通常不会进入睡眠状态,因此,当 Nginx上的进程数与 CPU核心数相等时(最好每一个worker进程都绑定特定的CPU核心),进程间切换的代价是最小的。

而 Apache的常用工作方式(Apache也有异步非阻塞版本,但因其与自带某些模块冲突,所以不常用),每个进程在一个时刻只处理一个请求,因此,当并发数上到几千时,就同时有几千的进程在处理请求了。这对操作系统来说,是个不小的挑战,进程带来的内存占用非常大,进程的上下文切换带来的cpu开销很大,自然性能就上不去了,而这些开销完全是没有意义的。

三十九、为什么 Nginx可以采用异步非阻塞的方式来处理


看看一个请求的完整过程:请求过来,要建立连接,然后再接收数据,接收数据后,再发送数据。

具体到系统底层,就是读写事件,而当读写事件没有准备好时,必然不可操作,如果不用非阻塞的方式来调用,那就得阻塞调用了,事件没有准备好,那就只能等了,等事件准备好了,你再继续吧。阻塞调用会进入内核等待,cpu就会让出去给别人用了,对单线程的worker来说,显然不合适,当网络事件越多时,大家都在等待呢,cpu空闲下来没人用,cpu利用率自然上不去了,更别谈高并发了。好吧,你说加进程数,这跟 apache的线程模型有什么区别,注意,别增加无谓的上下文切换。所以,在nginx里面,最忌讳阻塞的系统调用了。不要阻塞,那就非阻塞喽。非阻塞就是,事件没有准备好,马上返回EAGAIN,告诉你,事件还没准备好呢,你慌什么,过会再来吧。好吧,你过一会,再来检查一下事件,直到事件准备好了为止,在这期间,你就可以先去做其它事情,然后再来看看事件好了没。虽然不阻塞了,但你得不时地过来检查一下事件的状态,你可以做更多的事情了,但带来的开销也是不小的。

 
posted @ 2020-11-20 16:45  Java程序员进阶  阅读(446)  评论(0编辑  收藏  举报