基于 Nginx1.14-1.16
Nginx 介绍
Nginx 有以下功能:
- 负载均衡
- HTTP web 服务器
- http 协议的反向代理服务器
- pop3\smtp\imap4 等邮件协议的反向代理
- 能缓存打开的文件(元数据) ,支持 FastCGI、uWSGI 协议,作为缓存服务器
- 模块化(非 DSO 机制),过滤器 zip,SSI,SSL
特性:
- 模块化设计,较好扩展性
- 高可靠性:master/worker 支持多进程,也支持多线程
- 支持热备份:不停机更新配置文件,更换日志,更新服务器程序版本
- 低内存消耗:10000 个 keep-alive 连接模式下非活动连接仅消耗 2.5M 内存
- 热部署(平滑升级):旧的配置维持现状,新的配置立刻使用,并在使用中逐步自动将旧配置替换为新配置。
Nginx 架构:
nginx 会以 daemon 方式启动进程,后台进程包含一个 master 进程和多个 worker 进程。
master 进程用于启动管理多个 worker 进程,若取消 master 进程,则 nginx 会以单进程运行
一个请求只能在一个 worker 进程中处理,一个 worker 进程只能处理一个请求。worker 进程个数一般设置为 cpu 核数
- master:加载配置文件、管理 worker 进程、平滑升级
- worker:http 服务、http 代理、fastcgi 代理
- 事件驱动:epoll
- 消息通知:select、poll、rt signals
- 模块类型:核心模块、标准 http 模块、可选 http 模块、邮件模块、第三方模块
Master 进程完成的工作:
- 读取并验证配置文件
- 创建、绑定及关闭套接字
- 启动、终止及维护 worker 进程
- 无需终止服务而重新配置
- 控制非中断式程序升级(平滑升级),启用新的二进制程序,并且能在需要时回滚到老版本
- 重新打开日志文件
- 可编译嵌入式 perl 脚本
Worker 进程完成的工作:
- 接收、传入并处理来自客户端的请求
- 提供反向代理及过滤功能
请求过程:
- 在 master 进程里面,先建立好需要 listen 的 socket(listenfd)之后,然后再 fork 出多个 worker 进程
- 所有 worker 进程的 listenfd 会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有 worker 进程在注册 listenfd 读事件前抢 accept_mutex,抢到互斥锁的那个进程注册 listenfd 读事件,在读事件里调用 accept 接受该连接。
- 当一个 worker 进程在 accept 这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接
IO 复用:
单进程模型:阻塞,一次仅处理一个请求,无法应对高并发场景
多进程模型:每个进程响应一个请求。缺陷:当请求量庞大时,会占用大量 CPU 及内存资源,因为每个请求访问的页面都会单独缓存,若访问的页面相同,会造成内存使用效率低,且进程间切换会消耗大量 CPU 资源
Linux 原生并不支持线程,Windows 和 SunOS 都支持。Linux 将线程当做进程来处理,但称作 LWP 轻量级进程(Light Weight Process),Linux 进行线程管理是通过调用各种线程库实现的。
多线程单请求模型:每个线程响应一个请求。线程和进程类似,也需要切换,但线程是轻量级的进程,切换消耗的资源很小。且同一进程内的线程共享一片存储空间,若有访问同一资源的请求,也可直接从存储空间调用该资源。线程对内存资源的需求量也比进程要小。而如果 CPU 数量只有 1 个,则多线程的优势基本无法体现,只有在多颗 CPU 的情况下,多线程才能发挥出极高的效率。
多线程多请求模型:一个线程响应多个请求。
Nginx 安装
在安装 nginx 前需要安装以下环境:
- gcc 与 gcc-c++等,可直接组安装 Development Tools
- prce-devel(perl 的正则表达库)
- zlib 与 zlib-devel(资料压缩的函数库)
- openssl 与 openssl-devel(安全套接字密码库)
注:带 devel 的包是一定要安装的。
创建系统用户组groupadd -r nginx
创建系统useradd -r nginx -M -g nginx
-r
创建系统用户(组),-M
不创建该用户家目录
创建目录/var/tmp/nginx/client
,否则在后面运行时可能报错。
初始化文件模块配置
./configure --prefix=/usr/local/nginx \ # 设置nginx安装目录 |
之后make && make install
也可以通过配置的 yum 源安装,此例是 centos7 的 repo 配置。然后直接yum install
最新的版本。
[nginx] |
nginx 命令
nginx |
Nginx 配置文件解析
配置文件/etc/nginx.conf
配置文件组织结构:
- 全局配置:main
- 模块配置:events,http,server,location
注:配置的每条指令的结尾必须加上分号;
全局配置
main 块
- 正常运行配置:
user Username [Groupname] # 指定运行worker进程的用户 |
- 性能优化配置:
worker_processes # worker进程个数,通常为物理CPU核心数量-1 |
worker_processes
和work_connections
决定了最大并发数量。最大并发量即为worker_processes × work_connections
而在反向代理场景,因为 nginx 既要维持和客户端的连接,又要维持和后端服务器的连接,因此处理一次连接要占用 2 个连接,所以最大并发数为:worker_processes × worker_connections/2
Nginx 可能还会打开其他的连接,这些都会占用文件描述符,影响并发数量
最大并发数量还受”允许打开的最大文件描述符数量”限制,可以使用worker_rlimit_nofile
指令修改。或直接通过ulimit -n
修改
- 调试定位配置:
daemon {on|off} # 是否以守护进程方式启动 |
模块配置
events 块
events { }
事件驱动,并发响应连接。控制 Nginx 处理连接的方式
包含以下配置:
worker_connections # 每个worker进程能响应的最大并发请求数量 |
http 块
http { }
处理 http 响应的配置
注:http 块下的文件相对路径是相对于 Nginx 的配置文件目录
包含以下配置:
#include 用于引入配置文件 |
server 块
#虚拟主机监听的本地端口和主机名 |
location 块
目录映射。允许根据用户请求的 URI 匹配定义的 location,匹配到就按该 location 中的配置处理
location 的第一个/
为 root 中指定路径的最后的/
alias
设置别名,用于定义路径别名,只用于 location 配置段。
location /images { |
与 root 和 alias 指令相关的变量为$document_root
、$realpath_root
。
$document_root
的值即是 root 指令、alias 指令的值
$realpath_root
的值是对 root、alias 指令进行绝对路径换算后的值
location
匹配
不带符号 URI:匹配该资源目录下的所有资源
=
URL 精确匹配,只匹配该资源~
正则表达式匹配,区分大小写~*
正则表达式匹配,不区分大小写(例如location ~* \.(gif|jpg)$
匹配.gif
或jpg
结尾的文件)^~
URI 左半部分匹配,非正则匹配,不区分大小写
优先级:=
> ^~
>~
或~*
> 不带符号的 URI
若要将 Nginx 作为文件存放服务器,在 location 块中添加autoindex on;
即可,并且要确保文件存放的目录中没有首页文件。
访问控制、身份认证与 SSL
Nginx 的两个配置访问控制的指令allow
和deny
,由模块ngx_http_access_module
提供。
allow|deny IP地址[/mask] |
若同一个块中同时配置了多条deny
或allow
,则先出现的访问权限生效。若多个块中都配置了权限指令,则内层的优先级高于外层的。
被访问控制 deny 的 IP 地址访问时会返回403状态码。
Nginx 的 basic 身份认证指令auth_basic
和auth_basic_user_file
由模块ngx_http_auth_basic_module
提供。可配置在 http、server、location 块中
auth_basic 描述 | off; 是否开启http_basic对用户认证 |
可通过 Apache 的工具htpasswd
创建用户认证文件
htpasswd -c -m -b /etc/nginx/secret mike 123456
使用 SSL 将网站配置为 HTTPS
注:如果是编译安装,需要在构建时添加--with_http_ssl_module
支持 SSL。
首先进入目录/etc/pki/CA/private
下创建服务器 RSA 私钥,叫server.key
。
openssl genrsa -out server.key 2048
生成服务器的 CSR 证书请求文件。CSR 证书请求文件是服务器的公钥,用于提交给 CA 机构进行签名。
openssl req -new -key server.key -out server.csr
按要求填写信息即可
Country Name (2 letter code) [XX]:CN |
CSR 参数含义:
参数 | 说明 |
---|---|
Country Name | 符合 ISO 的两个字母的国家代码,中国 CN |
State or Province Name | 省份 |
Locality Name | 城市 |
Organization Name | 公司名 |
Organizational Unit Name | 组织名/部门名 |
Common Name | 使用 SSL 加密的域名,不能填错 |
Email Address | 邮箱,可省略 |
A challenge password | 有的 CA 机构需要此密码,通常省略即可 |
An optional company name | 可选的公司名,可省略 |
CA 为服务器认证证书
openssl x509 -req -days 30 -in server.csr -signkey server.key -out server.crt
在 CA 用私钥签名了证书后,该证书将用于浏览器验证请求的网站是否真实,防止网络通信过程中被篡改。
浏览器保存了受信任的 CA 机构的公钥,在请求 HTTPS 网站时,会利用 CA 公钥验证服务器证书,并检查域名是否吻合、证书是否过期等。
在 Nginx 配置文件中设置 https 配置。Nginx 配置文件中已默认存在 SSL 配置,只是注释了,取消注释,并修改 crt 文件路径即可。
server { |
在/etc/hosts
中确定配置了 system3 的条目。在浏览器上输入https://system3.example.com
会说明此连接不安全。是因为当前的证书是服务器自己作为 CA 签名的,所以浏览器无法信任。点添加例外,可查看该证书,与配置的一致。
添加安全例外后,即可访问网页
Nginx 日志
关于 Nginx 日志的指令如下:
log_format
:日志格式。格式:log_format 格式名 格式(由变量组成)
,默认格式如下:'$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'
$remote_addr
:客户端的地址。如果 Nginx 服务器在后端,则该变量只能获取到前端(代理或负载均衡服务器)的 IP 地址$remote_user
:远程客户端用户名称$time_local
:记录访问时间和时区信息$request
:记录用户访问时的 url 和 http 协议信息$status
:记录客户端请求时返回的状态码$body_bytes_sent
:记录服务器响应给客户端的主体大小$http_referer
:记录此次请求是从哪个链接过来的,需要模块ngx_http_referer_module
支持,默认已装,可以防止盗链问题$http_user_agent
:记录客户端的浏览器信息。如使用什么浏览器发起的请求,是否是压力测试工具在测试等$http_x_forward_for
:若要在$remote_addr
中获取的是真正客户端的 IP,则要添加此项,并且在前端的服务器要开启x_forward_for
access_log
:访问日志的路径和采用的日志格式名。格式:access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
。若不指定格式名,默认为 combined。buffer 为日志缓冲区的大小,默认 64K。flush 为日志刷盘时间,即日志保存在缓冲区的最大时间。gzip 为日志刷盘前是否压缩以及压缩级别。if 指定条件。open_log_file_cache
:设置日志文件缓存。默认关闭,即每一条日志都是打开文件,写入后关闭,这样会消耗一定量的 IO。而设置了缓存后,会等日志数量达到一个指标后一下写入日志文件。格式:
open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
。其中 max 为缓存中的最大文件描述符数量。inactive 为存活时间,默认是 10s。min_uses 为在 inactive 时间段内,日志文件最少使用多少次后,该日志文件描述符记入缓存中,默认是 1 次。valid 为检查频率,默认 60s。log_subrequest
:是否在 access.log 中记录子请求的访问日志。默认不记录rewrite_log
:是否在 error.log 中记录 notice 级别的重写日志。默认关闭
日志切割
默认 Nginx 不会自动切割日志。
手动切割:
mv access.log access-XXXX.log |
自动切割:
创建 shell 脚本
#!/bin/bash |
添加定时任务
0 0 * * * /bin/sh XXX.sh &> /dev/null #每天晚上12点切割日志 |
Nginx 的其他常见 HTTP 功能
文件查找规则
Nginx 为了响应一个请求,它将请求传递给一个内容处理程序,由配置文件中的location
指令决定处理。
无条件内容处理程序首先被尝试 :perl
、proxy_pass
、flv
、mp4
等。如果这些处理程序都不匹配,那么 Nginx 会将该请求按顺序传递给下列操作之一,顺序依次是 :random index
、 index
、 autoindex
、gzip_static
、 static
。
处理以斜线结束请求的是 index 处理程序。如果 gzip 没有被激活,那么 static 模块会处理该请求。
这些模块如何在文件系统上找到适当的文件或者目录则由某些指令组合来决定。**root
指令最好定义在一个默认的 server
指令内 , 或者至少在一个特定的 location
指令之外定义,以便它的有效界限为整个 server。**
文件路径指令:
指令 | 说明 |
---|---|
root | 设置文档根目录,URI 后设置,可在文件系统中找到具体文件 |
try_files | 测试文件的存在性,若前面的文件没找到,则最后的条目作为备用,需要确保最后一个路径或 location 存在 |
例:
location / { |
域名解析
指令 | 说明 |
---|---|
resolver | 设置一个或多个域名解析服务器。可选 valid 参数,会覆盖域名记录的 TTL |
resolver_timeout | 当解析服务器不可达时的等待时间,尽量设置的小。目前只适用于商业订阅 |
该指令需要配置在 server 块中
server { |
客户端交互
Nginx 与客户端交互的方式有多种,这些方式可以从连接本身 (IP 地址、超时、存活时间等)到内容协商头的属性。
指令 | 说明 |
---|---|
default_type | 设置响应的默认 MIME 类型。只有文件的 MIME 类型不匹配任何 types,才会用此项 |
types | 设置 MIME 类型到文件扩展名的映射,一般不用设置。只需要载入conf/mime.types 即可 |
ignore_invalid_headers | 禁止忽略无效名字的头。一个有效的名字由 ASCII 字母、数字、连字符号组成 |
recursie_error_pages | 启用 error_page 实现多个重定向 |
Nginx 缓存
Nginx 的模块ngx_http_proxy_module
自带缓存功能,Nginx 可实现几种缓存:网页内容缓存,日志缓存,打开文件缓存,fastcgi 缓存。Nginx 的缓存功能主要由proxy_chache
相关的命令集和fastcgi_cache
相关命令集构成,前者用于反向代理,后者用于对 FastCGI 缓存。
需要下载第三方的 Nginx 模块ngx_cache_purge
,用于清除指定的 URL 缓存,下载地址
下载后解压,然后重新编译 Nginx,通过nginx -V
查看编译选项,复制过去然后加上解压后的模块目录(不是里面的.c 文件)
./configure |
编译后能看到新的模块添加成功的信息
configuring additional modules |
然后只要make
就行,不能make install
,不然就是重新安装 Nginx 了
最后还要替换 nginx 启动文件,在重新编译后,nginx 的下载目录中有个objs
目录,将里面的nginx
启动脚本复制到/usr/sbin/
下,替换原来的nginx
rm -f /usr/sbin/nginx |
通过nginx -V
查看,--add-module=/root/ngx_cache_purge-2.3
已成功编译
Nginx 自带的缓存相关指令有三个:
proxy_cache_path
:配置缓存存放路径。常用格式:proxy_cache_path path [levels=levels] keys_zone=name:size [max_size=size] [inactive=time];
。levels
:缓存目录级别以及缓存目录名的字符数,数字间用冒号分隔。最多可有三层目录,目录名最多两个字符。例:levels=1:2:2
表示一共三层目录,第一层目录名 1 字符,第二和第三层目录名为 2 字符。keys_zone
:缓存标识名和内存中缓存的最大空间。名字必须唯一。可在keys_zone
的值后加上:内存缓存空间
指定内存中的缓存空间大小max_size
:磁盘中缓存目录的最大空间inactive
:缓存的默认时长,到达指定时间却没被访问的缓存会被自动删除
proxy_cache
:指定要使用的缓存方法,使用proxy_cache_path
中keys_zone
指定的名称。格式:proxy_cache 缓存标识名;
proxy_cache_valid
:根据状态码指定缓存有效期。格式:proxy_cache_valid 状态码(可指定多个,或直接指定any) 时间(支持m|h等时间单位);
在配置文件添加缓存配置。首先在 http 块下(server 块外)添加缓存路径,路径可根据需要自定义
proxy_temp_path /data/ngxcache/proxy_temp_dir; |
Nginx 启动后,缓存加载程序只进行加载一次,加载时会将缓存的元数据加载到共享内存区域,但是如果一次加载整个缓存全部内容可能会使 Nginx 刚启动的前几分钟性能消耗严重,大幅度降低 Nginx 的性能。所以可以在 proxy_cache_path 命令中配置缓存迭代加载。缓存迭代加载一共可以设置三个参数:
loader_threshold - 迭代的持续时间,以毫秒为单位(默认为200) |
例:proxy_cache_path /data/ngxcache/proxy_cache_dir keys_zone=cache_one:10m loader_threshold=300 loader_files=200;
proxy_cache_methods[GET HEAD POST]; |
在虚拟服务器下配置proxy_cache_methods
命令可以指定该虚拟服务器下什么类型的 HTTP 方法可以被缓存。默认情况下 GET 请求及 HEAD 请求会被缓存,而 POST 请求不会被缓存。
proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment; |
proxy_cache_bypass
命令可以配置不会向客户端响应缓存,而是直接将请求转发给后端服务进行请求数据。可以通过上述命令配置需要绕过缓存的请求 URL,也就是说 URL 中包含该配置的值,则这次请求会直接跳过缓存直接请求后端服务去获取数据。
proxy_cache_min_uses 2; |
proxy_cache_min_uses
命令可以设置当某请求最少响应几次后会被缓存。若设置为 2 则表示每个请求最少被请求 2 次后会加入到缓存中。
例:简单添加 location 缓存配置
location / { |
当开启缓存后,Nginx 会生成进程cache_manager
对缓存进行管理。
nginx 1359 1356 0 11:23 ? 00:00:00 nginx: cache manager process |
可在server
块中添加add_header X-Cache $upstream_cache_status;
,该参数用于显示缓存状态返回值,一共有七种:
返回值 | 说明 |
---|---|
HIT | 缓存 |
MISS | 未命中,请求被传送到后端 |
EXPIRED | 缓存已过期,请求被传到后端 |
UPDATING | 正在更新缓存,将使用旧的应答 |
STALE | 无法从后端服务器更新缓存时,返回了旧的缓存 |
BYPASS | 缓存被绕过了 |
REVALIDATED | 在启用 proxy_cache_revalidate 后,当缓存内容过期后时,Nginx 通过一次 If-Modified-Since 的请求头去验证缓存内容是否过期,此时会返回该状态 |
add_header X-Cache "$upstream_cache_status from $server_addr"; |
完整的配置:
proxy_temp_path /data/ngxcache/proxy_temp_dir; |
通过浏览器访问http://web
。如果是第一次访问,会发现状态是 MISS
若再次访问,则状态会变为 HIT。
并且可以看到缓存目录下已建立缓存
/data/ngxcache/proxy_cache_dir/ |
另外常用的三种缓存:
open_log_cache
:日志缓存open_file_cache
:文件缓存fastcgi_cache
:fastcgi 缓存
清除缓存
如果缓存过期则需要从缓存中删除过期的缓存文件,防止新旧缓存出现交错出错,当 Nginx 接收到自定义 HTTP 头或者 PURGE 请求时,缓存将会被清除。
在 HTTP 节点下创建一个新变量$purge_method 来标识使用 PURGE 方法的请求并删除匹配的 URL。
http { |
进入虚拟服务器配置,在 location 中配置高速缓存,并且指定缓存清除请求命令proxy_cache_purge
。
server { |
发送清除命令
配置proxy_cache_purge
指令后需要发送 PURGE 请求来清除缓存。例如使用 PURGE 方式请求 url:
PURGE web/app |
则 app 对应的缓存中的数据将被删除。但是,这些高速缓存数据不会从缓存中完全删除,它们将保留在磁盘上,直到它们被删除为非活动状态,或由缓存清除进程处理。
限制 IP 访问清除命令:清除缓存这种命令一般需要权限才可进行操作,所以一般需要配置允许发送缓存清除请求的 IP 地址:
geo $purge_allowed { |
当 Nginx 接收到清除缓存请求时,Nginx 检查客户端 IP 地址,若 IP 地址已经获得清除缓存权限,则$purge_method设置为$purge_allowed,值为 1 表示允许清除缓存,值为 0 表示表示 IP 地址未获得权限。
从缓存中完全删除文件:高速缓存数据不会从缓存中完全删除,它们将保留在磁盘上,直到它们被删除为非活动状态,或由缓存清除进程处理。要完全删除与 getArticle 相匹配的缓存数据,需要在proxy_cache_path
添加参数 purger,该参数表示永久的遍历所有缓存条目,并删除与通配符相匹配的条目。
proxy_cache_path /data/ngx_cache/proxy_cache_dir keys_zone=cache_one:10m purger=on; |
Nginx 负载均衡
Nginx 提供负载均衡模块ngx_http_upstream_module
,且 Nginx 有四种典型的负载均衡配置方式。
配置方式 | 说明 |
---|---|
轮询 round-robin | 默认配置方式,每个请求按照时间顺序逐一分配到不同后端服务器 |
权重 | 利用 weight 指定轮询的权重比率,weight 与访问比率成正比。用于后端服务器性能不均的情况 |
ip_hash | 每个请求按访问 IP 的 hash 结果分配,使每个访客固定访问一个后端服务器,可解决 Session 问题 |
fair(第三方) | 按后端服务器响应时间来分配请求,响应时间短的优先 |
url_hash(第三方) | 按访问 URL 的哈希结果来分配请求,使每个 URL 固定访问一个后端服务器。适用于后端服务器做缓存的情况 |
一致性 Hash(Tengine) | 将每个服务器虚拟成 N 个节点,均匀分布在哈希环上,每次请求时会根据配置参数计算出一个 Hash,在哈希环上查找离这个哈希值最近的虚拟节点,对应服务器作为该次请求的后端服务器。好处:若增加或减少了机器,对整个集群的影响会最小 |
Nginx 作负载均衡的优点:
- 配置简单
- 成本低
- 支持 Rewrite 重写规则
- 有内置的健康检查功能
- 节省带宽:支持 Gzip,可添加浏览器本地缓存的 Header
- 稳定性高:高并发的情况下也基本不会宕机
轮询
实验环境:
- 负载均衡器:172.16.246.133
- 后端服务器 1:172.16.246.134
- 后端服务器 2:172.16.246.135
仅需在负载均衡服务器上配置
server{ |
在本机上进行访问
> curl sys1.example.com |
权重
仍在负载均衡器上配置,仅修改 upstream 块
upstream sys1.example.com { |
参数 | 说明 |
---|---|
weight | 权重 |
max_fails | 允许请求失败次数,默认为 1。当超过最大次数时,返回 proxy_next_upstream 指令定义的错误 |
fail_timeout | 在经历 max_fails 次失败后,暂停服务的时间 |
backup | 预备备份机器,当所有其他主机都 down 后,就会启用该主机 |
down | 指定的 server 暂不参与负载均衡 |
查看现象
> curl sys1.example.com # 一共四次访问,访问web1和web2的比例正好为1:3 |
ip_hash
upstream sys1.example.com { |
测试:
> curl sys1.example.com |
每个 IP 地址绑定一个 web 服务器,可能会导致某些 web 服务器负载很大,有的很少,反而无法保证负载均衡。
upstream 块补充指令:
keepalive
:指定每个 worker 进程缓存到上游服务器的连接数。需要先将proxy_http_version
设为 1.1,proxy_set_header
设为Connection ""
upstream apache {
server 127.0.0.1:8080;
keepalive 32;
}
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://apache;
}least_conn
:直接激活最少连接算法,将请求发送至活跃连接数最少的服务器。
Nginx 反向代理
在模块ngx_http_proxy_module
中与 Nginx 反向代理有关的指令:proxy_pass
,用于设置后端服务器的地址,仅用于location
块。可以设置代理服务器的协议和地址以及映射位置的可选 URI。
实验环境:
- 代理服务器:172.16.246.133
- 后端服务器 1:172.16.246.134
- 后端服务器 2:172.16.246.135
只需要在代理服务器上配置即可
server{ |
然后在本机上配置 hosts 文件
172.16.246.133 web1 web2 |
确保代理服务器和后端服务器的端口都打开。然后在本机测试
> curl web1 |
如果代理的是动态内容服务器,要使用fastcgi_pass
指令进行代理
location ~* \.php$ { |
补充指令:
**
proxy_set_header
**:在将客户端请求转交给后端服务器前,更改请求头信息。默认只重新定义了两个字段proxy_set_header Host $proxy_host;
# 最好使用$host变量,它的值等于“Host”请求头字段中的服务器名称,如果此字段不存在则等于主服务器名称
proxy_set_header Host $host;
# 服务器名称可以与代理服务器的端口一起传递
proxy_set_header Host $host:$proxy_port;
# 请求host为代理主机
proxy_set_header Host $proxy_host;
# 添加客户端IP
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Connection close;若设置了
X-Real-IP
,且后端服务器是 httpd,则可以修改 httpd 配置文件中的LogFormat
,添加一个%{X-Real-IP}i
。而若后端是 Nginx,则不用修改了,因为 Nginx 的log_format
中已默认配置了$remote_addr
,即客户端 IP。如果启用了缓存,则标题字段为“If-Modified-Since”,“If-Unmodified-Since”,“If-None-Match”,“If-Match”,“Range”和“If-Range”来自原始请求不会传递给代理服务器。
**
proxy_connect_timeout
**:配置 Nginx 与后端代理服务器尝试连接的超时时间,默认 60s,且通常不会大于 75 秒。proxy_connect_timeout 30;
proxy_read_timeout
:配置 Nginx 向后端服务器组发出 read 请求后,等待响应的超时时间。仅在两个连续的读操作之间设置超时,而不是整个响应。如果代理服务器在此时间内未传输任何内容,则关闭连接。默认为 60 秒。proxy_read_timeout 15;
proxy_send_timeout
:配置 Nginx 向后端服务器组发出 write 请求后,等待响应的超时时间。仅在两个连续的写操作之间设置超时,而不是整个请求。如果代理服务器在此时间内未收到任何内容,则关闭连接。默认为 60 秒。proxy_send_timeout 15;
**
proxy_redirect
**:用于修改后端服务器返回的响应头中的 Location 和 Refreshproxy_redirect off;
proxy_buffer_size
:用于读取从代理服务器接收的响应的第一部分的缓冲区的大小,这部分通常包含一个小的响应头。默认情况下,缓冲区大小等于一个内存页面,默认为 4K 或 8K。proxy_buffer_size 4k;
**
proxy_buffers
**:用于从代理服务器读取响应的缓冲区的数量(number)和大小(size),用于单个连接。默认情况下,缓冲区大小等于一个内存页面。默认为 4K 或 8K。语法:proxy_buffers number size;
proxy_buffers 32 4k;
# 那么最多支持的并发活动连接数 = 分配给nginx的内存量(字节)/ (number×size×1024)**
proxy_temp_file_write_size
**:当启用从代理服务器到临时文件的响应缓冲时,限制一次写入临时文件的数据大小。默认情况下,size 由proxy_buffer_size
和proxy_buffers
指令设置的两个缓冲区限制。默认为 8k 或 16k。proxy_temp_file_write_size 64k;
可以讲这些代理指令单独存放为一个配置文件,然后在主配置文件的代理块中include
应用。
非 HTTP 型上游服务器
Memcached 服务器
Nginx 提供 memcached 模块并默认开启,用于与 memcached 守护进程通信,此时 Nginx 并不是充当反向代理,而是使 Nginx 使用 memcached 协议会话,因此 key 的查询能够在请求传递到应用服务器之前完成。
upstream memcaches { |
memcached_pass
将使用$memcached_key
变量实现 key 查找。
FastCGI 服务器
fastcgi 模块也是默认开启。开启后 Nginx 可使用 FastCGI 协议与多个上游服务器会话。
upstream fastcgis { |
SCGI 与 uWSGI 服务器与 FastCGI 类似,分别使用scgi_pass
与uwsgi_pass
指定上游服务器。
Nginx 邮件服务
Nginx 提供邮件代理服务,能代理 POP3、IMAP、SMTP。
每一个进入的请求都会基于相应的协议处理,对于每一个协议,Nginx 都需要一个用户名密码认证服务,如果认证成功,则连接被代理到邮件服务器。
Nginx 邮件服务,若是编译安装,则需要在构建时指定--with-mail
。
如果代理 pop3 服务,在配置文件中添加以下配置
mail { |
如果是代理 imap 服务,则同理,而可以不用指定protocol imap;
,因为 imap 是默认值。而端口号改为 143。
如果代理 smtp 服务,则修改protocol
后,再修改端口号为 25。
Nginx 还为三种服务提供了可选指令,能做到根据服务器的情况灵活代理。
最好在mail
块中,指定server_name
,为邮件代理提供统一入口。
重写与重定向
Nginx 提供模块ngx_http_rewrite_module
实现 URL 重写与重定向。rewrite
指令能用于server
、location
、if
块中。Nginx 的 rewrite 需要 pcre 的支持。
rewrite regex replacement [flag];
其中 regex 为正则表达式,replacement 为符合正则的替换算法。flag 为进一步处理的标识。以下为 flag 的可选值:
参数值 | 说明 |
---|---|
last | 终止 rewrite,继续匹配 |
break | 终止 rewrite,不再继续匹配 |
redirect | 临时重定向,返回的 HTTP 状态码为 302 |
permanent | 永久重定向,返回的 HTTP 状态码为 301 |
例:
rewrite ^/(.*) http://www1.example.com/$1 permanent; |
rewrite 有以下的作用:
- 使 URL 更加规范,方便开发
- 使搜索引擎收录网站内容
- 网站换域名后,让旧域名的访问跳转到新域名上
- 根据特殊变量、目录、客户端信息进行 URL 跳转
应用实验:使 www1.example.com 和 example.com 访问同一个地址
server { |
重写
两种匹配规则:
break
:本条规则匹配完成后,不再继续匹配后续任何规则last
:本条规则匹配完成后,继续向下匹配新的 location URI 规则
在 server 块中添加以下内容:
# !-e判断请求指定的资源是否不存在,若不存在就执行if块中的指令。$request_filename表示当前请求的文件路径 |
试验:
> curl http://web/asdasd |
若使用 last 标记:
常用的测试:
双目测试:
~
,!~
:正则匹配与不匹配(区分大小写)=
,!=
:精确匹配与不匹配~*
,!~*
:正则匹配与不匹配(不区分大小写)
使用案例:
if ($request_method="POST"){}
if ($request_uri ~* "/forum"){}单目测试:
-f
,!-f
:文件存在与不存在-d
,!-d
:目录存在与不存在-e
,!-e
:文件或目录存在与不存在-x
,!-x
:判断是否可执行与不可执行
Nginx 优化
安全优化
隐藏 Nginx 版本号
# curl -I localhost |
通过修改配置,在 http 块中添加
server_tokens off; |
再次访问
# curl -I localhost |
这样配置,在 nginx 的报错页面中也不会出现版本号。
文件解析漏洞
CRLF注入漏洞
目录遍历漏洞
属于配置不当的漏洞,错误的配置会使目录被遍历,导致源码或重要信息泄露。
解决:对某location路径添加autoindex off;
,默认就是off的。
服务器请求伪造漏洞
整数溢出漏洞
性能优化
nginx 单个进程允许的客户端最大连接数
worker_connections
为 nginx 单个进程允许的客户端最大连接数,这个连接数包含了所有连接,如代理连接、客户端连接等,需要根据服务器性能和内存消耗来指定,同时还与最大打开文件数(worker_rlimit_nofile)有关,在配置ulimit -HSn 65535
后,该配置才能生效。
该参数配置在event
块。
nginx 的总并发连接 = worker 数量 * worker_connections
worker 进程最大打开文件数
worker_rlimit_nofile
为 nginx worker 进程最大打开文件数,可设置为ulimit -n
的大小。配置在主标签段,不属于任何块。
优化服务器域名的散列表
散列表和监听端口关联,每个端口都最多关联到三张表:
- 确切名字的散列表
- 以星号起始的通配符名字的散列表
- 以星号结束的通配符名字的散列表
Nginx 会按以上的顺序依次查找域名,为了尽快找到域名,尽量使用确切名字。若定义了大量名字,或定义了非常长的名字,则需要在 HTTP 配置块中调整server_names_hash_max_size
和server_names_hash_bucket_size
的值。nginx 默认的值取决于 cpu 缓存行长度,可能是 32 或 64 或 128。配置在主标签段。
先尝试设置server_names_hash_max_size
,差不多为名字列表的名字总量,若不能解决问题再设置server_names_hash_bucket_size
。
server_names_hash_max_size
默认为 512kb,一般是 cpu L1 的 4-5 倍,不要加单位,默认单位为 kb。
nginx 连接参数优化
keepalive_timeout
设置连接会话保持时间,默认 65。client_header_timeout
设置读取客户端请求头数据的超时时间,若超过这个时间,客户端还没发送完整的 header 数据,nginx 会返回 408(request time out)错误,默认 60,推荐为 15,有单位。设置在 http 或 server 块。client_body_timeout
同上,是客户端请求体的超时时间,默认为 60s。设置在 http 或 server 块或 location 块。send_timeout
指定响应客户端的超时时间,仅限于两个连接活动之间的时间,若超过这个时间客户端没有活动,则 nginx 连接关闭,默认 60s,推荐 25s,有单位。设置在 http 或 server 块或 location 块。
上传文件大小限制
client_max_body_size
限制上传文件大小,具体根据业务调整,默认为 1m,推荐先设为 8m。设置在 http 或 server 块或 location 块。
FastCGI 优化
gzip 压缩
expires 缓存
放盗链
防爬虫
CDN 加速
Nginx 降权
Nginx 与 PHP
Nginx 自带的 FastCGI 模块,不仅能接受 php-fpm,还能与任何兼容 FastCGI 的服务器通信,该模块默认启用。
常用指令:
FastCGI 指令 | 说明 |
---|---|
fastcgi_buffer_size | 来自 FastCGI 服务器响应头的第一部分设置缓冲大小 |
fastcgi_buffers | 设置来自 FastCGI 服务器响应的缓冲数量和大小,用于单个连接 |
fastcgi_cache | 定义一个共享的内存 zone,用于缓存 |
fastcgi_no_cache | 指定一个或多个字符串,Nginx 不会将来自 FastCGI 服务器的响应保存在缓存中 |
fastcgi_keep_conn | 服务器不立即关闭连接以保持到 FastCGI 服务器的连接 |
fastcgi_pass | 指定 FastCGI 服务器如何传递请求,格式:address:port |
fastcgi_cache_path | 该指令用于设置存储缓存响应的目录和存储活跃的 key 和响应元数据的共享内存 zone |
fastcgi_connect_timeout | 指定 Nignx 将等待它接受连接的最长时间 |
fastcgi_hide_header | 指定不应该传递到客户端的头列表 |
Nginx 常见模块
gzip 压缩
gzip 模块默认启用。
http { |
LNMP 分布式集群方案
搭建 Nginx+PHP 环境
编译安装 PHP,php 版本 7.2.11。
确保zlib
及其库zlib-devel
,gd
及其库gd-devel
,curl
及其库libcurl
、libcurl-devel
,openssl
及其库openssl-devel
,libxml2
和libxml2-devel
(php 编译必须的依赖包),libjpeg
及其库libjpeg-devel
,libpng
及其库libpng-devel
,freetype-devel
./configure --prefix=/usr/local/php7.2 \ |
安装完成后,php-fpm 是无法通过systemctl
管理的。需要以下配置
php 配置文件目录
/usr/local/php7.2/etc/ |
需要将php-fpm.conf.default
改名为php-fpm.conf
。然后进入解压包的目录中sapi/fpm
目录,里面有一个php-fpm.service
,将该文件的权限改为754
,然后复制到/usr/lib/systemd/system/
中。
如果是 CentOS/Redhat 6 系列,则将init.d.php-fpm
复制到/etc/init.d/
中并改名为php-fpm
,添加执行权限,然后chkconfig --add php-fpm
。
查看php-fpm.service
,确保 PID 文件、配置文件路径正确。
[Unit] |
其中配置文件的路径为安装目录的etc/php-fpm.conf
,所以该配置文件原来的.default
文件一定要改名为php-fpm.conf
。并且还需要将php-fpm.d/
下的www.conf.default
改名为www.conf
,否则还会报错。
/usr/local/php7.2/sbin/php-fpm -t
检查配置文件是否正确。
再启动 php-fpm,systemctl start php-fpm.service
ps -ef | grep php-fpm |
可以看出,php-fpm 监听了 9000 端口,主进程用户为 root,子进程用户为 nobody。
可以将bin
和sbin
目录添加到环境变量中,将配置文件软连接到/etc/php/
中,方便操作。
php-fpm 主配置文件php-fpm.conf
,该配置文件采用的是 INI 的格式
[global] # 全局配置 |
php-fpm 进程池配置文件www.conf
,采用的也是 INI 格式
[www] # 进程池配置 |
在 php 的解压包中,有两个关于php.ini
的配置文件:php.ini-production
和php.ini-development
,其中 production 适用于实际上线环境(安全性高),development 适合于开发环境(便于调试)。选择其中一个复制到 php 安装目录下lib/php/
中,并改名为php.ini
。
[PHP] # PHP核心配置 |
修改 Nginx 配置文件,取消以下行的注释:
location ~ \.php$ { |
查看/etc/nginx/fasstcgi_params
文件
# 请求字符串(/xxx.php?xxx=xxxx&xxx=xxxx) |
在index.php
中写入<? phpinfo(); ?>
。刷新配置,浏览器访问。
Nginx+Apache 动静分离
Nginx 提供外部访问,静态请求直接由 Nginx 处理,动态请求转交给 Apache 处理,实现动静分离。
首先要确保 Apache 已支持 PHP。确保 Apache 是--enable-so
的,会在 Apache 的bin/
下有一个apxs
命令,是 Apache 的一个扩展工具(Apache extension tools),用于编译模块。php 的 configure 可用 apxs 编译用于 Apache 访问 PHP 的模块。
使用php -i | grep configure
查看 php 的编译参数
Configure Command => './configure' '--prefix=/usr/local/php7.2' '--enable-fpm' '--with-zlib' '--enable-zip' '--enable-mbstring' '--with-mcrypt' '--with-mysql' '--with-mysqli' '--with-pdo-mysql' '--with-gd' '--with-jpeg-dir' '--with-png-dir' '--with-freetype-dir' '--with-curl' '--with-openssl' '--with-mhash' '--enable-bcmath' '--enable-opcache' |
重新整理并添加--with-apxs2=/usr/local/httpd-2.4/bin/apxs
。
注:如果此时直接编译仍然会报错,错误来自于apxs
命令。报错信息如下:
./configure: /usr/local/httpd-2.4/bin/apxs: /replace/with/path/to/perl/interpreter: |
查看apxs
命令。第一行是#!/replace/with/path/to/perl/interpreter -w
肯定是不存在的,需要替换为#!/usr/bin/perl -w
。重试./configure
,然后make
(不要make install
)。
修改 httpd 的配置文件,以下为要修改的内容
Listen 81 # 防止与Nginx冲突,要修改端口号 |
修改 Nginx 配置文件,将以下行取消注释并修改
location ~ \.php$ { |
参考文章
- nginx 基础及提供 web 服务(nginx.conf 详解)
- nginx 日志配置
- Nginx 缓存原理及机制
- Nginx 高性能 Web 服务器实战教程
- 高性能网站构建实战
- 跟老男孩学 Linux 运维 Web 集群实战
- 精通 Nginx(第二版)