nginx详解


+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
张贺,多年互联网行业工作经验,担任过网络工程师、系统集成工程师、LINUX系统运维工程师
个人网站:www.zhanghehe.cn
笔者微信:zhanghe15069028807,现居济南历下区
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-


1、并发访问

常用概念

函数调用

我们知道系统向上提供了很多的系统调用,比如有调用内存的、有调用网卡的,这些功能的被封装成了一个个的函数被用户空间的进程调用,但实际上用户空间的进程是无法直接使用内存、硬盘等硬件资源的,用户空间的进程调用了这些函数之后实际上还是内核帮助用户空间的进程完成硬件资源的调用。

用户空间到内核空间

当用户空间的进程调用了内核函数之后,CPU上就由运行用户空间进程的代码转为运行内核空间的代码

IO的两个阶段

IO有两个过程,内容从硬盘到内核空间,然后从内核空间到用户空间,这个在上一遍文章里面讲过,这里不多说。

闲等待和忙等待

当进程被阻塞之后,还有两种选择,一种选择是闲等待,就是啥是不干,就干等着,进程会进入不可中断的睡眠态,直到数据被内核取回来,为什么称之为不可中断的状态呢?假如cat /etc/passwd这个文件,cat是没有权限取出这个文件的,而是要发系统调用请求让内核帮cat取出/etc/passwd这个文件的内容,在内核取这个文件的过程当中,cat就是不可中断的睡眠状态,cat命令必须等到内核帮它取出文件之后才可以继续向下执行。

所谓忙等待,就是进程在等待时不会进入到睡眠状态,而是会频繁的询问内核是否帮助自己调用完了,用户空间进程自己一直处于焦虑状态,这就是忙等待,像上面的cat在等待内核加载数据的过程中是取于闲等待,自己啥也不干。

关于闲等待和忙等待最重要的区别是闲等待不会占用CPU的时间片,而忙等待会占用CPU的时间片。

同步和异步:

无论是闲等待还是忙等待都是在同步的前提下,也就是内核不给自己取回数据自己就无法继续执行向下执行。

异步与同步相反,即内核在帮用户空间取数据的过程中,用户空间的进程还是可以继续向下执行的,但是会通过某种机制通知内核,让内核取完数据之后告诉自己(即用户空间进程)一下,nginx响应用户请求也是如此,一个用户讲来之后,即使还没有处理完成,就可以接受下一个请求,等到第一个请求需要的内容被内核取到之后,再知nginx进程响应给用户。

同步状态下存在闲等待和忙等待两种类型的进程,无论是闲等待还是忙等待都得等待,都不能继续向下执行,所以都是同步状态。

异步状态下没有闲等待,只能忙等待。

阻塞和非阻塞

用户空间的进程一旦调用了这些函数之后,用户空间的进程状态会发生变化,有两种选项,一种是不能再继续向下执行,而是等待内核帮它执行完系统调用函数之后再向下执行,同时它还有另一种选项,就是不用等到函数的结果向下执行,第一种状态叫做阻塞(bloking),阻塞就是调用进程在等待结果之前被挂起;第二种状态叫做非阻塞(noblocking),调用结果返回之间,调用都不会被挂起,而是继续向下执行。

阻塞状态是指IO的两个阶段进程都处于阻塞状态,而非阻塞是指在IO的两个阶段仅第一段是忙等待,第二段还是阻塞状态。

IO模型

我们要为什么要讲IO模型呢?主要目的还是想通过IO模型来理解并发访问模型。

阻塞型

阻塞型IO就是两次IO进程都是阻塞的,都要等待在那里。

非阻塞型

非阻塞型IO指提第一次是不是阻塞的(忙等待),第二个过程依然是阻塞的。

复用型

比如一个进程当前正在IO,用人用ctrl+c给它发终止信号,可以让其中断吗?不行的,为什么?进程在等待内核取到数据之间是不可中断的,是阻塞状态,无法阻塞在哪一个阶段,只有它所处的那个阶段给它发终止信号他才会终止,比如cat阻塞在了内核空间,只有内核告诉它无法帮到取到数据,它才会停止,你在用户给它发停止信号它是收不到的,也就是阻塞是单向接收信号的。

复用型IO就是用来解决这个问题的,它使得一个进程可以同时接收很多方向的IO,那么它是怎么做到的呢?在复用型 IO模型下,进程在调用硬盘数据之前,会通知内核说它取一段数据,而内核会启动一个IO复用器,这个复用器有什么作用呢?这个复用器可以接到多方向的信号,最多可以同时接收1024个信号,无论哪一个信号给复用器发信号它都能立刻收到并立刻通知用户空间的进程,而select()模型就是这样的。

事件驱动型

前三个都是同步的,下面说异步的,事件驱动型IO。

不再等消息返回了,如果对方返回了怎么办?第一段不阻塞(正常处理),第二段个是阻塞的,这个效率的提升就是很明显的,因为第一步是消耗时间最长的,因为第一步是从硬盘到内核空间,硬盘到内核这是非常费时间的,主要原因还是硬盘的速度不行。

