Nginx

Nginx 简介

Nginx(发音:engine X)是一款轻量级的 HTTP 服务器(相比于Apache、Lighttpd而言),同时是一个高性能的 HTTP 和反向代理服务器。

NGINX is a free, open-source, high-performance HTTP server and reverse proxy, as well as an IMAP/POP3 proxy server. NGINX is known for its high performance, stability, rich feature set, simple configuration, and low resource consumption.

-Nginx Official

Nginx 特点

Nginx 特点
- 热部署
- 可以高并发连接
- 低的内存消耗
- 处理响应请求很快
- 具有很高的可靠性

Nginx 作为 HTTP 服务器的几项基本特性:
- 处理静态文件,索引文件以及自动索引;打开文件描述符缓冲
- 无缓存的反向代理加速,简单的负载均衡和容错
- FastCGI,简单的负载均衡和容错
- 模块化的结构,包括 gzipping, byte ranges, chunked responses, 以及 SSI-filter 等 filter。
- 支持 SSL 和 TLSSNI.

使用场景

使用场景
- HTTP 代理服务
- 负载均衡
- 基于 Nginx 的安全防护
- Nginx Lua 开发
- 资源缓存
- 静态资源 Web 服务
- 动静分离
- 访问认证
- HTTPS 服务
- 灰度发布测试

Nginx 主要功能

Nginx 反向代理


正向代理与反向代理



Reverse Proxy

Nginx 反向代理


Understanding Nginx HTTP Proxying, Load Balancing, Buffering, and Caching
NGINX REVERSE PROXY
WebSocket proxying

反向代理的注意点
  • 请求 Header 中所有的空的字段不会被传递;
  • Nginx 在默认配置下不会传递 Header 中带下划线的字段,需要进行配置后才支持 underscores_in_headers on;
  • Header 的 “Host” 字段会被重写成 $proxy_host



How To Set Up Nginx Load Balancing with SSL Termination

反向代理的好处
  • 统一的应用入口。可以简化登陆控制、可以使用同一个 SSL 证书(SSL Termination)来避免多证书产生的问题。
  • 可以构建私有网络,来保证内部数据访问安全。
  • 便于基础设施的弹性伸缩和透明维护。通过 Nginx 来切割流量的方向,来实现扩容和维护。
  • URL 重写、压缩、缓存等。


Nginx 负载均衡


Nginx 负载均衡



5 Common Server Setups For Your Web Application
Using nginx as HTTP load balancer

负载均衡的方式,主要分三种:
1.循环(round-robin) - 请求通过循环的方式分配给各个服务器。这种当时可以给每个服务器加上不同的权重。
2.最少连接数(least-connected) - 下一个请求将分配给当前激活连接数的最少服务器。在请求耗时较长时起到负载均衡的作用。
3.IP哈希(ip-hash) - 根据当前请求的IP,进行哈希运算后,分配到某个服务器上。前两种负载均衡的方案不能实现来自同一个 IP 的请求都发到同一台服务器上,IP哈希的方式就可以实现。

如何使用 Nginx

安装并启动 Nginx

Mac

安装
1
2
# 先执行brew update保持软件依赖包都是最新的
brew update && brew install nginx
运行

用浏览器打开 http://localhost:8080 看到 Nginx 的欢迎信息。

修改默认端口

1
2
3
4
5
# 下面的1.8.0请根据最新安装版本号对应修改
sudo chown root:wheel /usr/local/Cellar/nginx/1.8.0/bin/nginx
sudo chmod u+s /usr/local/Cellar/nginx/1.8.0/bin/nginx
# 用vi编辑器打开nginx配置文件,找到server字段的listen字段并将其值修改为80
vi /usr/local/etc/nginx/nginx.conf


检查配置文件语法是否有误并且重新加载配置:

1
nginx -t && nginx -s reload


Nginx 基本使用

Nginx 相关指令
启动:sudo systemctl start nginx
重启:sudo systemctl restart nginx
重载:sudo systemctl reload nginx
停止:sudo systemctl stop nginx
设置开机启动:sudo systemctl enable nginx

Nginx 命令帮助

终端输入 nginx -h 查看。

Nginx 配置

nginx documentation
nginx服务器安装及配置文件详解
nginx做负载均衡器以及proxy缓存配置

查看默认配置文件

