Nginx之二nginx web服务配置详解

一、简要回顾

IO模型:

    blocking、nonblocking、multiplexing、event-driven、AIO

 

Nginx特性:non-blocking、event-driven、AIO

  所以Nginx在典型的这几项IO模型当中的所谓实现高并发服务器端编程的概念上都是支持的。

  Nginx其此版本号为奇数的通常为开发版,为偶数的通常为稳定版,当然作为我们来讲,通常在生产环境中能够用到的应该是stable版本的。

    Nginx的开发也遵循这样的一种特性,一旦它的某一个偶数版升级以后那么随之而来的比它大一个数字的奇数版将成为新的开发板,这是需要注意的一点。在1.8.0当中,他已经引进了incorporating许多新特性包括几个著名的新特性。

 

二、Nginx1.8所引入支持的新特性(要做到了解)

  • hash load balancing method(基于hash计算的负载均衡方法)
  • backed SSL certificate verification(支持后端SSL证书校验)
  • thread pools support(支持线程池)

    以后要定义一个对应的Nginx线程,能支持多少个并发连接,可以通过线程池定义,这是一个很强大的也是很新的特性。

  • proxy request buffering(代理请求的缓冲)

    那这样一来新特性的引入势必能够更多的更强大的对于所谓代理模式下的连接的管理功能。

 

二、Nginx常用指令介绍