假如说A委托律师办离婚手续,委托完成之后,A就可以工作上班了,不用一直跟在律师身边,一直等到律师给他一个结果,但A要给律师一个联系方式,等到律师把事情处理完成之后,需要A签字的时候要能及时联系到A,而A也得从当前的事情当中抽出身来,继续来处理离婚的事情。事情驱动型IO也是这样的,用户空间进程委托内核取数据,进程正常忙活其它的事情,内核帮用户进程取到内核的内容之后,要通知用户空间的进程来继续处理,这就机制就是函数回调。

事件驱动型IO是网络并发访问模型里面最高级,nginx的event模型就是属于事情驱动型,而且是三级结构,主进程生成多个worker子进程,每个worker子进程可以响应多个请求,默认是1024.

nginx最常用的两个模型:复用型IO里面select()和事件驱动型IO里面epoll模型,epoll优先,默认就是epoll模型,在nginx的配置文件当中的event其实就是epoll模型。

select模型能同时接收并发1024个用户请求,而epoll没有理论上限,但实际性能差不多。

异步

第一步和第二步都不需要用户空间进程参与,内核一切全部替它干,第一阶段和第二阶段用户空间的进程全都是正常运行,用户空间的进程只要委托完内核之后,就去忙活自己的事情,内核帮用户空间的进程去硬盘当中取出数据,然后再将数据放置到用户空间,实现了完全异步,也被称之为高级IO(AIO),AIO是IO模型当中的最高级。

2、nginx初了解

介绍

解释一下上图,上图是nginx在做为一个反向代理时的架构图,从上向下看,主进程(master)主进程的作用是:装载配置文件,生成子进程,平滑升级。主进程生成了三个子进程(worker),worker进程用来响应用户的的http或https请求,至于每个worker进程可以响应多少个用户请求我们可以在配置文件当中定义,默认是1024(不要随便更改)。worker进程还可以通过http、fastCGI(通用网关接口)、memcache这三种协议对后端的web集群、php、tomcat,或memcahce进行代理,同时nginx还有代理缓存,worker进程的主要作用就是用来通过信号驱动型IO模型(epoll)和复用型IO模型(select)来响应用户请求,当然是优先使用epoll。同时nginx还提供代理缓存的功能,以便于快速响应用户的请求,代理缓存功能主要于cache loader(载入缓存对象)和cache manager(管理缓存对象)两个进程组成。整体来看,nginx在事件驱动、异步、非阻塞方面表现良好,nginx是master/worker模型,这一点一定要记住。还有一点,nginx的文件处理方面也比较优秀,采用了高级IO,异步、mmap、sendfile这几种机制,其中sendfile这种机制我们在前面的文章有讲过,mmap就是内存映射,就是将硬盘的部分数据映射到内存当中,以便于快速的存取、异步也不讲了前面也都讲过了。

nginx的官网:www.nginx.org

nginx的官网文档链接: http://nginx.org/en/docs/

nginx是调度模块化的,我们学习nginx在很大程度上是学习其模块如何使用,我们要通过官方文档来查看一些模块的用法,我们来观察一下nginx的文档结构,如下所示:

安装

关于安装nginx的详细视频,已经录制为视频,上传至百度云盘,里面详细介绍了编译安装和通过官方yum仓库的安装方法和报错解决,此处仅记录基本过程。

编译安装

yum groupinstall "Development Tools" "Server Platform Development"

yum install pcre-devel openssl-devel zlib-devel

useradd –r nginx

./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'

make && make install

官方yum仓库安装

依照官方给出的安装方法安装即可,亲测有效:

http://nginx.org/en/linux_packages.html#RHEL-CentOS

3、配置文件

传送阵

主配置文件

解释一下默认配置文件的意义

[root@n9 ~]# cat /etc/nginx/nginx.conf
//main段,即主配置段
user  nginx;   #使用哪个用户运行worker进程,不过哪个用户运行,反正master进程都得是root
worker_processes  auto; #运行几个woker进程,auto是让nginx探测一下CPU核数,一核一个,我们可以具体指明是几个。

error_log  /var/log/nginx/error.log warn;  #错误日志的路径和级别,和apache类似
pid        /var/run/nginx.pid;             #进程的pid文件,运行起来才会有值


events {                                  #epoll模型events
    worker_connections  1024;             #每个worker进程可以连接处理多少个客户端
}