1
cat /usr/local/etc/nginx/nginx.conf.default
一份不错的注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# user字段表明了Nginx服务是由哪个用户哪个群组来负责维护进程的,默认是nobody
# 我这里用了cainengtian用户,staff组来启动并维护进程
# 查看当前用户命令: whoami
# 查看当前用户所属组命令: groups ,当前用户可能有多个所属组,选第一个即可
user cainengtian staff;
# worker_processes字段表示Nginx服务占用的内核数量
# 为了充分利用服务器性能你可以直接写你本机最高内核
# 查看本机最高内核数量命令: sysctl -n hw.ncpu
worker_processes 4;
# error_log字段表示Nginx错误日志记录的位置
# 模式选择:debug/info/notice/warn/error/crit
# 上面模式从左到右记录的信息从最详细到最少
error_log /usr/local/var/logs/nginx/error.log debug;
# Nginx执行的进程id,默认配置文件是注释了
# 如果上面worker_processes的数量大于1那Nginx就会启动多个进程
# 而发信号的时候需要知道要向哪个进程发信息,不同进程有不同的pid,所以写进文件发信号比较简单
# 你只需要手动创建,比如我下面的位置: touch /usr/local/var/run/nginx.pid
pid /usr/local/var/run/nginx.pid;
events {
# 每一个worker进程能并发处理的最大连接数
# 当作为反向代理服务器,计算公式为: `worker_processes * worker_connections / 4`
# 当作为HTTP服务器时,公式是除以2
worker_connections 2048;
}
http {
# 关闭错误页面的nginx版本数字,提高安全性
server_tokens off;
include mime.types;
default_type application/octet-stream;
# 日志记录格式,如果关闭了access_log可以注释掉这段
#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可以让读取磁盘IO操作更快
# 当然如果你在学习的过程中可以打开方便查看Nginx的访问日志
access_log off;
sendfile on;
# 在一个数据包里发送所有头文件,而不是一个接一个的发送
tcp_nopush on;
# 不要缓存
tcp_nodelay on;
keepalive_timeout 65;
gzip on;
client_max_body_size 10m;
client_body_buffer_size 128k;
# 关于下面这段在后面紧接着来谈!
include /usr/local/etc/nginx/sites-enabled/*;
}


Nginx 配置最佳实践

Nginx 配置最佳实践
上面的配置文件最后一行 include 关键词会将 /usr/local/etc/nginx/sites-enabled/ 文件夹下面的所有文件都加载进当前的配置文件,这样子就可以将配置文件分离,nginx.conf 这个配置文件修改之后以后基本不会修改,配置不同站点的时候只需要在 /usr/local/etc/nginx/sites-enabled/ 不断增加新的文件即可,这是比较好的配置方式。

比如我在 /usr/local/etc/nginx/sites-enabled/ 下面增加了两个文件,用来配置普通的 HTTP 服务还有 HTTPS 服务:

1
2
touch /usr/local/etc/nginx/sites-enabled/default
touch /usr/local/etc/nginx/sites-enabled/default-ssl


default 配置解析

default 配置解析

Nginx 整个配置的结构大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
...
events {
...
}
http {
...
server {
...
location xxx {
...
}
}
}


对比上面我的 nginx.conf 文件可以知道 default 文件的内容就是配置 server 部分的,下面先弄一份最基本的配置(带有详细说明):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
server {
# Nginx监听端口号
listen 80;
# 服务器的名字,默认为localhost,你也可以写成aotu.jd.com,这样子就可以通过aotu.jd.com来访问
server_name localhost;
# 代码放置的根目录
root /var/www/;
# 编码
charset utf-8;
location / {
# index字段声明了解析的后缀名的先后顺序
# 下面匹配到/的时候默认找后缀名为php的文件,找不到再找html,再找不到就找htm
index index.php index.html index.htm;
# 自动索引
autoindex on;
# 这里引入了解析PHP的东西
include /usr/local/etc/nginx/conf.d/php-fpm;
}
# 404页面跳转到404.html,相对于上面的root目录
error_page 404 /404.html;
# 403页面跳转到403.html,相对于上面的root目录
error_page 403 /403.html;
# 50x页面跳转到50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}


上面的配置的意思就是:访问http://localhost『80端口号可以直接省略』的时候会在 /var/www/ 下面找 index.php 文件,如果没有找到就找 index.html,如果再没有找到那就找 index.htm,如果还是没有找到的话就 404 跳转到 404.html,如果你刚好将 /var/www/ 设置为 root 用户访问的话,那么就会直接无访问权限 403 跳转到 403.html

值得注意的是 server 字段里面的 root 字段,这个字段需要跟 alias 字段区分开来,通过下面两段配置解释一下:

1
2
3
4
5
6
7
8
9
10
11
12
# 当用root配置的时候,root后面指定的目录是上级目录
# 并且该上级目录必须含有和location后指定的名称的同名目录,否则404
# root末尾的"/"加不加无所谓
# 下面的配置如果访问站点http://localhost/test1访问的就是/var/www/test1目录下的站点信息
location /test1/ {
root /var/www/;
}
# 如果用alias配置,其后面跟的指定目录是准确的,并且末尾必须加"/",否则404
# 下面的配置如果访问站点http://localhost/test2访问的就是/var/www/目录下的站点信息
location /test2/ {
alias /var/www/;
}


配置反向代理

配置反向代理
例如我有一个 Node 服务的名字是 o2blog_wx,在启动 Node 的时候访问的地址是:http://localhost:3000/,但是对外网我们希望是:http://aotu.jd.com/o2blog_wx,接下来我们将通过 Nginx 进行配置(带有详细注释)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
listen 80;
server_name aotu.jd.com;
root /var/www/;
location /o2blog_wx/ {
# 反向代理我们通过proxy_pass字段来设置
# 也就是当访问http://aotu.jd.com/o2blog_wx的时候经过Nginx反向代理到服务器上的http://127.0.0.1:3000
# 同时由于解析到服务器上的时候o2blog_wx这个字段都要处理
# 所以通过rewrite字段来进行正则匹配替换
# 也就是http://aotu.jd.com/o2blog_wx/hello经过Nginx解析到服务器变成http://127.0.0.1:3000/hello
proxy_pass http://127.0.0.1:3000;
rewrite ^/o2blog_wx/(.*) /$1 break;
}
}


配置负载均衡


配置负载均衡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
http {
upstream myapp1 {
server srv1.example.com;
server srv2.example.com;
server srv3.example.com;
}

server {
listen 80;

location / {
proxy_pass http://myapp1;
}
}
}


负载均衡的权重
下面配置中,每 5 个请求会有三个被分配到 srv1:
1
2
3
4
5
upstream myapp1 {
server srv1.example.com weight=3;
server srv2.example.com;
server srv3.example.com;
}


配置临时跳转


配置临时跳转

有时候我们觉得一开始配置的 URL 不好想换掉,但又不想原先的链接失效,比如一开始对外网的链接是:http://aotu.jd.com/o2blog_wx/,后来想改成 http://aotu.jd.com/wxblog,又不想原先的失效。

这个时候可以在 Nginx 上配置一个 302 临时跳转,如下(server 部分跟前面的一样):

1
2
3
4
location /o2blog_wx/ {
# 当匹配到http://aotu.jd.com/o2blog_wx/的时候会跳转到http://aotu.jd.com/wxblog
return 302 http://aotu.jd.com/wxblog
}


配置限制访问


配置限制访问
在一台服务器上的资源不全部都是对外开放的,这个时候就需要通过 Nginx 配置一个限制访问,比如查看本服务器的PHP信息,我们就可以通过下面配置来实现限制访问:

1
2
3
4
5
6
7
# 当匹配到/info的时候只允许10.7.101.224访问,其它的全部限制
# 同时改写为/info.php
location = /info {
allow 10.7.101.224;
deny all;
rewrite (.*) /info.php
}


这个时候只有 IP10.7.101.224 的机器才可以访问:http://aotu.jd.com/info,其它机器都会 403 拒绝访问!

当然最佳的实践是将 IP 抽取出来变成白名单,这样子就可以实现部分 IP 可以访问,其它的不能访问。

default-ssl 配置解析

default-ssl 配置解析
我们都知道 HTTP 在传输的过程中都是明文的,这直接导致了在传输的任何一个过程中都容易被窃取信息,所以才有了 SSL(安全套接层)以及升级版 TLS(传输层安全协议)的出现,其实就是在 HTTP 应用层给 TCP/IP 传输层的中间增加了 TLS/SSL 层,统称为 HTTPS

那如何通过 Nginx 配置 HTTPS 站点呢,下面就是 default-ssl 配置文件的内容(详细解析):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
server {
# 默认情况下HTTPS监听443端口
listen 443 ssl;
server_name localhost;
root /var/www/;
# 下面这些都是配置SSL需要的
ssl on;
# 下面两个字段需要的crt利用openssl生成,具体可以看[这里](http://nginx.org/en/docs/http/configuring_https_servers.html)
ssl_certificate ssl/localhost.crt;
ssl_certificate_key ssl/localhost.key;
ssl_session_timeout 10m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location = /info {
allow 127.0.0.1;
deny all;
rewrite (.*) /info.php;
}
location /phpmyadmin/ {
root /usr/local/share/phpmyadmin;
index index.php index.html index.htm;
}
location / {
include /usr/local/etc/nginx/conf.d/php-fpm;
}
error_page 403 /403.html;
error_page 404 /404.html;
}


上面配置之后,就可以通过 https://localhost 访问我们的 Nginx 首页了。

Nginx 处理请求过程

How nginx processes a request

Nginx 调试

A debugging log

Nginx 调试
如果是通过源代码手工安装的,需要重新安装,并在安装的过程加上参数:./configure --with-debug

如果是通过包的形式安装(sudo yum install nginx)的,可以通过命令切换到调试模式:
1. 停止 nginx 进程:sudo systemctl stop nginx
2. 在 /etc/nginx/nginx.cond 中配置 error_log 的配置级别为 debug:error_log /your/log/path/error.log debug
3. 开启调试模式 sudo systemctl start nginx-debug
4. 完成。所有的 debug 数据都会记录在 /your/log/path/error.log 中。

Nginx 的不足

相对于 Apache 的不足
1. apache 相对 nginx 更稳定
2. apache 模块丰富
3. apache rewrite 更能强大
4. 动态请求要 apache 去做,nginx 只适合静态和反向

如何选择
1. 要求稳定,选apache
2. 要求并发量选nginx
3. 单台服务器环境,动态请求多用apache
5. 可生产静态文件用nginx
6. 大型环境里,前端用nginx作为反向代理抗住压力,apache作为后端处理动态请求

相关链接

Inside NGINX: How We Designed for Performance & Scale
The Architecture of Open Source Applications (Volume 2): nginx
lua-nginx-module
前端工程师应该知道的Nginx
前端工程师学习Nginx入门篇