1、主配置段的指令

  • 正常运行的必备配置:
  user USERNAME [GROUPNAME];

    指定运行worker进程的用户和组,组身份身份可省。它的组身份与用户身份是统一使用同一个身份来定义的,这一点有别于httpd。如果在编译时已经使用--user,--group指定了的话,那在配置文件中它很有可能是被注释掉的,因为它默认就会使用那一个对应的用户名来进行定义了。

    :user nginx nginx;

    注意:每一个指令必须使用分号结尾否则视为语法错误。

 

    pid /path/to/pid_file;

    指定Nginx守护进程的pid文件的位置;如果在编译时已指定在配置文件中可能被注释掉表明使用编译时指定的默认路径;

    例:pid /var/run/nginx/nginx.pid;

    大多数守护进程都会有pid文件来保存其pid信息的,包括此前学的httpd,甚至于是以fpm模式工作的php,它们都有自己的pid文件;

 

    worker_rlimit_nofile number;

    指定所有worker进程(加起来)所能够打开的最大文件句柄数;说白了就是所能够打开的最大文件数量。因为worker进程都是以Nginx用户的身份运行的,所以是一共能够打开的文件的总数。

    Linux系统上包括管理员默认都受限于所能够运行的资源的数量。每个用户默认能够打开的文件数是1024个,但是对于任何一个服务器软件来讲,如果它要监听在某个套接字上提供服务的话,那么任何一个客户端连接连入之后它都需要一个套接字文件来维持这个连接。因为Linux要靠此特性来达到一切皆文件的哲学思想或者说这样一种目的。因此,站在这个角度来讲,如果我们Nginx下能够支持1万个并发连接,那么它至少需要维持1万个套接字文件,还不包括自己打开自己的配置文件。对于要想能够支持更大的并发连接数,我们对应的每一个worker进程是不是应该设置它所能够单进程打开的文件数量提升呢?因为worker进程是以Nginx用户的身份来运行的,因此我们为了能够worker进程接受更多的并发连接,运行worker进程的用户得能够同时打开更多的文件才可以的。默认情况下的1024对于一个高并发的worker进程来讲显然是太小太小了,所以必要时我们可以通过调整此值对应于设置一个worker进程所能够打开的最大文件数。

 

    worker_rlimit_core size;

    用来指明所有的worker加起来所能够使用的总体的核心文件的最大大小。指的就是这个进程所能够使用的核心数量的或者说核心文件的最大文件体积大小。此指令一般很少需要我们手动调整。

 

  • 性能优化相关的配置:
    worker_processes #;

    指定所能够打开的worker进程的个数;通常应该为物理CPU核心数-1或-2,即通常应该略少于CPU物理核心数;要留出一个核为系统自己所使用。但是现在Nginx的版本支持它的值为auto,Tengine可以设置为auto表示自动设定,自动打开几个,自动判断。

 

    worker_cpu_affinity cpumask …;

    设定把worker进程绑定在哪些CPU上的。此处指明CPU用的是cpumask,cpumask为CPU掩码,CPU掩码指的是什么呢?假如主机上有四颗CPU,它可能用8位二进制来表示,那这四颗CPU怎么标识呢?在其对应上为1就表示那一颗CPU被选中了,如一号CPU为0000 0001、二号CPU为0000 0010、三号CPU为0000 0100,四号为0000 1000。所以这个worker_cpu_affinity后面所跟的如果只绑定在前三颗上面,只写前三个就可。

    例如:worker_cpu_affinity 00000001 00000010 00000100;

    但例如:0011表示第一和第二同时两颗。

    对于Nginx来讲,我们说过,它的工作特性是一个线程或者一个worker进程响应n个用户请求的。那么即便我们启动少于CPU的物理核心数的进程,但是它依然面临一个问题,如我们当前这个主机上所同时运行的进程数量可能会比较多,百十个进程应该是很常见的,所以即便有4个物理核心,在某一时刻,在当前CPU上所运行的也未必是Nginx的worker进程。那结果就是对于非常繁忙的worker进程来讲,它有可能被频繁的调度来调度去。那很有可能我们启动了假设叫三个进程,这三个进程到底谁运行在哪个CPU上是没法确定的。我们Linux的核心会动态的根据当前CPU的空闲数以及需要运行的进程数调度,因此可能是不断的在进行变化。这样一来带给我们的最大坏处在于由于在CPU上需要做进程切换(context switch),进程切换会消耗时间会浪费CPU,所以进程切换本身会带来额外的CPU消耗,会产生CPU的不必要的消耗,因此为了尽可能的压榨CPU,发挥CPU的最后一点一滴的性能,这就是我们要设置进程数要少于CPU核心数的原因。

    我们可以这么做,明确的指明把每一个进程绑定在指定CPU上,比如一号进程明确说明绑定在一号上面,它绝不在其它CPU核心上运行,二号进程绑定在二号CPU上,三号进程绑定在三号CPU上。所以这样一来,这三个进程一定不会被调度到其它核心上去了,但是其实这样是不可能避免进程间的切换的,因为我们虽然把它绑定在这颗CPU上并不意味着这个CPU不能运行其它进程。但是至少保证了这个对于Nginx这一个进程本身不用调度到其它CPU上去了,那带来的好处是这个进程自己的缓存不会失效,说白了就是如果说这个Nginx在运行时,我们这个Nginx进程加载了很多数据到CPU的二级缓存、一级缓存、一级指令缓存包括一级数据缓存,如果我们不幸的把它调度到另外一颗CPU上去,那在第一颗CPU的缓存的内容就会失效了,到另外一颗CPU缓存还需要重新加载的,所以我们一旦把进程绑定的话,我们这个进程自己曾经加载到CPU上的缓存即便这个进程被调度出去,等一会它在回来运行时,那些缓存却依然有效,所以保证了缓存的有效性。但是它却没办法避免进程切换。

    优点能够提升CPU缓存的命中率;

    但是不能避免context switch,我们真正要想极端去优化Nginx所在的服务器的话,还有一种方法;这种方法可能需要一些别的技能来支撑了。如:开机的时候设定主机上四颗CPU有三颗都不被内核使用,这叫CPU隔离,所以一开机我们就事先说明,内核只能使用这一颗CPU。那这样子一来,内核想调度任何进程就只能在第四颗CPU上调度。那开机完成以后,我们还要隔离中断,我们系统可能有很多中断,中断可能由硬件产生也可能是软中断,此处仅指硬件中断,硬件中断包括网卡上有报文到来或者我们的硬盘上有IO完成等等。这些也都从CPU上隔离,所以它不处理中断,也不接受任何进程调度。当启动完以后,我们明确说明第一个进程绑定在第一颗,第二个进程绑定在第二颗,第三个进程绑定在第三颗。那这三个进程不受内核调度,所以再也不会有其它进程到这颗CPU上运行了,那这就意味着专CPU专用,第一颗CPU只为运行Nginx,Nginx再也不会被切换出去了。所以这个Nginx但凡需要运行,它就自己牢牢霸占住第一颗CPU,充分发挥CPU每一时刻的运算能力来支撑Web服务的访问。

    那这样一来,三个worker线程启动起来以后,假如说每一个worker线程可以支持1万个并发,那三万个就很轻松拿下了。但是这需要做两个前提,第一,我们在启动OS时需要隔离出来这三颗CPU,第二,我们需要把中断处理从这三颗CPU上剥离出去才可以。

 

    timer_resolution interval;

  计时器解析度:降低此值,可减少gettimeofday()系统调用的次数;

    这种无谓的系统调用次数或者是我们用不到那么高的时候,降低这个值减少这个次数会在一定意义上提升Nginx性能的或者提升我们整个系统性能的。对于一个非常繁忙的服务器这点是不容忽略的。

    可用来减少降低worker线程中的计时器的解析度,降低解析度以后带来的结果是,说白了就是减少发起gettimeofday()这么一个系统调用,任何一个进程运行过程当中,如果系统调用很多,任何一次系统调用的发生必然会产生软中断,产生软中断的直接结果就是模式转换,每一次的模式转换必然会消耗时间。因为我们说过很多次,一个生产力非常强的进程应该把大量时间用在用户空间,那因此timer_resolution这个时间解析度为什么要用到这个东西呢?

    我们简单举个例子:比如任何一个请求到达Nginx的时候Nginx就得响应它,响应完以后就得记录日志了,但是到日志中会记录时间。那因此一旦有用户请求到达,我们这边处理响应而要记录日志的话那么它必须要获取系统当前时间并记录在日志文件中。怎么获取系统时间呢?通过gettimeofdate来获取时间而后把这个结果再保存到日志文件中。而如果我们的时间解析度过大的话,就可能带来的结果是他一秒钟就解析一千次,一秒钟就发起一千次系统调用,如果我把这个降低一点,比如100ms解析一次,那就意味着一秒钟只解析10次了。如果指定1s一次那就意味着1s钟只解析1次,但是我们知道时间解析度越低它的精准度就越小,时间解析度越高精度就越高,正如我们的显示器一样,分辨率越高,显示的就越逼真越清晰但是它所需要消耗的资源一定也是越大的。

    那因此为了提高性能,这个时间解析度应该是降低的。只要我们对于它的时间解析度要求不是特别高的话。

 

    worker_priority number;

  指明worker进程的优先级(nice值)。

    但是这个优先级是使用nice值来指明的,说白了其实这里是指明nice值

    nice值的范围是-20~19,-20对应的优先级是100,19对应的优先级是139。数字越小优先级越高。默认情况下每一个进程它的nice值都是0,因此其优先级为120,数字越小优先级越高,优先级越高它越被优先调度到CPU上被运行。

    例如:worker_priority -20;指定其100的优先级。那么如此高的优先级就说明我们的Nginx但凡有可能,只要没有比它优先级更高的,那么他将会被排在前面优先被调度到CPU上运行。这对于一个Web服务器来讲尤其是反代模式下的Web服务器或者是我们支持更高并发的Web服务器来讲这算是一个比较有效的设定。再次提醒,nice值越小,其对应的优先级越高。但是只有管理员才有权限调低nice值,好在我们启动Nginx的时候是以root用户启动的。只不过运行worker时,它们才会由nginx普通用户来运行它,在此之前它可以完全以root用户的身份来设定其nice值。

 

    以上是与系统优化相关的指令,这四项尤其是前两项,几乎任何时候我们新配置一个Nginx都要自己手动指定,务必要理解他们指的是什么。

 

  • 事件相关的配置:

    即放在event当中的一般而言用于使用的相关配置。

    accept_mutex {on|off};

  master调度用户请求至各worker进程时用的负载均衡锁;on表示能让多个worker轮流的、序列化的去响应新情求;

    mutex称为互斥锁,所谓互斥锁就是独占的,一般而言,任何一个进程持有锁以后其它进程都是被排除在外的,叫排它锁。当然这样解释其实并不精确,只是为了能够便于理解的。那么这个accept_mutex有什么用呢?

    内部调用用户请求至各worker时的负载均衡锁,这是一个。说白了,作为Nginx而言,它是由一个主控进程master生成多个worker进程响应用户请求的,问题是当一个请求到达时我们应该使用哪一个worker来响应它呢?因为worker和请求之间并不是一一对应的,所以当一个新的用户请求到达时,新的用户请求主控进程必须从这多个worker进程中来响应的,挑选谁呢?而accept_mutex其实就是实现这个功能的。

    如果说我们打开此功能,即启用了accept_mutex,表示让多个worker轮流的序列化的响应新请求。就是有先有后的、一个挨一个的来响应新的用户请求,否则的话,我们的master有可能随机的。

 

    accept_mutex_delay time;

    如果说accept_mutex启用了,如果某一个worker正在尝试接受用户请求的时候,那其它用户请求我们要延迟多长时间等待。所以要不然怎么叫互斥锁呢?一个请求来了大家不能抢,轮到谁了就让谁来响应。但是这个worker有可能会忙不过来,那就需要指定延迟或等多长时间。这么长时间以后,则有可能会选择为下一个。这个不是特别关键;

 

    lock_file file;

    accept_mutex用到的锁文件路径(互斥锁锁文件路径);

    既然我们指明接受用户请求的互斥锁,那么这个锁的锁文件应该是谁,就是lock_file。我们在编译时其实指明过这个锁文件了。

 

    use [epoll|rtsig(实时信号)|select|poll];

  用于指明使用的事件模型或者是复用模型机制;不建议手动指定,建议让Nginx自行选择;

    use method;注意此处的method可不是http协议的请求方法,而是指明连接处理方法,什么叫连接处理方法?对于一个并发服务器来讲,有多个用户请求到达时,我们怎么去处理用户请求呢?有多种机制,像基于事件驱动、基于复用器、基于select()来响应、基于poll()来响应、基于epoll()来响应或者叫基于event driven来响应等等。所以这里的use定义使用的事件模型的。

    Nginx会自行实现最优化选择,比如在Linux主机上能使用epoll它就不会使用select,我们说过epoll才是真正支持事件驱动的机制,你要使用select就跟我们的httpd prefork没什么区别了。它的并发连接数也势必会大大的降低的。所以说对于Nginx而言,虽然我们说它是支持使用event driven机制甚至是支持AIO的,但是事实上作为用户来讲,可以指明我们到底使用哪一种类型,就使用这个指令进行指定。

 

    worker_connections #;

  设定单个worker进程所能够处理的最大并发连接数量;

    worker的连接数,即一个worker进程所能够接受的最大并发连接数。

    例:假设worker_connections 10240;那请问当前Nginx最多能接受的最大并发连接为多少个?不是10240,这取决于worker_proccesses有多少个,如果其定义了为3个,这里定义为10240,那就是3*10240。当然如果前面定义为8个,此处定义为10240也没用,因为不可能能达到8万多个,因为我们的套接字有限,但是至少我们此处的这个数字不应该成为限制。

    因此它所能够接受的最大并发连接数等于worker_connections*work_processes。但是它有可能会小于这个值,因为这个值如果设定的大于65535个的话,一般来讲他就不会真正有效了。为了避免单个进程本身不会因为的数量小于这个值的话,可以直接定义worker_connections为51200,很多人的配置中可能是这么定义的。所以说,这个参数也是经常必须要调的参数之一。

 

  • 用于调试、定位问题:

    注意:要想能够打开调试功能,我们在编译时必须要使用—with-debug这个选项才可以,否则调试功能是没办法启用的。

    daemon {on|off};

  是否以守护进程方式运行Nginx;调试时应该设定为off;

    说白了就是如果为on就把它运行为守护进程,如果off就不运行为守护进程。不运行为守护进程就意味着它运行在前台了。运行在前台它可以把各种日志或者错误信息输出到控制台上来,我们可以在控制台上看到。这叫做daemon模式,所以在调试时将它定义为off,其它情况下应该设定为on。

 

  master_process {on|off};

  是否以master模型来运行Nginx;调试时可以设置为off;

    正常情况下应该以这种master模型运行Nginx,调试时可以设定为off;这就表示,你如果出于调试的目的只启动一个worker进程直接接受用户请求就不会说一个主进程一个子进程,因为主进程子进程两种方式不容易调试问题所在。

 

  error_log file | stderr | syslog:server=address[,parameter=value](记录到远程服务器) | memory:size(记录到内存中,内存大小) [debug | info | notice | warn | error | crit | alert | emerg];

    简单来讲,格式为:

  error_log 位置 级别;

  若要使用debug级别,需要在编译Nginx时使用了—with-debug选项;

    这是用来而配置日志功能的,所有的错误日志相关信息都会指明保存在某文件中,用于帮我们去排错的。简单来讲,它的格式其实只有三段组成,第二段主要用于指明记录到哪,第三部分主要用于指明日志级别的。默认情况下,我们通常都记录在本机指定位置下,而级别有可能为对应的warn或者notice。

    再次说明,只有使用—with-debug打开了调试功能时,我们也才可以使用debug这个级别。否则即便指定了这个指令也无效。

 

    以上是Nginx主配置段中常用到的一些参数,我们做了一些简要的说明。

 