//http功能段
http {
    include       /etc/nginx/mime.types; 		#mime文件(多媒体文件)的位置,如果客户请求的文件的扩展名在此文件当中存在,web服务器会通过响应报文告诉客户端的浏览器,可以直接播放,比如.txt结尾的。
    default_type  application/octet-stream;		#无法在mime类型里面识别的都用默认都是下载

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    #上面这是日志的格式,以main来命名,下面则是调用这个格式
    access_log  /var/log/nginx/access.log  main;
	
    sendfile        on;  #sendfile默认打开,我们在apache已经讲过这个了
    #tcp_nopush     on;  #后面会详细讲

    keepalive_timeout  65; #保持连接时长为65秒

    #gzip  on;  #关于压缩,后面会讲

    include /etc/nginx/conf.d/*.conf; #包含
}

main配置段指令

正常运行必备

user;使用哪个用户运行worker进程

pid /PATH/TO/PID_FILE; 指定德赫亚nginx主进程进程号的文件路径

include file | mask; 指明包含进来的其它配置文件片断

load_module file; 动态模块哪里面装载,指明要装载的动态模块

NOTE:实际上没有include和load_moudule file也可以

优化性能相关

worker_prcess number|auto worker进程的数量,通常应该等于小于当前主机cpu的物理核心数,auto当前主机物理CPU核心数

worker_cpu_affinity auto; #是否将woker进程绑定CPU核心,auto自动绑定,最好是这样。

worker_limit_nofile number;worker进程所能打开的文件的数量上限,应该大于worker数量*worker_connections

worker_priority number;指定worker进程的nice值,设定worker进程优先级[-20-20],默认是0,如果是当前服务器仅做nginx,可以把nginx的优先级调的高一些。

我们通过ps aux | grep nginx就可以过滤出nginx的进程,通过这次过滤我们会发现有5个nginx的进程,其中一个是master进程,master进程是主进程,是root用户运行的,余下的4个进程是nginx用户运行的worker进程,worker进程是master进程产生的子进程。主进程只可以有一个,而worker进程可以有多个,那么worker进程的数量取决于什么呢?worker进程的数量取决于CPU的核心数,有几个核心就会有几个worker进程。在nginx的配置文件当中,有这样一行配置worker_processes auto; 字面意思可解释为woker进程的数量设置为自动控制,其实这个自动控制就是根据CPU核心的多少来生成worker进程数量。我们不要轻易更改这个参数,因为worker进程的数量就应该小于或等于cpu的核心数,假如worker进程的数量大于CPU的核心数会有什么情况?假如说CPU只有二个核心,我们却启动了3个worker进程,那么前两个worker进程都会工作正常,而第三个worker进程就没有核心可以搭载,怎么办呢?很简单,等到内核将某一个正在运行的woker进程从CPU上踢下去之后再附加到空闲的核心上运行,这样肯定会影响nginx的效率,所以必须要保证一个worker进程起码要能长期附加在一个CPU核心之上。

再进一步改进,我们甚至可以这样,假如说我们有四个核心,同时启动了四个worker进程,我们将每个worker进程当前所对应的核心绑定到一起,不让这四个worker在不同的核心上进行切换,就将其固定住,这样做有什么好处呢?我们知道CPU一级缓存、二级缓存、三级缓存,每个核心在运行不同的worker进程的时候就要进行加载缓存,因为不同的worker进程的缓存也不一样,如果不绑定的话,每个核心对应不同的worker进程时都要重新进行加载一、二、三级缓存,这样也是浪费资源,所以我们将其绑定上,绑定上之后一、二、三级缓存就不用重新加载了,也会大大的提升nginx的效率。这么做的前提是当前主机上除了nginx之外不能再有大型服务了,比如还有一个mysql,这么做就不合适了,一旦还有别的大型服务,就会对核心进行时间切片,缓存又会被重新加载,绑定就没有多大意义了。

再进一步,尽管没有大型服务,我们绑定之后还是会有一些小的服务运行,还是会给worker进程抢占核心的资源,比如ssh,这个服务总不能关了吧!怎么办呢?我们可以在nginx开机的时候,就将nginx要用的三个核心隔离开并绑定到三个worker进程上(假设我们有四信核心,设置生成了三个worker进程),第四个核心用来运行除了nginx之外的服务,这样那隔离开的三个核心的缓存就会一直保留。

vim /etc/nginx/nginx.conf
user  nginx;
worker_processes 4;
#worker_cpu_affinity auto;
worker_rlimit_nofile 65535;
worker_priority -5;
worker_cpu_affinity 1000 0100 0010 0001;

//效果如下
[root@n9 ~]# ps -axo comm,pid,psr,ni | grep nginx
nginx            11403   0   0
nginx            11404   3  -5
nginx            11405   2  -5
nginx            11406   1  -5
nginx            11407   0  -5

调度及定位相关

daemon on|off:是否以守护进程方式运行nginx,意思就是是否后台运行,这个无需设置,默认就是on,一旦设置成off,在启动nginx的时候会一直卡着不动。

master_prcess on|off:是否以master/worker模型运行nginx,默认就是on的,也无需设备。

error_log file [LEVEL]:错误日志的的保存位置和级别,默认配置文件里面的就有。

在配置nginx.conf 的时候,有一项是指定错误日志的,默认情况下你不指定也没有关系,因为nginx很少有错误日志记录的。但有时出现问题时,是有必要记录一下错误日志的,方便我们排查问题。
error_log 级别分为 debug, info, notice, warn, error, crit 默认为crit, 该级别在日志名后边定义格式如下:
error_log /your/path/error.log crit;
crit 记录的日志最少,而debug记录的日志最多。如果你的nginx遇到一些问题,比如502比较频繁出现,但是看默认的error_log并没有看到有意义的信息,那么就可以调一下错误日志的级别,当你调成error级别时,错误日志记录的内容会更加丰富。

例如:

# 定位排错相关
error_log  /var/log/nginx/error.log warn;
daemon on;
master_process on;

事件驱动相关

events{

worker_connections NUMBER; 每个woker进程所能打开的最大并发连接数数量

use METHOD;指定并发连接请求的处理方法,比如use epoll;默认就是epoll

accept_mutex on|off;处理新的连接请求的方法,on意味着由各worker轮流处理新请求,off意味着每个新请求的到达都会通知所有的worker进程,谁先抢到谁就响应,肯定是空闲的先抢到。

}

NOTE:关于accept_mutex 到底是off还是on好,这个其实差别并不是太大,on意味着公平,一人一个轮着来,而off是谁空闲谁来,个人倾向于off。

例如:

# 事件驱动相关
events {
    use epoll;
    worker_connections  1024;
    accept_mutex off;
}

main段私人配置

[root@n9 ~]# cat /etc/nginx/nginx.conf
#-------------------------------main段如下---------------------------#
# Author:zhanghe
# WeChat:zhanghe15069028807

# 运行必备
user  nginx;
pid /var/run/nginx.pid;

# 性能优化
worker_processes 4;
worker_cpu_affinity 1000 0100 0010 0001;
#worker_cpu_affinity auto;
worker_rlimit_nofile 65535;
worker_priority -5;

# 定位排错相关
error_log  /var/log/nginx/error.log warn;
daemon on;
master_process on;

# 事件驱动相关
events {
    use epoll;
    worker_connections  1024;
    accept_mutex off;
}

http段配置

套接字相关

server{};一个server就代表一个虚拟主机。

   server {
            listen 192.168.80.59:80 default_server;
	    	#listen 192.168.80.59:443 ssl; 在使用https时要用一行来代替上一行
            server_name www.zhanghe.com;
            root /nginx/html/;
          }

如果是反代的没有root参数,会用替代proxy_pass http://192.168.0.2的参数,意思是无论请求什么,都会反向代理到192.168.0.2上面的,类似于dnat。

但你有没有想过,每个workder默认是可以接受1024个用户请求,如果超出了怎么办?这个问题就像一个饭店能坐50个客户,结果一下子来了70个客人,那20个客户怎么办呢?排除呗,nginx也是这样,如果用户请求太多了,处理不完就得排队。其实排除就是就是暂时将用户的请求放置在内存当中,那么这个内存可以存多少用户请求呢?设置listen()调用中的backlog参数,该参数限制挂起连接队列的最大长度。默认情况下,FreeBSD、DragonFly BSD和macOS上的backlog设置为-1,其他平台上的backlog设置为511。

default_server:设定默认虚拟主机;

ssl:限制仅能够通过ssl连接提供服务;

backlog=number:后援队列长度;

rvbuf=size:接收缓冲区大小;

sndbuf=size:发送缓冲区大小;

NOTE:在多数情况下,我们仅需要一个listen就够了,listen 192.168.80.59:443 ssl;表示通过443端口进来的必须是ssl加密的。

server_name 指明虚拟主机的名称

支持*通配任意长度的任意字符,例如

*.zhanghe.com     www.zhanghe.*

支持起始的字符做正则表达式模式匹配,如^www\d+.zhanghe.com$,~的意思就是要用正则表达式,以www开头,后面跟一个数字,\d是指数字,+出现一次以上,点要被转义,最后是行尾锚定。(个人觉得后面的\d有点多余了)

匹配顺序:

​ 字符精确匹配

​ 左侧*通配符

​ 右侧*通配符

​ 正则表达式

保持连接的两种:请求超时、最多100个资源。

tcp_nodelay on|off:在keepalived模式下的连续是否启用,这个我们最好要启用起来。

这是什么意思呢?假如说我们请求的页面内容很小,只有10字节,那么在tcp层要封装首部,在ip层也要封装首部,这两个首部各是20个字节,包装要比要包装的内容还要多,这不划算,怎么办呢?在保持连接的状态,我们知道客户端还有后续报文需要处理,那就等到当前这个报文和后续处理完的报文组合起来,组成一个大数据包,然后再发送出去,以节省资源的浪费,浪费只有在keepalived启用之后才会生效。

练习定义四个虚拟主机,混合三种类型,仅允许192.168.80.0/24这个网段访问。

路径相关

root PATH

设置web资源路径映射:用于指明用户请求的url所对应的本地文件系统上的文档所在目录路径,可用的位置:http,location,if in locatioin

location [=|~|~*|^~] url

在一个server中localtion可以存在多个,用于实现从url到文件系统的路径映射;nginx会根据用户请求的URI来检查定义的所有的location,并找出一个最佳匹配,而后应用其配置。说白了,其实不加location其实也可以,但为什么还要加location呢?是因为如果不加,所有的用户访问的都是server段当中定义的root路径下的内容,但通过location我们可以通过路径控制,哪个IP地址不可以访问什么内容,哪些内容访问时要认证。

locakton的意义重大,如果没有localtion的话,所有人都会访问root,而如果使用了locakton的话,就是圈定范围,哪些IP对哪些路径下的内容可以采用什么操作,比如不拒绝192.0168.80.23这台主机对/下所有内容获取,然后剩下的放行所有。

= 是精确匹配

~对URL做正则表达式匹配,区分字符大小写。

~*对URL做正则表达式模式匹配,不区分大小写

^~对URL的左半部分做匹配检查,不区分字符大小写

不带符号:匹配起始此url的所有url

优先级:

= 必须精确

^~ 左侧匹配

~* 正则匹配

不带符号长匹配,比如location /doc 就表示路径以/doc开头的,如www.zhanghe.com/doc/name.txt

不带符号短匹配,比如location / 表示路由以server_name开头的,如,www.zhanghecom ,默认配置

location = / {           #完全匹配/
    [ configuration A ]
}

location / {             #以/开头
    [ configuration B ]
}

location /documents/ {  #包含路径/documents
    [ configuration C ]
}

location ^~ /images/ {  #以images开头的
    [ configuration D ]
}

location ~* \.(gif|jpg|jpeg)$ {  #以图片格式结尾的
    [ configuration E ]
}

A、B、会把/给匹配上,但生效的是A

B会把/index.html给匹配上

B、C、会把/documents/document.html,但生效的是C,因为C更精确

B,D、E会把/images/1.gif给匹配上,但生效的是D

B、C,E会把/documents/1.jpg给匹配上,但生效的E

NOTE:location =/ 这样做没有什么意义,因为当我们输入 URL:www.zhanghe.com这个就是根,根本不用精确匹配,直接location / 这样即可。但我们要明确,假如说写了location =/的话,那www.zhanghe.com/就可以匹配上,而www.zhanghe.com/index.html就无法匹配,因为后者不精确。

root的真正含义:

location /bbs {
                root /nginx/html/;
                index index.html;
                }

当用户访问www.zhanghe.com/bbs时会被匹配上,定位到/nginx/html/bbs/index.html上,root表示location后面的第一个根。

alias与之相反:表示location后面最后一个根。

location /blog/ {
                alias /nginx/html/blog/;
                index index.html;
                }

当我们访问www.zhanghe.com/blog的时候,其实上访问的路由是/nginx/html/blog,这一点一定要注意。

//错误界面

error_page CODE FILE.HTML,比如error_page 404 /notfound.html,如下所示:

 server {
            listen 192.168.80.59:80 default_server;
            #listen 192.168.80.59:443 ssl; 在使用https时要用一行来代替上一行
            server_name www.zhanghe.com;
            location / {
                #是指从根开始的,www.zhanghe.com后面无论跟什么都是从根开始,都要匹配到里面的属性,这时默认的,不写也生效,优先级最低。
                root /nginx/html/;
                index index.html;
                deny 192.168.80.77/32;
                allow all;
                }
            location ~*(jpg|png)$ { #URL只要是pgp或png结尾的都定位到当前localtion下,也就是去/nginx/html/images里面去找
                root /nginx/html/images/;
                }
            location /bbs {
                root /nginx/html/;
                index index.html;
                }
            location /blog/ {
                alias /nginx/html/blog/;
                index index.html;
                }
                error_page 404 /notfound.html;
            }
          }

如果用户访问的界面是not found的话,不会再显示我们那个熟悉的404界面了,那个界面其实也是一个网页,我们自己也可以定义一个网页,如上所示,notfound.html默认的位置继续自/下root指定的目录下,即/nginx/html/notfound,那当然我们也可以自定义notfound.html页面的位置,如下所示:

 server {
            listen 192.168.80.59:80 default_server;
            #listen 192.168.80.59:443 ssl; 在使用https时要用一行来代替上一行
            server_name www.zhanghe.com;
            location / {
                #是指从根开始的,www.zhanghe.com后面无论跟什么都是从根开始,都要匹配到里面的属性,这时默认的,不写也生效,优先级最低。
                root /nginx/html/;
                index index.html;
                deny 192.168.80.77/32;
                allow all;
                }
            location ~*(jpg|png)$ { #URL只要是pgp或png结尾的都定位到当前localtion下,也就是去/nginx/html/images里面去找
                root /nginx/html/images/;
                }
            location /bbs {
                root /nginx/html/;
                index index.html;
                }
            location /blog/ {
                alias /nginx/html/blog/;
                index index.html;
                }
                error_page 404 /notfound.html;
                location = /notfound.html { 
                root /data/error/;
            }
          }
 #如果用户访问到/notfound.html此界面就会匹配到下面的location,实际会定向到/data/error/notfound.html文件当中。

甚至我们还可以欺骗一下用户的浏览器所显示的状态码,用户的浏览器是听我们服务器给其返回的数据,当客户端的浏览器访问到404界面时,我们可以给他返回一下200的正确状态码,欺骗客户端一下,客户端通过F12打开开发者工具后看到的就是200,挺好玩的,如下所示:

error_page 404 =200 /notfound.html;
                location = /notfound.html {
                root /data/error/;

客户端请求相关

//设定保持连接的超时时长,0表示禁止长连接,默认为75秒。
keepalive_timeout
//在一次长连接上所允许请求的资源最大数量,默认为100
keepalive_requests

上面这两个参数是密切相关的,这里不多说,因为我在另一篇博文里面已经说的很清楚了:https://www.cnblogs.com/yizhangheka/p/12067415.html

//对哪种浏览器禁用长连接,这个没必要设置,现在所有的浏览器几乎都支持浏览器。
keepalive_disable none | browser;
//向客户端发送响应报文的超时时长,此处,是指两次写操作之间的间隔时长;
send_timeout time;

send发送给客户端的时候,假如客户端掉线了,服务器将会重试,但不会一直重试,这个时候就是指重试的超时时间。

//用于接收客户端报文的body部分的缓冲区大小,默认是16K,超出此大小时,其将被暂存到磁盘上的client_body_temp_path指令所指定的位置。
client_body_buffer_size size;

body,只有nginx允许上传,请求报文当中才有body,post方法里面就有body,比如博客,可以一次上传56000字,一超就是磁盘IO,博客论坛可以调大一点。(优化)

//设定用户存储客户端请求报文的body部分的临时存储路径及子目录结构和数量。

client_body_temp_path /var/tmp/client_body 1 2 2

1:表示用一位16进程数字表示一级子目录

2:表示用2位16进制数字表示二级子目录;

2: 表示用2位进制数字表示三级目录。

每个数字就是一个16进制数字,一个16进制数字要用1111,也就是4位也表示,两个16进程就占8位,8位二进制总共有256种变化,所以一级子目录一定要取16个或256个。从一端向另一端取,到底每一级目录取几个,这就看文件的数量有多少。

如何存取的。

作用就是快速定位。

前两个记录就可以,后面了解就可以。

客户端限止相关配置

//限制响应给客户端的传输速率,单位是bytes/second,0表示没有限制。
limit_rate rate;
比如,VIP一种速率,普通用户一种速率。

//限制对指定的请求方法之外方法

limit_except method;

//只允许此网段使用
location /downloads {
            limit_rate_after 1m; #设置多少bytes过后将启动limit计数,如果小于此值则不限速
            limit_rate 500k;     #限止传输速率为500k
        }

        location / {  
            proxy_pass http://localhost:3000;
            limit_except GET { #除了GET方法之外的方法全都拒绝;
                deny all;
            }
        }

文件优化配置

aio on | off | threads[=pool];
是否启用aio功能;

directio size | off;
在Linux主机启用O_DIRECT标记,此处意味文件大于等于给定的大小时使用,例如directio 4m;

open_file_cache off;
open_file_cache max=N
nginx可以缓存以下三种信息:
(1) 文件的描述符、文件大小和最近一次的修改时间;
(2) 打开的目录结构;
(3) 没有找到的或者没有权限访问的文件的相关信息;

max=N:可缓存的缓存项上限;达到上限后会使用LRU算法实现缓存管理;
inactive=time:缓存项的非活动时长,在此处指定的时长内未被命中的或命中的次数少于open_file_cache_min_uses指令所指定的次数的缓存项即为非活动项;

open_file_cache_valid time;
缓存项有效性的检查频率;默认为60s;

open_file_cache_min_uses number;
在open_file_cache指令的inactive参数指定的时长内,至少应该被命中多少次方可被归类为活动项;

open_file_cache_errors on | off;
是否缓存查找时发生错误的文件一类的信息;

日志配置

ngx_http_log_module模块
he ngx_http_log_module module writes request logs in the specified format.

log_format name string ...;
string可以使用nginx核心模块及其它模块内嵌的变量;

access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
access_log off;

访问日志文件路径,格式及相关的缓冲的配置;
buffer=size
flush=time

open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
open_log_file_cache off;
缓存各日志文件相关的元数据信息;

max:缓存的最大文件描述符数量;
min_uses:在inactive指定的时长内访问大于等于此值方可被当作活动项;
inactive:非活动时长;
valid:验正缓存中各缓存项是否为活动项的时间间隔;

location /ngxstatus {
	stub_status;
	acess log off;
}

压缩相关

ngx_http_gzip_module:
The ngx_http_gzip_module module is a filter that compresses responses using the “gzip” method. This often helps to reduce the size of transmitted data by half or even more.

gzip on | off;
Enables or disables gzipping of responses.

gzip_comp_level level;
Sets a gzip compression level of a response. Acceptable values are in the range from 1 to 9.

gzip_disable regex ...;
Disables gzipping of responses for requests with “User-Agent” header fields matching any of the specified regular expressions.

gzip_min_length length;
启用压缩功能的响应报文大小阈值;

gzip_buffers number size;
支持实现压缩功能时为其配置的缓冲区数量及每个缓存区的大小;

gzip_proxied off | expired | no-cache | no-store | private | no_last_modified | no_etag | auth | any ...;
nginx作为代理服务器接收到从被代理服务器发送的响应报文后,在何种条件下启用压缩功能的;
off:对代理的请求不启用
no-cache, no-store,private:表示从被代理服务器收到的响应报文首部的Cache-Control的值为此三者中任何一个,则启用压缩功能;

gzip_types mime-type ...;
压缩过滤器,仅对此处设定的MIME类型的内容启用压缩功能;

//http段下:
gzip  on;
gzip_comp_level 6;
gzip_min_length 64;
gzip_proxied any;
gzip_types text/xml text/css  application/javascript;	

压缩会节省带宽,但压缩会占用大量的CPU时间片,自己需要平衡好。

https

ngx_http_ssl_module模块

ssl on | off;
Enables the HTTPS protocol for the given virtual server.

ssl_certificate file;
当前虚拟主机使用PEM格式的证书文件;

ssl_certificate_key file;
当前虚拟主机上与其证书匹配的私钥文件;

ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2];
支持ssl协议版本,默认为后三个;

ssl_session_cache off | none | [builtin[:size]] [shared:name:size];
builtin[:size]:使用OpenSSL内建的缓存,此缓存为每worker进程私有;
[shared:name:size]:在各worker之间使用一个共享的缓存;

ssl_session_timeout time;
客户端一侧的连接可以复用ssl session cache中缓存 的ssl参数的有效时长;

配置示例:

					server {
						listen 443 ssl;
						server_name www.zhangge.com;
						root /vhosts/ssl/htdocs;
						ssl on;
						ssl_certificate /etc/nginx/ssl/nginx.crt;
						ssl_certificate_key /etc/nginx/ssl/nginx.key;
						ssl_session_cache shared:sslcache:20m;
					}							

https虽然非常安全,但是消耗的资源非常厉害,前期的证书的发送、还要有cpu参与解密。

重写

应用场景

有时候我们需要将用户发起的http请求定向到https上

有时候访问zhanghe.com/bbs/定义到bbs.zhagnhe.com

定向的原理

类似于sed的查找替换,通过模式找到请求报文当中的UR,然后进行替换。

例如:

rewrite /(.*)$ https://www.zhanghe.com/$1

定向是分为两种情况,一种情况是服务器告诉客户端要去访问什么什么,客户端再重新发起请求;另一种情况是服务器内部就完成了重定向,客户端没有感觉,下面有详细说明。

当前有两个rewrite:
rewrite /(.*)\.png$ http://www.zhanghe.com/$1.jgp
rewrite /(.*)$ https://www.zhanghe.com/$1

用户的请求进来了之后,先匹配第一条,将重写的请求发送给客户端,客户端重新发起请求,然后再从一条rewrite现依次向下匹配,这样容易发生无限循环,怎么解决呢?

将用户请求的URI基于regex所描述的模式进行检查,而后完成替换;

rewrite regex replacement [flag]
将用户请求的URI基于regex所描述的模式进行检查,匹配到时将其替换为replacement指定的新的URI;
注意:如果在同一级配置块中存在多个rewrite规则,那么会自下而下逐个检查;被某条件规则替换完成后,会重新一轮的替换检查,因此,隐含有循环机制;[flag]所表示的标志位用于控制此循环机制;

如果replacement是以http://或https://开头,则替换结果会直接以重向返回给客户端;
301:永久重定向;

[flag]:
last:重写完成后停止对当前URI在当前location中后续的其它重写操作,而后对新的URI启动新一轮重写检查;提前重启新一轮循环;
break:重写完成后停止对当前URI在当前location中后续的其它重写操作,而后直接跳转至重写规则配置块之后的其它配置;结束循环;
redirect:重写完成后以临时重定向方式直接返回重写后生成的新URI给客户端,由客户端重新发起请求;不能以http://或https://开头;
permanent:重写完成后以永久重定向方式直接返回重写后生成的新URI给客户端,由客户端重新发起请求;

return
return code [text];
return code URL;
return URL;

Stops processing and returns the specified code to a client.

rewrite_log on | off;
是否开启重写日志;

if (condition) { ... }
引入一个新的配置上下文 ;条件满足时,执行配置块中的配置指令;server, location;

condition:
比较操作符:
==
!=
~:模式匹配,区分字符大小写;
~:模式匹配,不区分字符大小写;
!~:模式不匹配,区分字符大小写;
!~
:模式不匹配,不区分字符大小写;
文件及目录存在性判断:
-e, !-e
-f, !-f
-d, !-d
-x, !-x

set $variable value;
用户自定义变量 ;

示例:

if ($http_user_agent ~ MSIE) {
    rewrite ^(.*)$ /msie/$1 break;
}

if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
    set $id $1;
}

if ($request_method = POST) {
    return 405;
}

if ($slow) {
    limit_rate 10k;
}

if ($invalid_referer) {
    return 403;
}

referer

ngx_http_referer_module模块:
The ngx_http_referer_module module is used to block access to a site for requests with invalid values in the “Referer” header field.

valid_referers none | blocked | server_names | string ...;
定义referer首部的合法可用值;

none:请求报文首部没有referer首部;
blocked:请求报文的referer首部没有值;
server_names:参数,其可以有值作为主机名或主机名模式;
arbitrary_string:直接字符串,但可使用作通配符;
regular expression:被指定的正则表达式模式匹配到的字符串;要使用~打头,例如 ~.
.magedu.com;

配置示例:

valid_referers none block server_names *.zhanghe.com *.zhanghe.com zhanghe.*  zhanghe.* ~\. zhanghe\.;
				
				if($invalid_referer) {
					return http://www. zhanghe.com/invalid.jpg;
				}

直接在浏览器上输入URL的请求就没有referer,从别处跳转而来的才会referer,nono就是表示没有referer

总结

私人配置

相关面试题

1、nginx的并发访问为什么会比apache要高呢?

因为nginx采用的是epoll这种事情驱动型的并发访问模型,那么这种并发访问模型怎么就这么厉害呢?

我们用一个类比,A用户去理性店染发,正常是两个流程是一个流程是先洗头,另一个流程是上色烤一烤,就在A用户刚上色完毕后,这时B用户来了,理性师不用一直等着A用户将头发烤完,而是在A用户烤头发的过程当中就去给B用户洗头了,当A用户烤完之后,理性师再放下手头上的工作再去线A用户服务,这样可以节省时间,提高效率,与epoll模型异曲同工。

2、请介绍一下nginx

nginx是一款轻量且高性能的WEB应用服务程序,它不仅可以实现web服务的功能,而且还可以实现四层代理和七层代理,并且还可以实现邮件服务。异步非阻塞、事件驱动就nginx最大的特性。当nginx做为代理服务器时,它可以通过http协议与web集群通信,或通过fastcgi协议与tomcat、php进行通信,并能同mysql组成经理的LNMP架构。

3、Nginx常用优化配置

  1. 调整worker_processes指定Nginx需要创建的worker进程数量,刚才有提到worker进程数一般设置为和CPU核心数一致。
  2. 调整worker_connections设置Nginx最多可以同时服务的客户端数。结合worker_processes配置可以获得每秒可以服务的最大客户端数。
  3. 启动gzip压缩,可以对文件大小进行压缩,减少了客户端http的传输带宽,可以大幅度提高页面的加载速度。
  4. 启用缓存,如果请求静态资源,启用缓存是可以大幅度提升性能的。关于启用缓存可以观看Nginx缓存这篇文章:Nginx缓存原理及机制

.nginx和apache的区别
轻量级,同样起web 服务,比apache 占用更少的内存及资源
抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能
高度模块化的设计,编写模块相对简单
最核心的区别在于apache是同步多进程模型,一个连接对应一个进程;nginx是异步的,多个连接(万级别)可以对应一个进程
fastcgi与cgi的区别
cgi:
web服务器会根据请求的内容,然后会fork一个新进程来运行外部c程序(或perl脚本...), 这个进程会把处理完的数据返回给web服务器,最后web服务器把内容发送给用户,刚才fork的进程也随之退出。 如果下次用户还请求改动态脚本,那么web服务器又再次fork一个新进程,周而复始的进行。
fastcgi:
web服务器收到一个请求时,他不会重新fork一个进程(因为这个进程在web服务器启动时就开启了,而且不会退出),web服务器直接把内容传递给这个进程(进程间通信,但fastcgi使用了别的方式,tcp方式通信),这个进程收到请求后进行处理,把结果返回给web服务器,最后自己接着等待下一个请求的到来,而不是退出。

worker_processes 8; 工作进程个数
worker_connections 65535; 每个工作进程能并发处理(发起)的最大连接数(包含所有连接数)
error_log /data/logs/nginx/error.log; 错误日志打印地址
access_log /data/logs/nginx/access.log 进入日志打印地址
log_format main '$remote_addr"$request" ''$status $upstream_addr "$request_time"'; 进入日志格式

fastcgi_connect_timeout=300; #连接到后端fastcgi超时时间
fastcgi_send_timeout=300; #向fastcgi请求超时时间(这个指定值已经完成两次握手后向fastcgi传送请求的超时时间)
fastcgi_rend_timeout=300; #接收fastcgi应答超时时间,同理也是2次握手后
fastcgi_buffer_size=64k; #读取fastcgi应答第一部分需要多大缓冲区,该值表示使用1个64kb的缓冲区读取应答第一部分(应答头),可以设置为fastcgi_buffers选项缓冲区大小
fastcgi_buffers 4 64k;#指定本地需要多少和多大的缓冲区来缓冲fastcgi应答请求,假设一个php或java脚本所产生页面大小为256kb,那么会为其分配4个64kb的缓冲来缓存
fastcgi_cache TEST;#开启fastcgi缓存并为其指定为TEST名称,降低cpu负载,防止502错误发生

listen 80; 监听端口
server_name rrc.test.jiedaibao.com; 允许域名
root /data/release/rrc/web; 项目根目录
index index.php index.html index.htm; 访问根文件

502错误可能原因
(1).FastCGI进程是否已经启动
(2).FastCGI worker进程数是否不够
(3).FastCGI执行时间过长
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
(4).FastCGI Buffer不够
nginx和apache一样,有前端缓冲限制,可以调整缓冲参数
fastcgi_buffer_size 32k;
fastcgi_buffers 8 32k;
(5). Proxy Buffer不够
如果你用了Proxying,调整
proxy_buffer_size 16k;
proxy_buffers 4 16k;

-----------未完待续

posted @ 2020-02-23 11:56  张贺贺呀  阅读(801)  评论(0编辑  收藏  举报