本文主要解释nginx.conf如何配置,和为什么这样配置。
一、基本概念
1、HTTP代理和反向代理
先不说教科书上的文绉绉的字段,我也记不住。
我只说的我的理解:代理,相当于一个中间层,将服务的提供者和服务的使用者隔开。
- 服务提供者将服务(可以理解成数据)传送给代理服务器,但是不知道谁去使用该服务,这叫做正向代理。
- 服务使用者从代理服务器获取服务(数据),但不知道真正是哪台服务器提供的服务,这叫反向代理。
举个例子:如果小明去食堂吃饭,服务员端来饭菜,小明不知道这个菜是哪位厨师做的,这叫反向代理。厨师将饭菜做好后给服务员,但不知道谁去吃,这叫正向代理。
那么为什么要用代理呢?
我的理解主要有:
- 一是,可以屏蔽某一方,用于保密;
- 二是,将服务单一化、统一化,方便使用;
- 三是,对于公共的一些处理,我们可以在代理服务器上统一处理。
2、负载均衡
负载均衡,可以有两个方面的含义:
- 一方面,是将单一的重负载分担到多个网络节点上做并行处理,节点将任务处理完毕后汇总返回,这样提高网络系统的处理能力。我感觉,Elasticsearch这种搜索引擎就是采用这种”分而治之”思想。但是这种问题,需要能够划分成子问题。
- 另一方面,将大量前端并发访问分担到后端各个服务器节点上分别处理,这样可以有效减少前端用户的等待时间,减轻单机压力。Nginx服务器就是这种。
Nginx服务器的负载均衡策略有很多,可以分为两个部分:
- 内置策略:包括轮询、加权轮询、IP Hash。
- 扩展策略:是指第三方模块实现的策略,有url hash,包括我在使用的sticky策略。
3、Web缓存
作为前置的服务器,可以缓存前端请求,将页面静态文件等资源缓存,从而提高性能。
二、配置文件的基本结构
1 | work_process 1; |
上述配置文件中,将配置文件的骨架抽离出来了。
所有的配置和编程语言一样,括号外面的全局生效,里面的在{}范围内生效,出现冲突,也遵循就近原则。
整体上可以看成:
1 | --nginx.conf |
1、全局块:
影响全局,比如work_process指的是nginx可以生成几个进程共同处理请求。官方文档建议是1个,但是出于性能考虑,我认为和计算机的CPU核数相同就好。
1 | worker_process 2; #存在2个work进程处理请求 |
2、events块
影响Nginx服务器与用户的网络连接。
常用的设置有:
- 是否对多个worker process序列化
网络中有种现象叫做“惊群”,该问题产生于,当有个网络连接到来时,多个睡眠等待进程会被同时叫醒。但是,只有一个进程获得连接。如果每次叫醒的进程过多,对资源是一种浪费。
处理方案:将接受连接的进程进行序列化,避免争抢。配置如下:
1 | accept_mutex on|off; |
- 是否允许接受多个网络连接
每个 work process是否接受多个连接,配置如下:
1 | multi_accept on|off; |
- 选取哪一种事件驱动模型处理连接请求
将每个消息看成一个事件,当消息来临时。采用什么样的方式处理该事件。有select、poll和epoll等方式。配置如下:
1 | use method; |
- 每个work process可以同时支持的最大连接数目
1 | worker_connections 512; |
3、http块
重要部分!代理、缓存、日志定义等绝大多数功能都在其中。
- 文件引入、MIME-TYPE定义。
用于Nginx区分资源,比如HTML、XML、GIF等,打开mime.types文件可以看到对应的定义。
1 | include mime.types |
日志自定义。
1
access_log path [format[buffer=size]]
是否使用sendfile传输文件。
1
2sendfile on|off #是否开启
sendfile_max_chunk size #chunk的大小,nginx每次调用sendfile()传输大小不超过这个值连接超时时间。nginx与前端保持连接的时间。
1
keepalive_timeout timeout[header_timeout]
单连接请求数上限。通过某一连接发送的次数,默认100
1
keepalive_requests number;
4、server块
server块 代表虚拟主机的概念。目的是节约硬件成本,用一台主机对外可以表现出多个机器的感觉。
每个server块 就相当于一台虚拟主机。
- 配置监听
1
2listen 80;
server_name ip|域名;
5、location块
其实是server块中的一个命令,用来匹配请求,然后对该请求进行重写、转发等功能。正则匹配url后进行处理。
1 | location / { |
三、Nginx服务器事件驱动模型
记得有一次面试,面试官是网易金融的架构师。问到了事件驱动模型,我一脸懵逼,囧。所以现在遇到了,单独拉出来记录一下。
在Java的SpringMVC那一套中,每个请求都是实打实的请求request,然后交给dispacherServlet来处理各个请求。Servlet就一个实例。然后nginx采用的这套模型和java中的不同。
事件驱动模型是由事件收集器、事件发送器和事件处理器三部分基本单元构成。
在nginx中,基于事件驱动模型,这些消息事件在“事件处理器”中采用的方式是:
- “事件发送器”每传递过来一个请求,“消息事件”就将其放到一个待处理的事件的列表中,使用非阻塞I/O方式调用“事件处理器”来处理请求。
- 事件驱动处理 就是 多路I/O复用方法。在Nginx中,最常见的是:select模型、poll模型和epoll模型。
1、select模型
- 创建所关注事件的描述符集合。关注该描述符的Read、Write和Exception事件,所以创建三类事件描述符集合。
- 调用select()函数,等待事件发生。
- 轮询所有事件描述符集合中的每个事件的描述符,检查是否有相应事件发生,有了就处理。
2、poll模型
poll模型与select模型类似,创建->等待->轮询。
区别在于poll模型没有针对Read、Write和Exception事件分别做集合,而是只有一个集合,在描述符的对应结构上分别设置这三个事件。poll是select的升级版。
3、epoll模型
epoll是poll的变种,非常优秀!
之前的流程是 创建事件列表->将列表传入内核->返回结果后轮询列表;
当列表事件很多时,效率较低。
epoll让内核创建事件描述符。事件发生后,内核将事件的描述符返回给epoll,从而进行处理。所以没有轮询操作。
四、参考&致谢
《Nginx高性能Web服务器详解》 苗泽编著
《Mastering Nginx》 [瑞士]Dimitri Aivaliotis 著