总结:常需要进行调整的参数

    再次说明,我们将来在配置时,最常用需要修改的四个参数为:worker_proccesses、worker_connections、worker_cpu_affinity以及worker_priority。都跟worker相关。

 

示例:

    若期望修改的配置生效:

 

    这样Nginx就能够读取新配置了。

 

    只要不改Nginx端口,只需要使用nginx –s reload即可生效。

 

新改动配置生效的方式:

  nginx –s reload      #这表示向nginx的主控进程传一个参数

 

  除了reload接受的其它参数还有:stop、quit、reopen

  

三、Nginx作为WEB服务器时使用的配置

  Nginx所有跟WEB相关的配置都放在http这个上下文中,所以我们称之为http的上下文http的配置段

 

  这个配置段是由httpd的核心模块引入的,我们前面说过,Nginx的配置文件或者我们整个Nginx它是由多个模块组成的,这个模块其中有核心模块,像我们刚才讲的都是由核心模块提供的。那除了Nginx自己的核心模块之外,他还有http的标准模块、有可选的http模块还有邮件相关的模块以及第三方模块。在这些众多模块当中,其中有一个叫做标准http的模块,而标准http的模块中有一项叫做http core,说白了就是为了提供http协议相关的功能而提供的一个核心模块。它用于实现控制端口、location、错误页面、别名以及其它一些最基础的相关配置,我们称为HTTP core

 

