笔记簿
ᴄᴏᴅɪɴɢ ɪs ᴀʀᴛ
首页
关于
搜索
登录
注册
nginx的配置说明 - nginx.conf
nginx的配置系统由一个主配置文件和其他一些辅助的配置文件构成。这些配置文件均是纯文本文件,全部位于nginx安装目录下的conf目录下。 配置文件中以#开始的行,或者是前面有若干空格或者TAB,然后再跟#的行,都被认为是注释,也就是只对编辑查看文件的用户有意义,程序在读取这些注释行的时候,其实际的内容是被忽略的。 由于除主配置文件nginx.conf以外的文件都是在某些情况下才使用的,而只有主配置文件是在任何情况下都被使用的。所以在这里我们就以主配置文件为例,来解释nginx的配置系统。 在nginx.conf中,包含若干配置项。每个配置项由配置指令和指令参数2个部分构成。指令参数也就是配置指令对应的配置值。 ### 指令概述 配置指令是一个字符串,可以用单引号或者双引号括起来,也可以不括。但是如果配置指令包含空格,一定要引起来。 ### 指令参数 指令的参数使用一个或者多个空格或者TAB字符与指令分开。指令的参数有一个或者多个TOKEN串组成。TOKEN串之间由空格或者TAB键分隔。 TOKEN串分为简单字符串或者是复合配置块。复合配置块即是由大括号括起来的一堆内容。一个复合配置块中可能包含若干其他的配置指令。 如果一个配置指令的参数全部由简单字符串构成,也就是不包含复合配置块,那么我们就说这个配置指令是一个简单配置项,否则称之为复杂配置项。例如下面这个是一个简单配置项: ```ruby error_page 500 502 503 504 /50x.html; ``` 对于简单配置,配置项的结尾使用分号结束。对于复杂配置项,包含多个TOKEN串的,一般都是简单TOKEN串放在前面,复合配置块一般位于最后,而且其结尾,并不需要再添加分号。例如下面这个复杂配置项: ```ruby location / { root /home/lynn/nginx-book/build/html; index index.html index.htm; } ``` ### 指令上下文 nginx.conf中的配置信息,根据其逻辑上的意义,对它们进行了分类,也就是分成了多个作用域,或者称之为配置指令上下文。不同的作用域含有一个或者多个配置项。 当前nginx支持的几个指令上下文: ```text main: nginx在运行时与具体业务功能(比如http服务或者email服务代理)无关的一些参数,比如工作进程数,运行的身份等。 http: 与提供http服务相关的一些配置参数。例如:是否使用keepalive啊,是否使用gzip进行压缩等。 server: http服务上支持若干虚拟主机。每个虚拟主机一个对应的server配置项,配置项里面包含该虚拟主机相关的配置。在提供mail服务的代理时,也可以建立若干server.每个server通过监听的地址来区分。 location: http服务中,某些特定的URL对应的一系列配置项。 mail: 实现email相关的SMTP/IMAP/POP3代理时,共享的一些配置项(因为可能实现多个代理,工作在多个监听地址上)。 ``` 指令上下文,可能有包含的情况出现。例如:通常http上下文和mail上下文一定是出现在main上下文里的。在一个上下文里,可能包含另外一种类型的上下文多次。例如:如果http服务,支持了多个虚拟主机,那么在http上下文里,就会出现多个server上下文。 我们来看一个示例配置: ```ruby user nobody; worker_processes 1; error_log logs/error.log info; events { worker_connections 1024; } http { server { listen 80; server_name www.linuxidc.com; access_log logs/linuxidc.access.log main; location / { index index.html; root /var/www/linuxidc.com/htdocs; } } server { listen 80; server_name www.Androidj.com; access_log logs/androidj.access.log main; location / { index index.html; root /var/www/androidj.com/htdocs; } } } mail { auth_http 127.0.0.1:80/auth.php; pop3_capabilities "TOP" "USER"; imap_capabilities "IMAP4rev1" "UIDPLUS"; server { listen 110; protocol pop3; proxy on; } server { listen 25; protocol smtp; proxy on; smtp_auth login plain; xclient off; } } ``` 在这个配置中,上面提到个五种配置指令上下文都存在。 存在于main上下文中的配置指令如下: - user - worker_processes - error_log - events - http - mail 存在于http上下文中的指令如下: - server 存在于mail上下文中的指令如下: - server - auth_http - imap_capabilities 存在于server上下文中的配置指令如下: - listen - server_name - access_log - location - protocol - proxy - smtp_auth - xclient 存在于location上下文中的指令如下: - index - root 当然,这里只是一些示例。具体有哪些配置指令,以及这些配置指令可以出现在什么样的上下文中,需要参考nginx的使用文档。 ### nginx的模块化体系结构 nginx的内部结构是由核心部分和一系列的功能模块所组成。这样划分是为了使得每个模块的功能相对简单,便于开发,同时也便于对系统进行功能扩展。为了便于描述,下文中我们将使用nginx core来称呼nginx的核心功能部分。 nginx提供了web服务器的基础功能,同时提供了web服务反向代理,email服务反向代理功能。nginx core实现了底层的通讯协议,为其他模块和nginx进程构建了基本的运行时环境,并且构建了其他各模块的协作基础。除此之外,或者说大部分与协议相关的,或者应用相关的功能都是在这些模块中所实现的。 ### 模块概述 nginx将各功能模块组织成一条链,当有请求到达的时候,请求依次经过这条链上的部分或者全部模块,进行处理。每个模块实现特定的功能。例如,实现对请求解压缩的模块,实现SSI的模块,实现与上游服务器进行通讯的模块,实现与FastCGI服务进行通讯的模块。 有两个模块比较特殊,他们居于nginx core和各功能模块的中间。这两个模块就是http模块和mail模块。这2个模块在nginx core之上实现了另外一层抽象,处理与HTTP协议和email相关协议(SMTP/POP3/IMAP)有关的事件,并且确保这些事件能被以正确的顺序调用其他的一些功能模块。 目前HTTP协议是被实现在http模块中的,但是有可能将来被剥离到一个单独的模块中,以扩展nginx支持SPDY协议。 ### 模块的分类 nginx的模块根据其功能基本上可以分为以下几种类型: ```text event module: 搭建了独立于操作系统的事件处理机制的框架,及提供了各具体事件的处理。包括ngx_events_module, ngx_event_core_module和ngx_epoll_module等。nginx具体使用何种事件处理模块,这依赖于具体的操作系统和编译选项。 phase handler: 此类型的模块也被直接称为handler模块。主要负责处理客户端请求并产生待响应内容,比如ngx_http_static_module模块,负责客户端的静态页面请求处理并将对应的磁盘文件准备为响应内容输出。 output filter: 也称为filter模块,主要是负责对输出的内容进行处理,可以对输出进行修改。例如,可以实现对输出的所有html页面增加预定义的footbar一类的工作,或者对输出的图片的URL进行替换之类的工作。 upstream: upstream模块实现反向代理的功能,将真正的请求转发到后端服务器上,并从后端服务器上读取响应,发回客户端。upstream模块是一种特殊的handler,只不过响应内容不是真正由自己产生的,而是从后端服务器上读取的。 load-balancer: 负载均衡模块,实现特定的算法,在众多的后端服务器中,选择一个服务器出来作为某个请求的转发服务器。 ``` ### nginx的请求处理 nginx使用一个多进程模型来对外提供服务,其中一个master进程,多个worker进程。master进程负责管理nginx本身和其他worker进程。 所有实际上的业务处理逻辑都在worker进程。worker进程中有一个函数,执行无限循环,不断处理收到的来自客户端的请求,并进行处理,直到整个nginx服务被停止。 worker进程中,ngx_worker_process_cycle()函数就是这个无限循环的处理函数。在这个函数中,一个请求的简单处理流程如下: - 操作系统提供的机制(例如epoll, kqueue等)产生相关的事件。 - 接收和处理这些事件,如是接受到数据,则产生更高层的request对象。 - 处理request的header和body。 - 产生响应,并发送回客户端。 - 完成request的处理。 - 重新初始化定时器及其他事件。 ### 请求的处理流程 为了让大家更好的了解nginx中请求处理过程,我们以HTTP Request为例,来做一下详细地说明。 从nginx的内部来看,一个HTTP Request的处理过程涉及到以下几个阶段。 - 初始化HTTP Request(读取来自客户端的数据,生成HTTP Request对象,该对象含有该请求所有的信息)。 - 处理请求头。 - 处理请求体。 - 如果有的话,调用与此请求(URL或者Location)关联的handler。 - 依次调用各phase handler进行处理。 在这里,我们需要了解一下phase handler这个概念。phase字面的意思,就是阶段。所以phase handlers也就好理解了,就是包含若干个处理阶段的一些handler。 在每一个阶段,包含有若干个handler,再处理到某个阶段的时候,依次调用该阶段的handler对HTTP Request进行处理。 通常情况下,一个phase handler对这个request进行处理,并产生一些输出。通常phase handler是与定义在配置文件中的某个location相关联的。 一个phase handler通常执行以下几项任务: - 获取location配置。 - 产生适当的响应。 - 发送response header。 - 发送response body。 当nginx读取到一个HTTP Request的header的时候,nginx首先查找与这个请求关联的虚拟主机的配置。如果找到了这个虚拟主机的配置,那么通常情况下,这个HTTP Request将会经过以下几个阶段的处理(phase handlers): ```text NGX_HTTP_POST_READ_PHASE:读取请求内容阶段 NGX_HTTP_SERVER_REWRITE_PHASE:Server请求地址重写阶段 NGX_HTTP_FIND_CONFIG_PHASE:配置查找阶段: NGX_HTTP_REWRITE_PHASE:Location请求地址重写阶段 NGX_HTTP_POST_REWRITE_PHASE:请求地址重写提交阶段 NGX_HTTP_PREACCESS_PHASE:访问权限检查准备阶段 NGX_HTTP_ACCESS_PHASE:访问权限检查阶段 NGX_HTTP_POST_ACCESS_PHASE:访问权限检查提交阶段 NGX_HTTP_TRY_FILES_PHASE:配置项try_files处理阶段 NGX_HTTP_CONTENT_PHASE:内容产生阶段 NGX_HTTP_LOG_PHASE:日志模块处理阶段 ``` 在内容产生阶段,为了给一个request产生正确的响应,nginx必须把这个request交给一个合适的content handler去处理。如果这个request对应的location在配置文件中被明确指定了一个content handler,那么nginx就可以通过对location的匹配,直接找到这个对应的handler,并把这个request交给这个content handler去处理。这样的配置指令包括像,perl,flv,proxy_pass,mp4等。 如果一个request对应的location并没有直接有配置的content handler,那么nginx依次尝试: - 如果一个location里面有配置 random_index on,那么随机选择一个文件,发送给客户端。 - 如果一个location里面有配置 index指令,那么发送index指令指明的文件,给客户端。 - 如果一个location里面有配置 autoindex on,那么就发送请求地址对应的服务端路径下的文件列表给客户端。 - 如果这个request对应的location上有设置gzip_static on,那么就查找是否有对应的.gz文件存在,有的话,就发送这个给客户端(客户端支持gzip的情况下)。 - 请求的URI如果对应一个静态文件,static module就发送静态文件的内容到客户端。 内容产生阶段完成以后,生成的输出会被传递到filter模块去进行处理。filter模块也是与location相关的。所有的fiter模块都被组织成一条链。输出会依次穿越所有的filter,直到有一个filter模块的返回值表明已经处理完成。 这里列举几个常见的filter模块,例如: - server-side includes。 - XSLT filtering。 - 图像缩放之类的。 - gzip压缩。 在所有的filter中,有几个filter模块需要关注一下。按照调用的顺序依次说明如下: write: 写输出到客户端,实际上是写到连接对应的socket上。 postpone: 这个filter是负责subrequest的,也就是子请求的。 copy: 将一些需要复制的buf(文件或者内存)重新复制一份然后交给剩余的body filter处理。 ```ruby #运行用户 user nobody; #启动进程,通常设置成和cpu的数量相等 worker_processes 1; #全局错误日志及PID文件 #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; #工作模式及连接数上限 events { #epoll是多路复用IO(I/O Multiplexing)中的一种方式, #仅用于linux2.6以上内核,可以大大提高nginx的性能 use epoll; #单个后台worker process进程的最大并发链接数 worker_connections 1024; # 并发总数是 worker_processes 和 worker_connections 的乘积 # 即 max_clients = worker_processes * worker_connections # 在设置了反向代理的情况下,max_clients = worker_processes * worker_connections / 4 为什么 # 为什么上面反向代理要除以4,应该说是一个经验值 # 根据以上条件,正常情况下的Nginx Server可以应付的最大连接数为:4 * 8000 = 32000 # worker_connections 值的设置跟物理内存大小有关 # 因为并发受IO约束,max_clients的值须小于系统可以打开的最大文件数 # 而系统可以打开的最大文件数和内存大小成正比,一般1GB内存的机器上可以打开的文件数大约是10万左右 # 我们来看看360M内存的VPS可以打开的文件句柄数是多少: # $ cat /proc/sys/fs/file-max # 输出 34336 # 32000 < 34336,即并发连接总数小于系统可以打开的文件句柄总数,这样就在操作系统可以承受的范围之内 # 所以,worker_connections 的值需根据 worker_processes 进程数目和系统可以打开的最大文件总数进行适当地进行设置 # 使得并发总数小于操作系统可以打开的最大文件数目 # 其实质也就是根据主机的物理CPU和内存进行配置 # 当然,理论上的并发总数可能会和实际有所偏差,因为主机还有其他的工作进程需要消耗系统资源。 # ulimit -SHn 65535 } http { #设定mime类型,类型由mime.type文件定义 include mime.types; default_type application/octet-stream; #设定日志格式 log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log logs/access.log main; #sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件, #对于普通应用,必须设为 on, #如果用来进行下载等应用磁盘IO重负载应用,可设置为 off, #以平衡磁盘与网络I/O处理速度,降低系统的uptime. sendfile on; #tcp_nopush on; #连接超时时间 #keepalive_timeout 0; keepalive_timeout 65; tcp_nodelay on; #开启gzip压缩 gzip on; gzip_disable "MSIE [1-6]."; #设定请求缓冲 client_header_buffer_size 128k; large_client_header_buffers 4 128k; #设定虚拟主机配置 server { #侦听80端口 listen 80; #定义使用 www.nginx.cn访问 server_name www.nginx.cn; #定义服务器的默认网站根目录位置 root html; #设定本虚拟主机的访问日志 access_log logs/nginx.access.log main; #默认请求 location / { #定义首页索引文件的名称 index index.php index.html index.htm; } # 定义错误提示页面 error_page 500 502 503 504 /50x.html; location = /50x.html { } #静态文件,nginx自己处理 location ~ ^/(images|javascript|js|css|flash|media|static)/ { #过期30天,静态文件不怎么更新,过期可以设大一点, #如果频繁更新,则可以设置得小一点。 expires 30d; } #PHP 脚本请求全部转发到 FastCGI处理. 使用FastCGI默认配置. location ~ .php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } #禁止访问 .htxxx 文件 location ~ /.ht { deny all; } } } ``` nginx的upstream目前支持4种方式的分配 1、轮询(默认) 每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。 2、weight 指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。 例如: ```ruby upstream bakend { server 192.168.0.14 weight=10; server 192.168.0.15 weight=10; } ``` 2、ip_hash 每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。 例如: ```ruby upstream bakend { ip_hash; server 192.168.0.14:88; server 192.168.0.15:80; } ``` 3、fair(第三方) 按后端服务器的响应时间来分配请求,响应时间短的优先分配。 ```ruby upstream backend { server server1; server server2; fair; } ``` 4、url_hash(第三方) 按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。 例:在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法 ```ruby upstream backend { server squid1:3128; server squid2:3128; hash $request_uri; hash_method crc32; } ``` tips: ```ruby upstream bakend{#定义负载均衡设备的Ip及设备状态}{ ip_hash; server 127.0.0.1:9090 down; server 127.0.0.1:8080 weight=2; server 127.0.0.1:6060; server 127.0.0.1:7070 backup; } ``` 在需要使用负载均衡的server中增加 `proxy_pass http://bakend/;` 每个设备的状态设置为: 1.down表示单前的server暂时不参与负载 2.weight为weight越大,负载的权重就越大。 3.max_fails:允许请求失败的次数默认为1.当超过最大次数时,返回proxy_next_upstream模块定义的错误 4.fail_timeout:max_fails次失败后,暂停的时间。 5.backup: 其它所有的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力会最轻。 nginx支持同时设置多组的负载均衡,用来给不用的server来使用。 client_body_in_file_only设置为On 可以讲client post过来的数据记录到文件中用来做debug client_body_temp_path设置记录文件的目录 可以设置最多3层目录 location对URL进行匹配.可以进行重定向或者进行新的代理 负载均衡 1、通过不同的端口区分不同的虚拟主机 ```ruby server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } #error_page 404 /404.html; error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } server { listen 81; server_name localhost; location / { root html; index index.html index.htm; } #error_page 404 /404.html; error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } ``` 2、通过不同的域名区分不同的虚拟主机 ```ruby server { listen 80; server_name baidu.com; location / { root html; index index.html index.htm; } #error_page 404 /404.html; error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } server { listen 80; server_name jd.com; location / { root html; index index.html index.htm; } #error_page 404 /404.html; error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } ``` 3、配置反向代理 ```ruby upstream xxxx{ #ip_hash; server 192.168.217.135:8080; server 192.168.217.136:8080; } server { listen 80; server_name www.xxxx.com xxxx.com; location / { proxy_pass http://xxxx$request_uri;; proxy_set_header Host $host:$server_port; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } upstream oooo{ #ip_hash; server 192.168.217.135:8080; server 192.168.217.136:8080; } server { listen 80; server_name www.oooo.com oooo.com; location / { proxy_pass http://oooo$request_uri;; proxy_set_header Host $host:$server_port; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } ```