Nginx 是一个开源且高性能、可靠的 http 中间件,代理服务。Nginx(发音同 engine x)是一个 Web 服务器,也可以用作反向代理,负载平衡器和 HTTP 缓存。
nginx.conf:
# 全局配置
user nginx; # 配置用户或者组
worker_processes 1; # 允许生成的进程数
error_log /var/log/nginx/error.log warn; # 错误日志路径,warn 代表日志级别,级别越高记录越少
pid /var/run/nginx.pid; # Nginx 进程运行文件存放地址
events {
accept_mutex on; # 设置网路连接序列化,防止惊群现象发生
multi_accept on; # 设置一个进程是否同时接受多个网络连接
worker_connections 1024; # 每个进程的最大连接数,因此理论上每台 Nginx 服务器的最大连接数 = worker_processes * worker_connections
}
# HTTP 配置
http {
include /etc/nginx/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 /var/log/nginx/access.log main; # 访问日志路径
sendfile on; # 允许 sendfile 方式传输文件
keepalive_timeout 65; # 连接超时时间
server {
listen 80; # 监听端口
server_name localhost; # 监听地址
location / { # 请求的url过滤,正则匹配
root /usr/share/nginx/html; # 根目录
index index.html index.htm; # 默认页
}
}
}
Nginx 的 HTTP 配置
HTTP 配置是 Nginx 配置最关键,同时也是 Nginx 实用技巧中最常涉及的部分。Nginx 的 HTTP 配置主要分为三个层级的上下文:http — server — location。
http
http 主要存放协议级别的配置,包括常用的诸如文件类型、连接时限、日志存储以及数据格式等网络连接配置,这些配置对于所有的服务都是有效的。
server
server 是虚拟主机配置,主要存放服务级别的配置,包括服务地址和端口、编码格式以及服务默认的根目录和主页等。部分特殊的配置各级上下文都可以拥有,比如 charest (编码格式) access_log (访问日志)等,因此你可以单独指定该服务的访问日志,如果没有则默认向上继承。
location
location 是请求级别的配置,它通过 url 正则匹配来指定对某个请求的处理方式,主要包括代理配置、缓存配置等。location 配置的语法规则主要为:
# location [修饰符] 匹配模式 { ... }
location [=|~|~*|^~] pattern { ... }
1)没有任何修饰符时表示路径前缀匹配,下边这个例子,匹配 http://www.jd.com/test 和 http://www.jd.com/test/may。
server {
server_name www.jd.com;
location /test { }
}
2)= 表示路径精确匹配,下边这个例子,只匹配 http://www.jd.com/test。
server {
server_name www.jd.com;
location = /test { }
}
3)~ 表示正则匹配时要区分大小写,下边这个例子,匹配 http://www.jd.com/test,但不匹配 http://www.jd.com/TEST。
server {
server_name www.jd.com;
location ~ ^/test$ { }
}
4)~* 表示正则匹配时不需要区分大小写,下边这个例子,既匹配 http://www.jd.com/test,也匹配 http://www.jd.com/TEST。
server {
server_name www.jd.com;
location ~* ^/test$ { }
}
5)^~ 表示如果该符号后面的字符是最佳匹配,采用该规则,不再进行后续的查找。
Nginx location 有自己的一套匹配优先级:
- 先精确匹配 =
- 再前缀匹配 ^~
- 再按文件中顺序的正则匹配 ~ 或 ~*
- 最后匹配不带任何修饰的前缀匹配
下边这个例子,http://www.jd.com/test/may 虽然命中了两个 location 规则,但是由于 ^~ 匹配优先级高于 ~* 匹配,所以将优先使用第二个 location。
server {
server_name www.jd.com;
location ~* ^/test/may$ { }
location ^~ /test { }
}
Nginx 实用技巧
正向代理
客户端通过访问一个代理服务,由它将请求转发到目标服务,再接受目标服务的请求响应并最终返回给客户端,这就是一个代理的过程。“科学上网” 就是一种典型的正向代理,在这个过程中,Nginx 就充当了代理中介的角色。(不同客户端的请求代理到一个服务器)
修改 Nginx 的 location 配置,利用 proxy_pass 属性让主路径访问请求转发到目标服务 web1:
// ...
location / {
proxy_redirect off;
proxy_pass http://web1; ## 转发到web1
}
// ...
负载均衡
代理还包括反向代理,我们业务中最常提到的负载均衡,就是一种典型的反向代理。当网站的访问量达到一定程度后,单台服务器不能满足用户的请求时,就需要用多台服务器构建集群服务了,此时多台服务器将以合理的方式分担负载,避免出现某台服务器负载高宕机而某台服务器闲置的情况。
nginx.conf 中,我们创建了一个 upstream 配置 web-app,web-app 配置了三个目标服务,因此我们的请求将经由 web-app 代理到目标服务。Nginx 自带的负载均衡策略有多种,包括默认的轮询方式、权重方式、依据 IP 分配的 ip_hash 方式以及最少连接的 least_conn 方式等,采取哪种策略需要根据不同的业务和并发场景而定,这里我们使用 least_conn 策略来处理请求的分发。
// ...
upstream web-app {
least_conn; # 最少连接,选取活跃连接数与权重weight的比值最小者,为下一个处理请求的server
server web1 weight=10 max_fails=3 fail_timeout=30s;
server web2 weight=10 max_fails=3 fail_timeout=30s;
server web3 weight=10 max_fails=3 fail_timeout=30s;
}
server {
listen 80; # 监听端口
server_name localhost; # 监听地址
location / {
proxy_redirect off;
proxy_pass http://web-app; ## 转发到web-app
}
}
// ...
Server-side Include
Server-side Include(简称 SSI)是一种简单的解释型服务端脚本语言,是指在页面被获取时,服务器端能够进行 SSI 指令解析,对现有 HTML 页面增加动态生成的内容。SSI 是早期 Web 实现模块化的一个重要手段,适用于多种运行环境,且解析效率比 JSP 高,目前仍然在一些大型网站中广泛应用。
在 HTML 中使用 SSI 的格式就像这样:
<!--#include virtual="/global/foot.html"-->
一行注释,通过服务端的 SSI 解析,会被置换成 /global/foot.html 的内容,virtual 可以是绝对路径,也可以是相对路径。
在 nginx.conf 中简单配置以下两个属性,开启 Nginx 的 SSI 支持,其中 ssi_silent_errors 表示处理 SSI 文件出错时需要输出错误提示:
location / {
ssi on;
ssi_silent_errors on; # 处理 SSI 文件出错时输出错误提示,默认 off
proxy_redirect off;
proxy_pass http://web1; ## 转发到web1
}
GZIP 压缩
location / {
# ...
gzip on;
gzip_min_length 1k; # 大于1K的文件才会压缩
# ...
}
压缩后的请求响应头中多了 Content-Encoding: gzip。
防盗链
某些情况下我们不希望自己的资源文件被外部网站使用,比如有时候我会把 JD 图片服务上的图片链接直接复制到 GitHub 上使用,这个时候假如 JD 要禁用来自 GitHub 的图片访问,可以怎么做呢?很简单:
location ~* \.(gif|jpg|png|webp)$ {
valid_referers none blocked server_names jd.com *.jd.com;
if ($invalid_referer) {
return 403;
}
return 200 "get image success\n";
}
我们利用 Nginx 自带的 valid_referers 指令,对所有图片请求做了一个 referer 校验,只有 jd.com 及其子域下的图片请求才能成功,其他的都走 403 禁止,变量 $invalid_referer 的值正是校验结果。
HTTPS
HTTPS 大家都比较熟悉了,它是在 HTTP 基础上引入 SSL 层来建立安全通道,通过对传输内容进行加密以及身份验证,避免数据在传输过程中被中间人劫持、篡改或盗用的一种技术。Chrome 从 62 版本开始将带有输入数据的 HTTP 站点和以隐身模式查看的所有 HTTP 站点自动标记为 “不安全” 站点,可见在网络安全规范普及下,HTTPS 化是未来 Web 网站的一大趋势。
Nginx 可以简单快速地搭建起 HTTPS 服务,需要依赖于 http_ssl_module 模块。nginx -V 能够列出 Nginx 的编译参数,查看是否已安装 http_ssl_module 模块。
搭建 HTTPS 服务需要生成密钥和自签 SSL 证书(测试用,正式的需要签署第三方可信任的 SSL 证书),我们需要利用到 openssl 库。新建 nginx/ssl_cert 目录:
1)生成密钥 .key
openssl genrsa -out nginx_quick.key 1024
2)生成证书签名请求文件 .csr
openssl req -new -key nginx_quick.key -out nginx_quick.csr
3)生成证书签名文件 .crt
openssl x509 -req -days 3650 -in nginx_quick.csr -signkey nginx_quick.key -out nginx_quick.crt
完成这三步后,我们也就生成了 HTTPS 所需的密钥和 SSL 证书,直接配置到 nginx.conf 中:
# ...
server {
listen 443 ssl; # 监听端口
server_name localhost; # 监听地址
ssl_certificate /etc/nginx/ssl_cert/nginx_quick.crt;
ssl_certificate_key /etc/nginx/ssl_cert/nginx_quick.key;
# ...
}
页面缓存
我们常说的页面缓存主要分为三类:客户端缓存、代理缓存、服务端缓存,这里重点讨论的是代理缓存。
当 Nginx 做代理时,假如接收的大多是一些响应数据不怎么变化的请求,比如静态资源请求,使用 Nginx 缓存将大幅度提升请求速度。Nginx 中的缓存是以文件系统上的分层数据存储的形式实现的,缓存键可配置,并且可以使用不同的特定于请求的参数来控制进入缓存的内容。
Nginx 利用 proxy_cache_path 和 proxy_cache 来开启内容缓存,前者用来设置缓存的路径和配置,后者用来启用缓存:
http {
# ...
proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=mycache:10m max_size=10g inactive=60m;
server {
# ...
proxy_cache mycache;
# ...
}
}
上边我们设置了一个缓存 mycache,并在 server 中启用:
1)/data/nginx/cache 指定了本地缓存的根目录;
2)level 代表缓存目录结构是两层的,最多设置3层,数字代表命名长度,比如 1:2 就会生成诸如 /data/nginx/cache/w/0d 的目录,对于大量缓存场景,合理的分层缓存是必要的;
3)keys_zone 设置了一个共享内存区,10m 代表内存区的大小,该内存区用于存储缓存键和元数据,保证 Nginx 在不检索磁盘的情况下能够快速判断出缓存是否命中;
4)max_size 设置了缓存的上限,默认是不限制;
5)inactive 设置了缓存在未被访问时能够持续保留的最长时间,也就是失活时间。
林秀栋的技术博客