http {}:由ngx_http_core_module模块所引入;

 

    所以我们只要用这个模块就会用到http的相关配置的部分。它是用来做什么的?说白了,http core是用来配置一个静态的WEB服务器,它能够作为像httpd一样来工作的静态WEB服务器。我们如果想让它工作在反向代理模式下,需要使用额外的反向代理模块来实现。

 

    这个配置段当中的配置框架配置主体部分:

 

    大体上遵循这样的格式,首先:

 

http {
    upstream {  #用到负载均衡或反向代理时会引入的上下文
    …
    }
    server {
        location URL {#有点类似于DocumentRoot,但是location跟DocumentRoot还不完全一样,因为对于我们的VirtualHost来讲DocumentRoot只能有一个,
             #但是对于一个虚拟主机server来讲,location可以有多个。所以它有点类似于我们对应与httpd中的location容器,用来定义URL和本地的对应关系的。 root “
/path/to/somedir”; … }#类似于httpd的<Location>,用于定义URL与本地文件系统的映射关系;所以我们说在一个server中可以有n个location,
      #因为在一个server中它对应的那个站点的URL的起始事件可以多个,比如www.magedu.com下的images、bbs等等,每一个路径都可以单独分别指定的把网页放在不同位置。
      #就这一点而言,它事实上比httpd要灵活。同一个路径或同一组URL下可以映射多组不同的路径。而在location中,它还可以对应一个URL,这个URL对应在什么地方呢?
      #那一般而言,它由于要对本地文件系统建立映射关系,所以这里边通常会有一个root用来本地文件系统路径。 location URL {
if … { … } }#同时,在location中还可以引入一个新的上下文叫if语句;如果符合某个条件,那么应该…。它还可以有条件判断的功能; } #每一个server有点类似于httpd中的<VirtualHost>;所以它是用来定义虚拟主机的 server{ … } }

  注意:与http相关的配置指令必须仅能够放置于http、server、location、upstream、if上下文中。但有些指令仅应用于这5种上下文的某些中而非全部,即不同指令它的应用位置、能够生效的位置各不相同。

 

这些配置框架中经常用到的配置指令:

  • server{}

    定义一个虚拟主机;

    演示效果:

 

    这是基于端口的虚拟主机,基于IP的只需不同的listen IP:PORT,基于名称的时序server_name不同。

 

  • listen

  指定监听的地址和端口;

  listen address[:port];
  listen port;

  (可以只指端口不指地址表示默认监听本机上的所有地址,也可以只指地址不指端口表示默认监听在80端口)

  对于Nginx来讲,其listen有三种格式:

    此前说过,Linux主机上表示套接字的地址家族有三种方式,一IPv4二IPv6三本机上通信可以基于Unix Socket文件。所以说我们对应的Nginx的虚拟主机也可以listen一个Unix的path路径,表示本地的一个socket类型的文件,一旦监听在socket类型的文件上,这意味着客户端只能是本机了。

    那这么复杂的指令到底是所谓哪般呢?为何要这么复杂呢?它所指明的这些参数应该非常容易理解,像:

    ssl:表示如果我们要启用ssl功能,应该指定ssl;

    spdy:这是Google改进的http协议的版本,它比1.1更加优越,但是现在http2.0版本已经出现了,依然取代spdy这种格式了。若有兴趣,可自行了解。

    rcvbuf:接收的缓冲的大小;

    sndbuf:发送的缓冲大小;

    accept_filter:接收时使用的过滤器;

 

    所以我们在一个listen指令上可以定义非常复杂的选项,当然对于此处而言我们完全没必要搞得这么麻烦,只需要知道我们有一个listen指令可以指定监听的地址和端口即可。

    它的更为复杂的功能其实我们只有在特殊情况下单独设定其接受缓冲和发送缓冲大小时偶尔才会用到,所以不再说其更为复杂的功能。

 

  • server_name NAME […];

    指定主机名,后可跟多个主机名;名称还可以使用正则表达式(使用正则表达式时需要以~开头作为引导)或通配符;

    server_name只能用在server中,别的地方不能使用,所以每一个指令的context表示他可用于的上下文是有要求的。

    当我们有多个server时,当用户使用其中的某一个server时,我们应该匹配到哪一个server上?如:

server{
    server_name www.test.com;  
}

server{
    server_name *.test.com
}

  当用户匹配到www.test.com应该匹配到哪一个上面?

  所以此处有做匹配时的匹配法则

  (1)先做精确匹配检查;

    也就是说若你访问到的主机名刚好与某一个server主机名一模一样,而且他没有所谓的通配的概念。那么这个就完全匹配到了,所有优先级最高的为精确匹配检查。

  (2)左侧通配符匹配检查;

    如写成*.magedu.com,当用户去访问mail.magedu.com,第一个不匹配,于是去检查第二个。而第二个这个通配符正好在左侧,而有的是这样写的:

 

server {
    server_name www.test.com;  
}

server {
    server_name *.test.com;
}

server {
    server_name mail.*;  
}

 

 

  当用户访问mail.magedu.com的时候,第二个与第三个都能匹配,如果左侧通配符能匹配于是就立即生效,否则再去检查右侧通配符。

 

 

  (3)右侧通配符匹配检查,如mail.*;

 

  (4)正则表达式匹配检查,如:~^*\.magedu.com\.com$;

 

  (5)default_server;

 

 

  若默认服务器未定义,那就自上而下找第一个server来匹配。

 

 

 

    所以说,当用户请求到达时,而我们当前主机上定义了n个虚拟主机,那我们应该应用在哪个虚拟主机之上,事实上是由server_name后面这么一个固定的检查法则检查完以后进行确定的。

 

  • root path;

 

 

  设置资源路径映射的;用于指明请求的URL所对应的资源所在的文件系统上的起始路径;

  放置的容器越大,范围越大,优先级越低。

 

 

  • location

 

    两种用法:

 

  location [ = | ~ | ~* | ^~ ] uri { ... }
  location @name { ... }

 

    根据请求的URI设定特定请求的配置的。

 

   功能:允许根据用户请求的URI来匹配定义的各location;匹配到时,此请求将被相应的location配置块中的配置所处理,例如做访问控制等功能;

 

例:

 

 

  当用户在浏览器中输入http://www.test.com/bbs/时,它到底应用在哪个location上呢?第一个与第三个能够匹配到,那到底以哪个为准呢?所以说它也有相应的优先级了,比如:

 

  =:精确匹配检查;

 

       即错一个字符都不行;

 

       因此如果访问的是http://www.test.com/,而且location = /{...},那第一个就能匹配,但如果写的是http://www.test.com/bbs/,并且location写的是=,那它就不能被第一个匹配了。因为第一个必须是根,后面不能带任何东西,这叫做精确匹配。如果不带=表示,则起始路径的都可以被匹配。

 

  ~:正则表达式模式匹配检查,区分字符大小写;

 

  ~*:正则表达式模式匹配检查,不区分字符大小写;

 

  ^~:URI的前半部分匹配,不支持正则表达式;

 

  匹配的优先级:精确匹配(=)、^~、~、~*、不带任何符号的location;

 

 

 

  验证:

 

 

 

  因为此时匹配到的第三个到/vhosts/text下去找images,然后再images下找a.txt。

 

  对于Nginx而言,location是非常关键的。

 

 

 

  • alias path;

 

  用于location配置段,实现定义路径别名;

 

  这与httpd中的alias并没有两样。用来实现路径映射;

 

    注意root表示指明路径为对应的location “/” URL;也就意味着你怎么指root,它都相对于”/”这个起始路径而言的。像刚才我们访问images下面的a.txt,这个时候images本身也是相对于这个对应的/vhosts/下面补上这个images而言,说白了你无论路径怎么映射,它是起始于/images而言的。

 

    alias表示路径映射,即location指令后定义的URL是相对于alias所指明的路径而言;

 

  如:

 

    root:

 

 

定义:
location /images/ {
    root "/vhosts/web1";
}

例如:
访问http://www.test.com/images/a.jpg表示访问的是服务器路径:/vhosts/web1/images/a.jpg

 

    alias:

 

定义:
location /images/ {
    alias "/www/pictures";
}

例如:
访问http://www.magedu.com/images/a.jpg表示访问的是服务器目录下的:/www/pictures/a.jpg

 

  验证:

 

 

  对于alias而言这个location URL路径右侧的根相对于这个整个alias后面的根,所以在images下的所有文件是直接在这个/vhosts/web1目录直接去找的。而root是images左侧这个根相对于/vhosts/web1而言。

 

  • index file

  这是一个新的叫做index的模块所提供的;

  默认主页面:

  index index.php index.html;

  表示有两个页面自左而右可以作为主页面,先搜索index.php找不着的话再去找index.html;

 

 

  • error_page code […] [=code] URI | @name(错误码或响应码或状态码可以有多个)

  根据http响应状态码指明特用的错误页面;

    例如:

  error_page 404 /404_customed.html

    说明:404_customed.html放在什么路径下都可以,看error_page定义在什么位置,对应的就相对于那个对应你所所在的location中的root而言的。

  [=code]:可省略,如果要定义的话,表示以指定的响应码进行响应,而不是默认的原来的响应码;(比如找不到页面可能用404响应,但我把它定义成302就可以了);默认表示以新资源(即指定的网页)的响应码为其响应码。

  例如:

 

    这是Nginx自带的404错误页面;

  自定义:

       使用-t可以测试语法错误;

    我们还可以自行定义它的响应码,如:

 

 

  • 基于IP的访问控制机制

  两个相关指令:

    •   allow IP/Network;
    •   deny IP/Network;

    基于IP的访问可以用在http、server、location中,看你的访问控制有多大的范围。

  举例:拒绝192.168.241.1访问演示:

   只允许192.168.241.0网段访问:

 

  • 基于用户的访问控制

  支持两种认证方式:basic、digest

  要想做基于用户的访问控制,只需要在需要做访问控制的配置段中使用如下指令:

    auth_basic “”;   #相当于httpd的AuthName;

    auth_basic_user_file “PATH/TO/PASSWORD_FILE”;   #跟用户相关的账号密码文件的位置;这个文件尽可能隐藏起来,因为不是所有用户都会使用ls –a 来查看这个文件的,
                                #所以隐藏一下总然是由好处的。

  账号密码文件建议使用htpasswd创建;htpasswd这个命令是由httpd提供的,因此需要安装httpd。

  • https服务:

  生成私钥,生成证书签署请求,并获得证书;

  演示示例:

    显示不可靠,因为浏览器没有导入CA的证书,所以它没办法验证是否可靠。

 

  • stub_status {on|off};

  状态页的启用或关闭;

  此指令仅能用于location上下文中;

  在此结果示例中:

  Active connections:活动连接数;指的是当前所有处于打开状态的连接数。如果向客户端代理的话也包括代理的。

  server accepts handled requests    #表示接收下来并已经处理的请求;

  70 70 47  #第一个数字表示已经接受过的连接数,第二个为已经处理过的连接数,第三个为已经处理过的请求数。由于我们可能已经启用了连接处理功能,
         #所以请求数可能会大于连接数。在“保持连接”模式下,请求数量可能会多于连接数量。

    Reading:正处于接收请求状态的连接数;

    Writing:请求已经接收完成,正处于处理请求或发送响应的过程中的连接数;

    Waiting:保持连接模式,且处于活动状态的连接数;(要么处于正在接收请求要么正在发送请求的状态中的连接数)

 

  • rewrite regex replacement flag;

    指的是URL rewrite,说白了是实现URL重写的。你本来请求的是某一个特定URL,服务器处理之后将URL悄悄的换成别的URL。

    例如:

    rewrite ^/images/(.*\.jpg)$ /imgs/$1 break;

       它所带来的效果是,比如说用户如果请求的是http://www.magedu.com/images/a/b/c/1.jpg,那它会把这个路径自动换成http://www.magedu.com/imgs/a/b/c/1.jpg

 

  URL重写在什么时候用到呢?比如,我们网站从a域名改到b域名了,但是两个域名都是我们的,我们期望用户以后访问时看到的都是b域名,所以所有对于a域名的访问都改成b域名下的相关地址也是可以的。再不然,我们本来的网站某个页面路径在某个URL下现在换成新的URL像我们这里的images换成imgs也可以基于URL重写实现。

    当然在完成某些SEO时也有用,比如说我们把这个动态的路径改成静态的方式,那么这会使得我们缓存服务器能缓存下来而且SEO时还能提高我们的权重。

  不过此处的flag却是大有讲究的;

  flag:标识位

    •  last:一旦被此rewrite规则重写完成后,就不再被后面的其它rewrite规则进行处理;而是由User Agent重新对重写后的URL再次发起请求并从头开始执行类似的过程。

    如:匹配规则有n条,用户请求来之后,检查第一条不匹配、第二条不匹配、…、第四条匹配了,后面的就不检查了。此时因为一重写已经成为了一个新的URL,这个新的URL会再次被重新发给Nginx服务器,Nginx 服务器一样会从第一条检查、第二条、…,如果检查第三条又被重写了,因此又一次改成新的URL再次发起请求从头到尾来检查。

    完成当前这个重写规则以后,重启处理机制。

    例如:我们访问http://www.magedu.com/images/a/b/c/1.jpg通过URL重写会转换成http://www.magedu.com/imgs/a/b/c/1.jpg,对于这个新的请求来讲,浏览器会重新用新的URL对Web服务器发起新的请求,Web服务器即Nginx收到这个新情求后一样会像此前处理任何一个请求一样处理这个请求,即此时还会再次检查这个规则,如果这个规则又能完全匹配到它的话,还会再次重写,所以这叫重启。所以第一个请求我们通过规则检查匹配到了,所以改成新的URL了,所以浏览器此时会重新对这个链接发新情求,这个新请求会经过规则的再次检查,如果检查以后没有被匹配,因此就不会被重写,于是请求就往后继续处理了。

    •  break一旦此rewrite规则重写完成之后,由User Agent对新的URL重新发起请求,且不再会被当前location内的任何rewrite规则所检查;新请求的URL就不会再被任何重写规则所检查,直接进行后续处理。

    什么时候用到break呢?就是在很不幸的情况下,两条规则可能造成循环。例如:

...
rewrite ^/images/(.*\.jpg)$ /imgs/$1 last;
rewrite ^/images/(.*\jpg)$ /images/$1 last;
...

如:访问http://www.test.com/images/a/b/c/1.jpg <--> http://www.test.com/imgs/a/b/c/1.jpg
    •  redirect以302响应码(临时重定向)返回新的URL;

    什么时候才会用302响应呢?如果说我们替换成一个目标位置是以http协议开头时是一个新的绝对路径时,可以以redirect实现。

    •  permanent以301响应码(永久重定向)返回新的URL;

 

  举例:

 

 

 

 

  • if

  语法:

  if (condition){…}

  应用环境:server、location;

  condition:

  (1) 变量名;

       变量值为空串,或者以“0”开始,则为false;其它的均为true;

       如果变量为空串,即变量没有值的话表示为假否则为真。因此任何空串或者任何为0的值甚至于任何以0起始的字符串其都为假否则则为真。

  (2) 以变量为操作数构成的比较表达式

       可使用=、!=类似的比较操作符进行测试。

       比如,看看某个变量的值是否是我们所请求的某个值。

  (3) 可实现正则表达式的模式匹配

       ~:区分大小写的模式匹配检查;

       ~*:不区分大小写的模式匹配检查;

       !~和!~*:对上述两种测试取反;

       需要注意的是,并不是在配置文件中用到模式正则表达式越高级越好,不用正则表达式是最好的,因为正则表达式引擎在匹配时必然会消耗更多的CPU使用周期,会降低性能的。

  (4) 测试路径为文件的可能性

       -f:表示是否存在;

       !-f:表示是否不存在;

  (5) 测试指定路径为目录的可能性

       -d

       !-d

  (6) 测试文件的存在性(不管是目录还是文件)

       -e

       !-e

  (7) 检查文件是否有执行权限

       -x

       !-x

  例如:

    if($http_user_agent ~* MSIE){   #若浏览器类型为MSIE,即为微软浏览器

       rewrite ^(.*)$ /msie/$1 break;

    }

       #http_user_agent为内建变量,表示客户端浏览器的类型;

    有些时候的URL重写是必须的,像根据客户端浏览器的不同返回不同的站点,定向至不同的位置上去。这样可以让用户有更好的用户体验。可以想象用户使用手机打开电脑版的页面的话那该是多么痛苦。

 

  • 防盗链

    假如站点的图片只允许站内链接或者不是我们所允许的域名通常都不允许链接。

  locating ~* \.(jpg|gif|jpeg|png)$ {

      valid_referer none blocked www.test.com;      #validreferer是Nginx自带的一个内置的指令,它用于定义哪种引用是合法的。valid表示合法的、允许的,referer表示引用者,因此valid_referer表示合法的引用者是谁。none blocked表示不做阻止的。

       if($invalid_referer){ #$invalid_referer是我们的refereer模块所引入的一个变量,它表示但凡不能够被上面定义为合法引用的,统统都会归到不合法引用上面,所以如果说这个变量的值不为空,就说明这一次引用可能是不合法的。因为为空就因为上面的已经被匹配了。

         rewrite ^/ http://www.test.com/403.html;

      }

   }

 

  • 定制访问日志格式

    使用log_format指令进行定义,类似于在httpd中使用的log_format指令;

    如果想定义成兼容给我们对应的日志格式一模一样的话,此处要使用log_format,并查找服务器端所提供的或者服务器上模块所提供的内置的变量来类似使用兼容格式,叫combined来进行定义。log_format后接的为格式名。一旦我们要想自己定义访问日志的话,千万要记得要使用access_log,如:

五、网络连接相关的配置

  我们将来把Nginx作为一个反代服务器时有可能会用到。

    例:前端使用Nginx作为反代服务器,后端使用Tomcat作为Web服务器,通过Nginx访问Tomcat时总是会出现503超时,而直接访问Tomcat时是正常的,但是通过前端Nginx访问偶尔总是会出现超时,代理服务器上去取后端内容超时。出现这种情况推测很有可能是Nginx反代时它会自己构建一个请求报文到后端Tomcat,但是构建这个请求等待Tomcat响应的时间默认设置的过短,而Tomcat自己的处理可能是需要一定的时长的。一旦这个时间过短的话,对方还没响应过来就超时了,事实上响应是正常的。

    这一些都跟网络连接相关,所以这个时候我们就不得不调节这些跟网络连接相关的超时时长。

 

  • keepalive_timeout #;

  保持连接或长连接的超时时长,默认为75s;(对于负载均衡器来讲,75有点大)

 

  • keepalive_requests #;

    在一次长连接上所能够允许请求的最大资源数;

 

  • keepalive_disable [msie6|safari|none];

    为指定类型的User Agent禁用长连接;

    禁用长连接,但是这种禁用并不是禁用整个长连接的功能,而是只为特定类型的浏览器禁用长连接,因为有些浏览器在长连接的支持上是功能有限的。

 

  • tcp_nodelay on|off;

    是否对长连接使用TCP_NODELAY选项;

    一般来讲,如果想提高用户体验的话,让用户一请求立即就能得到响应,我们还是应该启用tcp_nodelay,不让它延迟,每个包都单独发送,无论它有多小。

    tcp是一种开销比较大的连接类型,因为任何一次数据发送之前都要先三次握手而后要四次断开。如果很不幸的是,某一个连接恰好它所发送的数据量非常小,访问这个页面下一共也只有几个字节或者十几个字节而已,如果我们请求网站上的资源很多都是这很小的资源的话,会发现每一个包单独封装而且没有使用长连接结果是大量的资源开销被浪费掉了,那为了避免这么一种浪费,TCP在它的拥塞避免算法中有一种解决方案是它把多次请求的小资源合并成一次大的响应过去,比如请求了很多东西每一个都很小,把多个小的请求合并成一个响应报文响应过去了。这样子可以节约带宽可以节约请求次数。但是这样一来会有问题,想象一下对于Web站点来讲,用户请求一个资源半天还没响应,为什么呢?他等待跟其他包合并呢?这是无法接受的,所以在有些本来就具有的确拥有很多小资源的Web站点上,我们不应该启用tcp_delay的功能,所以我们把tcp延迟功能关闭,tcp_nodelay就表示tcp是不是不延迟,on就可以。

 

  • client_header_timeout #;

    读取http报文请求首部的超时时长;

    如果我么自己的服务器很繁忙自己带宽不够用,读别人读不到,因为别人挤不进来,那这个时候应该把此时长调长一些以便能读进来别人的请求,不过即便时间延长用户体验也是很差了。

    一个客户端发请求过来了,所有的数据流依然都是要编码成数据流式报文进行请求的。那我们读了第一个首部、读第二个首部、…但是客户端的带宽太小了,半天读不到一个,那到底应该等多长时间呢?

 

  • client_body_timeout #;

    读取http请求报文的body部分的超时时长。

 

  • send_timeout #;

    发送响应报文的超时时长;

 

六、fastcgi的相关配置

    Nginx与php结合时他不能像httpd与php结合一样把php直接做成Nginx的模块,这是不支持的,所以如果你想构建LNMP,方法只能有一个,php要启用为fpm模型。

    在Nginx上要想配置用户请求php页面时能够发给fpm的php来提供服务的话,方法非常简单,配置文件中有示例:

 

    fastcgi_params文件中定义了如何把用户请求时的变量值映射提供给后端的fastcgi服务器的。

posted @ 2019-04-20 18:04  LongMX  阅读(8493)  评论(0编辑  收藏  举报