Nginx学习笔记

1-什么是Nginx?

Nginx (engine x) 是一款轻量级的 Web 服务器 、反向代理服务器及电子邮件(IMAP/POP3)代理服务器。

Nginx因为它的稳定性、丰富的模块库、灵活的配置和低系统资源的消耗而闻名.业界一致认为它是Apache2.2+mod_proxy_balancer的轻量级代替者,不仅是因为响应静态页面的速度非常快,而且它的模块数量达到Apache的近2/3。对proxy和rewrite模块的支持很彻底,还支持mod_fcgi、ssl、vhosts ,适合用来做mongrel clusters的前端HTTP响应。
Nginx和Apache一样使用模块化设计,nginx模块包括内置模块和第三方模块,其中内置模块中包含主模块和事件模块。

Nginx处理请求逻辑图

2-正向代理和反向代理

2.1-什么是正向代理?

正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。

  • 客户端请求目标服务器之间的代理服务器
  • 请求会先经过代理服务器,然后再转发请求到目标服务器,获得内容后,最后再响应给客户端

2.2-什么是反向代理?

反向代理,客户端是无感知代理的存在,以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端。

反向代理(Reverse Proxy)方式是指以代理服务器来接受 internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

作用:

  • 保证内网的安全,公网作为访问地址,web服务器谁内网;
  • 负载均衡

3-Nginx的作用

3.1-Nginx可以提供的服务

  • 正向代理

  • 反向代理

  • HTTP服务器

    Nginx本身也是一个静态资源的服务器,当只有静态资源的时候,就可以使用Nginx来做服务器,同时现在也很流行动静分离,就可以通过Nginx来实现,首先看看Nginx做静态资源服务器。

    现在很多项目流行前后分离,也就是前端服务器和后端服务器分离,分别部署,这样的方式能让前后端人员能各司其职,不需要互相依赖,而前后分离中,前端项目的运行是不需要用Tomcat、Apache等服务器环境的,因此可以直接用nginx来作为静态服务器。

  • 负载均衡

    负载均衡也是Nginx常用的一个功能,负载均衡其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。简单而言就是当有2台或以上服务器时,根据规则随机的将请求分发到指定的服务器上处理,负载均衡配置一般都需要同时配置反向代理,通过反向代理跳转到负载均衡

3.2-Nginx的优点

  1. 高并发。静态小文件
  2. 占用资源少。2万并发、10个线程,内存消耗几百M。
  3. 功能种类比较多。web,cache,proxy。每一个功能都不是特别强。
  4. 支持epoll模型,使得nginx可以支持高并发。
  5. nginx 配合动态服务和Apache有区别。(FASTCGI 接口)
  6. 利用nginx可以对IP限速,可以限制连接数。
  7. 配置简单,更灵活。

4-Nginx的安装与使用

4.1-Nginx的安装

详细安装方法请参考:

https://github.com/dunwu/nginx-tutorial/blob/master/docs/nginx-ops.md

4.2-Nginx的使用

nginx 的使用比较简单,就是几条命令。

常用到的命令如下:

nginx -s stop       快速关闭Nginx,可能不保存相关信息,并迅速终止web服务。
nginx -s quit 平稳关闭Nginx,保存相关信息,有安排的结束web服务。
nginx -s reload 因改变了Nginx相关配置,需要重新加载配置而重载。
nginx -s reopen 重新打开日志文件。
nginx -c filename 为 Nginx 指定一个配置文件,来代替缺省的。
nginx -t 不运行,而仅仅测试配置文件。nginx 将检查配置文件的语法的正确性,并尝试打开配置文件中所引用到的文件。
nginx -v 显示 nginx 的版本。
nginx -V 显示 nginx 的版本,编译器版本和配置参数。

如果不想每次都敲命令,可以在 nginx 安装目录下新添一个启动批处理文件startup.bat,双击即可运行。内容如下:

@echo off
rem 如果启动前已经启动nginx并记录下pid文件,会kill指定进程
nginx.exe -s stop

rem 测试配置文件语法正确性
nginx.exe -t -c conf/nginx.conf

rem 显示版本信息
nginx.exe -v

rem 按照指定配置去启动nginx
nginx.exe -c conf/nginx.conf

如果是运行在 Linux 下,写一个 shell 脚本,大同小异。

5-Nginx配置实战

5.1-http 反向代理配置

nginx.conf 配置文件如下:
注:conf / nginx.conf 是 nginx 的默认配置文件。你也可以使用 nginx -c 指定你的配置文件

#运行用户
#user somebody;

#启动进程,通常设置成和cpu的数量相等
worker_processes 1;

#全局错误日志
error_log D:/Tools/nginx-1.10.1/logs/error.log;
error_log D:/Tools/nginx-1.10.1/logs/notice.log notice;
error_log D:/Tools/nginx-1.10.1/logs/info.log info;

#PID文件,记录当前启动的nginx的进程ID
pid D:/Tools/nginx-1.10.1/logs/nginx.pid;

#工作模式及连接数上限
events {
use epoll; #默认使用epoll
worker_connections 1024; #单个后台worker process进程的最大并发链接数
}

#设定http服务器,利用它的反向代理功能提供负载均衡支持
http {
#设定mime类型(邮件支持类型),类型由mime.types文件定义
include D:/Tools/nginx-1.10.1/conf/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 D:/Tools/nginx-1.10.1/logs/access.log main;
rewrite_log on;

#sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,对于普通应用,
#必须设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,以平衡磁盘与网络I/O处理速度,降低系统的uptime.
sendfile on;
#tcp_nopush on;

#连接超时时间
keepalive_timeout 120;
tcp_nodelay on;

#gzip压缩开关
#gzip on;

#设定实际的服务器列表
upstream zp_server1{
server 127.0.0.1:8089;
}

#HTTP服务器
server {
#监听80端口,80端口是知名端口号,用于HTTP协议
listen 80;

#定义使用www.xx.com访问
server_name www.helloworld.com;

#首页
index index.html

#指向webapp的目录
root D:\01_Workspace\Project\github\zp\SpringNotes\spring-security\spring-shiro\src\main\webapp;

#编码格式
charset utf-8;

#代理配置参数
proxy_connect_timeout 180;
proxy_send_timeout 180;
proxy_read_timeout 180;
proxy_set_header Host $host;
proxy_set_header X-Forwarder-For $remote_addr;

#反向代理的路径(和upstream绑定),location 后面设置映射的路径
location / {
proxy_pass http://zp_server1;
}

#静态文件,nginx自己处理
location ~ ^/(images|javascript|js|css|flash|media|static)/ {
root D:\01_Workspace\Project\github\zp\SpringNotes\spring-security\spring-shiro\src\main\webapp\views;
#过期30天,静态文件不怎么更新,过期可以设大一点,如果频繁更新,则可以设置得小一点。
expires 30d;
}

#设定查看Nginx状态的地址
location /NginxStatus {
stub_status on;
access_log on;
auth_basic "NginxStatus";
auth_basic_user_file conf/htpasswd;
}

#禁止访问 .htxxx 文件
location ~ /\.ht {
deny all;
}

#错误处理页面(可选择性配置)
#error_page 404 /404.html;
#error_page 500 502 503 504 /50x.html;
#location = /50x.html {
# root html;
#}
}
}
  1. 启动 webapp,注意启动绑定的端口要和 nginx 中的 upstream 设置的端口保持一致。
  2. 更改 host:在 C:\Windows\System32\drivers\etc 目录下的 host 文件中添加一条 DNS 记录
127.0.0.1 www.helloworld.com
  1. 启动前文中 startup.bat 的命令
  2. 在浏览器中访问 www.helloworld.com,不出意外,已经可以访问了。

5.2-Nginx跨域解决方案

为什么会有跨域问题?

浏览器的同源策略拒绝了我们的请求。 所谓同源是指,域名,协议,端口相同,浏览器执行一个脚本时同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报上面的异常,提示拒绝访问。这是为了同一浏览器打开多个网站时,保护你的A网站登陆信息不被B网站拿去访问A网站,B网站登陆信息同理。

web 领域开发中,经常采用前后端分离模式。这种模式下,前端和后端分别是独立的 web 应用程序,例如:后端是 Java 程序,前端是 React 或 Vue 应用。如果项目没做前后端分离,是不会有跨域问题的。前后端分离的项目中,前端调用后台服务时,报错 No 'Access-Control-Allow-Origin' header is present on the requested resource,你就是遇到了跨域问题。

各自独立的 web app 在互相访问时,势必存在跨域问题。解决跨域问题一般有两种思路:

  1. CORS

    在后端服务器设置 HTTP 响应头,把你需要运行访问的域名加入加入 Access-Control-Allow-Origin中。

  2. jsonp

    把后端根据请求,构造 json 数据,并返回,前端用 jsonp 跨域。

需要说明的是,nginx 根据第一种思路,也提供了一种解决跨域的解决方案

举例:

www.helloworld.com 网站是由一个前端 app ,一个后端 app 组成的。前端端口号为 9000, 后端端口号为 8080。

前端和后端如果使用 http 进行交互时,请求会被拒绝,因为存在跨域问题。来看看,nginx 是怎么解决的吧:

location / {
# 检查域名后缀
if ($http_origin ~ \.test\.com) {
# 允许跨域请求的域,*代表所有,$http_origin动态获取请求客户端请求的域
add_header Access-Control-Allow-Origin $http_origin;
# 允许请求的方法,比如 GET/POST/PUT/DELETE
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
# 允许带上 cookie 请求
add_header Access-Control-Allow-Credentials true;
# 允许请求的 header
add_header Access-Control-Allow-Headers DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type;
# 预检命令的缓存,如果不缓存每次会发送两次请求
add_header Access-Control-Max-Age 1728000;
}
# options请求不转给后端,直接返回204
# 第二个if会导致上面的add_header无效,这是nginx的问题,这里直接重复执行下
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Headers DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type;
add_header Access-Control-Max-Age 1728000;
return 204;
}

# 其他请求代理到后端
proxy_set_header Host $host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://xxx.xxx.xxx.xxx;
}

1. Access-Control-Allow-Origin

服务器默认是不被允许跨域的。给Nginx服务器配置`Access-Control-Allow-Origin *`后,表示服务器可以接受所有的请求源(Origin),即接受所有跨域的请求。

2. Access-Control-Allow-Headers 是为了防止出现以下错误:

Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

这个错误表示当前请求Content-Type的值不被支持。其实是我们发起了”application/json”的类型请求导致的。这里涉及到一个概念:预检请求(preflight request),请看下面”预检请求”的介绍。

3. Access-Control-Allow-Methods 是为了防止出现以下错误:

Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

4.给OPTIONS 添加 204的返回,是为了处理在发送POST请求时Nginx依然拒绝访问的错误

发送”预检请求”时,需要用到方法 OPTIONS ,所以服务器需要允许该方法。

5.3-Nginx防盗链

防盗链的意义在于防止网站中的文件链接在其他网站中被使用,盗链的文件或图片在其他网站中加载,在这个过程中,实质上加载的请求是被盗链服务器上响应的,这就造成了一些不正常流量(并非自己网站的正常打开页面加载的处理请求)造成了消耗不必要的带宽。

要实现防盗链,需要了解HTTP协议中的请求头部的Referer头域和采用URL的格式表示访问当前网页或者文件的源地址。通过该头域的值,我们可以检测到访问目标资源的源地址。这样,如果我们检测到Referer(rui’fe~)头域中的值并不是自己站点内的URL,就采取组织措施,实现防盗链。

nginx防盗链使用到了valid_referers这个名单定义项(相当是定义白名单域名,如需定义多个域名使用空格分隔,非这里定义的域名会在转跳中返回403状态码),if中定义:非名单里里的域名可以定义返回403状态拒绝访问或返回一个盗链显示图片,如果使用盗链显示图片就定义rewrite跳转到那个图片的URL。

  • location中定义防止盗链的文件类型,以正则的语法进行匹配
  • expires、access_log定义了这些文件过期时间和日志不记录类型的配置,防盗链与静态文件过期时间和访问日志不记录可以在一个location中定义配置
  • rewrite设定指定转跳,设定指定转跳的URL地址,返回状态码302
  • return跟rewrite类似,只不过在最后处理请求时是直接拒绝掉这个盗链的请求,返回状态码403
server{
..
# 对源站点验证
valid_referers *.xxx.com;
# 非法引入会进入下方判断
if($invalid_referer) {
return 404;
}
...
}

5.4-负载均衡配置

反向代理的例子中,代理仅仅指向一个服务器。

但是,网站在实际运营过程中,多半都是有多台服务器运行着同样的 app,这时需要使用负载均衡来分流。

nginx 也可以实现简单的负载均衡功能。

假设这样一个应用场景:将应用部署在 192.168.1.11:80、192.168.1.12:80、192.168.1.13:80 三台 linux 环境的服务器上。网站域名叫 www.helloworld.com,公网 IP 为 192.168.1.11。在公网 IP 所在的服务器上部署 nginx,对所有请求做负载均衡处理。

nginx.conf 配置如下:

http {
#设定mime类型,类型由mime.type文件定义
include /etc/nginx/mime.types;
default_type application/octet-stream;
#设定日志格式
access_log /var/log/nginx/access.log;

#设定负载均衡的服务器列表
upstream load_balance_server {
#weigth参数表示权值,权值越高被分配到的几率越大
server 192.168.1.11:80 weight=5;
server 192.168.1.12:80 weight=1;
server 192.168.1.13:80 weight=6;
}

#HTTP服务器
server {
#侦听80端口
listen 80;

#定义使用www.xx.com访问
server_name www.helloworld.com;

#对所有请求进行负载均衡请求
location / {
root /root; #定义服务器的默认网站根目录位置
index index.html index.htm; #定义首页索引文件的名称
proxy_pass http://load_balance_server ;#请求转向load_balance_server 定义的服务器列表

#以下是一些反向代理的配置(可选择性配置)
#proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
#后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
proxy_set_header X-Forwarded-For $remote_addr;
proxy_connect_timeout 90; #nginx跟后端服务器连接超时时间(代理连接超时)
proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时)
proxy_read_timeout 90; #连接成功后,后端服务器响应时间(代理接收超时)
proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小
proxy_buffers 4 32k; #proxy_buffers缓冲区,网页平均在32k以下的话,这样设置
proxy_busy_buffers_size 64k; #高负荷下缓冲大小(proxy_buffers*2)
proxy_temp_file_write_size 64k; #设定缓存文件夹大小,大于这个值,将从upstream服务器传

client_max_body_size 10m; #允许客户端请求的最大单文件字节数
client_body_buffer_size 128k; #缓冲区代理缓冲用户端请求的最大字节数
}
}
}

Nginx的负载均衡策略

1. 轮循(round-robin)默认策略(RR-默认)

根据请求次数,将每个请求均匀分配到每台服务器,如果后端服务器宕机,自动剔除。

#动态服务器组
upstream load_balance_server {
#默认使用轮询策略
server 192.168.1.11:80;
server 192.168.1.12:80;
server 192.168.1.13:80;
}

最基本的配置方法,上面的例子就是轮询的方式,它是upstream模块默认的负载均衡默认策略。每个请求会按时间顺序逐一分配到不同的后端服务器。

有如下参数:

fail_timeout与max_fails结合使用。
max_fails设置在fail_timeout参数设置的时间内最大失败次数,如果在这个时间内,所有针对该服务器的请求都失败了,那么认为该服务器会被认为是停机了,
fail_time服务器会被认为停机的时间长度,默认为10s。
backup标记该服务器为备用服务器。当主服务器停止时,请求会被发送到它这里。
down标记服务器永久停机了。

注意:

  • 在轮询中,如果服务器down掉了,会自动剔除该服务器。
  • 缺省配置就是轮询策略。
  • 此策略适合服务器配置相当,无状态且短平快的服务使用。

2. 权重(Weight)

权重方式,在轮询策略的基础上指定轮询的几率,指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。

upstream load_balance_server {
#weigth参数表示权值,权值越高被分配到的几率越大
server 192.168.1.11:80 weight=5;
server 192.168.1.12:80 weight=1;
server 192.168.1.13:80 weight=6;
}

注意:

  • 权重越高分配到需要处理的请求越多。
  • 此策略可以与least_conn和ip_hash结合使用。
  • 此策略比较适合服务器的硬件配置差别比较大的情况。

3. ip_hash

在负载均衡系统中,假如用户在某台服务器上登录了,那么该用户第二次请求的时候,因为我们是负载均衡系统,每次请求都会重新定位到服务器集群中的某一个,那么已经登录某一个服务器的用户再重新定位到另一个服务器,其登录信息将会丢失,这样显然是不妥的
我们可以采用ip_hash指令解决这个问题,如果客户已经访问了某个服务器,当用户再次访问时,会将该请求通过哈希算法,自动定位到该服务器
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题

指定负载均衡器按照基于客户端IP的分配方式,这个方法确保了相同的客户端的请求一直发送到相同的服务器,以保证session会话。这样每个访客都固定访问一个后端服务器,可以解决session不能跨服务器的问题。

同一客户端的Web请求被分发到同一个后端服务器进行处理,使用该策略可以有效的避免用户Session失效的问题。该策略可以连续产生1045个互异的value,经过20次hash仍然找不到可用的机器时,算法会退化成轮循。

#动态服务器组
upstream load_balance_server {
ip_hash; #保证每个访客固定访问一个后端服务器
server localhost:8080 weight=2; #tomcat 7.0
server localhost:8081; #tomcat 8.0
server localhost:8082; #tomcat 8.5
server localhost:8083 max_fails=3 fail_timeout=20s; #tomcat 9.0
}

注意:

  • 在nginx版本1.3.1之前,不能在ip_hash中使用权重(weight)。
  • ip_hash不能与backup同时使用。
  • 此策略适合有状态服务,比如session。
  • 当有服务器需要剔除,必须手动down掉。

4. 最少连接(least_conn)

把请求转发给连接数较少的后端服务器。轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用的时间很长,会导致其所在的后端负载较高。这种情况下,least_conn这种方式就可以达到更好的负载均衡效果。

#动态服务器组
upstream load_balance_server {
least_conn; #把请求转发给连接数较少的后端服务器
server localhost:8080 weight=2; #tomcat 7.0
server localhost:8081; #tomcat 8.0
server localhost:8082 backup; #tomcat 8.5
server localhost:8083 max_fails=3 fail_timeout=20s; #tomcat 9.0
}

注意:

  • 此负载均衡策略适合请求处理时间长短不一造成服务器过载的情况。

5. 参数说明

  • weight

    启用权重策略,总数按照10进行计算,如果分配为3,则表示所有连接中的30%分配给该服务器,默认值为1;

  • max_fail/fail_timeout

    某台服务器允许请求失败的次数,超过最大数后,在fail_timeout时间内,新的请求不会分配给这台机器,等fail_timeout时间过去后, 会再尝试请求这台服务器,如此循环往复;如果设置为0,反向代理服务器则会将这台服务器设置为永久无效状态。max_fail默认为1,fail_timeout默认为10秒;

  • backup

    将某台服务器设定为备用机,当列表中的其他服务器都不可用时,启用备用机;

  • down

    将某台服务器设定为不可用状态;

  • max_conns

    限制分配给某台服务器的最大连接数,超过这个数量,反向代理服务器将不会分配新的连接,默认为0,表示不限制;

6. 第三方策略

扩展策略默认不被编译进nginx内核,如果启用该策略,需要自行编译安装,需要安装第三方插件。

  • fair

    按照服务器端的响应时间来分配请求,响应时间短的优先分配。

    根据后台服务器的响应时间判断负载情况,从中选出负载最轻的后端服务。但是在实际请款中,网络环境往往不那么简单,所以慎用。

    在编译安装后,如果需要启用该策略,需要在upstream标签中添加fair;,启用该策略后,加权轮循将失效。

    #动态服务器组
    upstream load_balance_server {
    server localhost:8080; #tomcat 7.0
    server localhost:8081; #tomcat 8.0
    server localhost:8082; #tomcat 8.5
    server localhost:8083; #tomcat 9.0
    fair; #实现响应时间短的优先分配
    }
  • url_hash

    按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,要配合缓存命中来使用。同一个资源多次请求,可能会到达不同的服务器上,导致不必要的多次下载,缓存命中率不高,以及一些资源时间的浪费。而使用url_hash,可以使得同一个url(也就是同一个资源请求)会到达同一台服务器,一旦缓存住了资源,再此收到请求,就可以从缓存中读取。 

    • 按照请求url的hash结果来分配请求,试每个url定向到同一个后端服务器,在1.7.2之后的nginx版本中,该模块应集成到内核中,不需要单独安装;
    • 启用该策略,需要在upstream标签中添加`hash $request_url;。
    #动态服务器组
    upstream load_balance_server {
    hash $request_uri; #实现每个url定向到同一个后端服务器
    server localhost:8080; #tomcat 7.0
    server localhost:8081; #tomcat 8.0
    server localhost:8082; #tomcat 8.5
    server localhost:8083; #tomcat 9.0
    }

5.5-网站有多个 webapp 的配置

当一个网站功能越来越丰富时,往往需要将一些功能相对独立的模块剥离出来,独立维护。这样的话,通常,会有多个 webapp。

举个例子:假如 www.helloworld.com 站点有好几个 webapp,finance(金融)、product(产品)、admin(用户中心)。访问这些应用的方式通过上下文(context)来进行区分:

www.helloworld.com/finance/

www.helloworld.com/product/

www.helloworld.com/admin/

我们知道,http 的默认端口号是 80,如果在一台服务器上同时启动这 3 个 webapp 应用,都用 80 端口,肯定是不成的。所以,这三个应用需要分别绑定不同的端口号。

那么,问题来了,用户在实际访问 www.helloworld.com 站点时,访问不同 webapp,总不会还带着对应的端口号去访问吧。所以,你再次需要用到反向代理来做处理。

配置也不难,来看看怎么做吧:

http {
#此处省略一些基本配置
upstream product_server{
server www.helloworld.com:8081;
}

upstream admin_server{
server www.helloworld.com:8082;
}

upstream finance_server{
server www.helloworld.com:8083;
}

server {
#此处省略一些基本配置
#默认指向product的server
location / {
proxy_pass http://product_server;
}

location /product/{
proxy_pass http://product_server;
}

location /admin/ {
proxy_pass http://admin_server;
}

location /finance/ {
proxy_pass http://finance_server;
}
}
}

5.6-https 反向代理配置

一些对安全性要求比较高的站点,可能会使用 HTTPS(一种使用 ssl 通信标准的安全 HTTP 协议)。

这里不科普 HTTP 协议和 SSL 标准。但是,使用 nginx 配置 https 需要知道几点:

  • HTTPS 的固定端口号是 443,不同于 HTTP 的 80 端口
  • SSL 标准需要引入安全证书,所以在 nginx.conf 中你需要指定证书和它对应的 key

其他和 http 反向代理基本一样,只是在 Server 部分配置有些不同。

首先要确定 Nginx有没安装了ssl 模块。

nginx -V
# 如未安装需进入Nginx 源码目录重新编译,在原命令基础上添加--with-http_ssl_module再进行编译安装
./configure --prefix=/usr/local/nginx ... --with-http_ssl_module
#HTTP服务器
server {
#监听443端口。443为知名端口号,主要用于HTTPS协议
listen 443 ssl;

#定义使用www.xx.com访问
server_name www.helloworld.com;

#ssl证书文件位置(常见证书文件格式为:crt/pem)
ssl_certificate cert.pem;
#ssl证书key位置
ssl_certificate_key cert.key;

#ssl配置参数(选择性配置)
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
#数字签名,此处使用MD5
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

location / {
root /root;
index index.html index.htm;
}
}

5.7-静态站点配置

有时候,我们需要配置静态站点(即 html 文件和一堆静态资源)。

举例来说:如果所有的静态资源都放在了 /app/dist 目录下,我们只需要在 nginx.conf 中指定首页以及这个站点的 host 即可。

配置如下:

worker_processes  1;

events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;

gzip on;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/javascript image/jpeg image/gif image/png;
gzip_vary on;

server {
listen 80;
server_name static.zp.cn;

location / {
root /app/dist;
index index.html;
#转发任何请求到 index.html
}
}
}

然后,添加 HOST:

127.0.0.1 static.zp.cn

此时,在本地浏览器访问 static.zp.cn ,就可以访问静态站点了。

5.8-搭建文件服务器

有时候,团队需要归档一些数据或资料,那么文件服务器必不可少。使用 Nginx 可以非常快速便捷的搭建一个简易的文件服务。

Nginx 中的配置要点:

  • 将 autoindex 开启可以显示目录,默认不开启。
  • 将 autoindex_exact_size 开启可以显示文件的大小。
  • 将 autoindex_localtime 开启可以显示文件的修改时间。
  • root 用来设置开放为文件服务的根路径。
  • charset 设置为 charset utf-8,gbk;,可以避免中文乱码问题(windows 服务器下设置后,依然乱码,本人暂时没有找到解决方法)。

一个最简化的配置如下:

autoindex on;# 显示目录
autoindex_exact_size on;# 显示文件大小
autoindex_localtime on;# 显示文件时间

server {
charset utf-8,gbk; # windows 服务器下设置后,依然乱码,暂时无解
listen 9050 default_server;
listen [::]:9050 default_server;
server_name _;
root /share/fs;
}

5.9-Nginx缓存配置

面对第一次客户端的应用Nginx需要从后端的服务获取数据,对于后续的请求,Nginx若进行了缓存就不再从后端服务获取数据。

1. proxy_cache_path

语法:proxy_cache_path path [levels=levels].只能用在http中。

指定缓存位置、缓存名称、内存中缓存内容元数据信息大小限制、缓存总大小限制。缓存位置是一个目录应该先创建好,nginx并不会帮我们创建这个缓存目录

proxy_cache_path /root/cache levels=1:2 keys_zone=zzm_cache:10m max_size=1g inactive=60m use_temp_path=off;
  • /root/cache:定义 proxy_cache 生成文件的根路径
  • levels:默认所有缓存文件都放在上面指定的根路径中,从而可能影响缓存的性能。推荐指定为 2 级目录来存储缓存文件
  • key_zone:这个的值是字符串,可以随意写。用于在共享内存中定义一块存储区域来存放缓存的 key 和 metadata(类似于使用次数),这样 nginx 可以快速判断一个 request 是否命中缓存。1m 可以存储 8000 个key,10m可以存在80000个key
  • max_size:最大 cache 空间。如果不指定,会使用掉所有 disk space。当达到 disk 上限后,会删除最少使用的 cache
  • inactive:内存中缓存的过期检查周期。示例配置中如果 1h 内都没有被访问,则不论状态是否为 expired,都会清除缓存。需要注意的是,inactive 和 expired 配置项的含义是不同的,expired 只是判断过期时间,不会删除缓存;而 inactive 是直接删除过期缓存
  • use_temp_path:如果为 off,则 nginx 会将缓存文件直接写入指定的 cache 文件中,而不使用 temp_path 指定的临时存储路径

2. proxy_cache

proxy_cache zone | off。默认是关闭的,可以用在http,server,location中。

指定使用前面设置的缓存名称

location / {
proxy_cache zzm_cache;
proxy_pass http://zzm;
proxy_cache_valid 200 304 12h;
proxy_cache_valid any 10m;
proxy_cache_key $host$uri$is_args$args;
add_header Nginx-Cache "$upstream_cache_status";
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
}
  • proxy_cache:对应 http 段的 key_zone,是你定义的 proxy_cache 所使用的共享空间的名称,我在1.1中定义的是zzm_cache,所以在这里也写的是zzm_cache。
  • proxy_cache_valid:对指定的 HTTP 状态进行缓存,并指定缓存时间。可以自定义写入多个配置项。这里我们对200和304的返回码缓存12小时。其余的缓存10分钟
  • proxy_cache_key:缓存的维度
  • is_args:如果$args设置,值为”?”,否则为””
  • proxy_next_upstream 当请求服务器发生错误或超时时,会尝试到下一台服务器

3. 静态资源缓存浏览器

expires 指令

location /static{
alias /xxx/yyy;
#expires 10s; # 缓存10s后过期
expires @22h30m; # 指定缓存过期时间
# expires -1h; # 缓存1小时前失效,即不缓存
# expires epoch; # 过期时间为1 January,1970,00:00:01 GMT,同样不进行缓存
# expires off; # 不设置,会使用浏览器默认值
# expires max; # 过期时间设置为31 December 2037 23:59:59 GMT,“Cache-Control”的值为10年
}

4. 服务器资源缓存到 Nginx端

# 设置缓存保存的目录
# keys_zone 设置共享内存及占用的空间大小,mycache为自定义名称,在下方需使用
# max_size 设置缓存总大小
# inactive 缓存超出所指定的时间进行清理
proxy_cache_path /usr/local/nginx/upstream_cache keys_zone=mycache:5m max_size=1g inactive=30m use_temp_path=off;
server {
...
# 开启并使用缓存,mycache为上方所指定的名称
proxy_cache mycache;
# 针对指定状态码的缓存过期时间
proxy_cache_valid 200 304 8h;
...
}

5.10-常见问题

  • 使用Nginx的反向代理,让同一个用户的请求一定转发到同一台服务器上,这种均衡策略会消耗更多的服务器资源,也增加了代理服务器的负担;
  • 使用其他策略作为负载均衡时,会出现用户Session丢失的情况,为避免出现这种情况,可以将用户的Session存放到缓存服务器中,比较常用的方案时redis/memchache;
  • 反向代理服务器也可以开启缓存服务,但是开启该项服务会增加代理服务器的负担,影响整体的负载均衡效率;
  • 使用Nginx反向代理布置负载均衡,操作相对简单,但是会有“单点故障”的问题,如果后台某台服务器宕机,会带来很多的麻烦,后期如果后台服务器继续增加,反向代理服务器会成为负载均衡方案的瓶颈。

6-Nginx的架构和原理

6.1-Nginx的整体架构

1. 主进程

Nginx启动时,会生成两种类型的进程,一个是主进程(master),一个(windows版本的目前只有一个)或多个工作进程(worker)。

主进程并不处理网络请求,主要负责调度工作进程,也就是图示的3项:加载配置、启动工作进程、非停升级

因此,Nginx启动以后,查看操作系统的进程列表,我们就能看到至少有两个Nginx进程。

2. 工作进程

服务器实际处理网络请求及响应的是工作进程(worker),在类unix系统上,Nginx可以配置多个worker,而每个worker进程都可以同时处理数以千计的网络请求。

3.模块化设计

Nginx的worker进程,包括核心和功能性模块,核心模块负责维持一个运行循环(run-loop),执行网络请求处理的不同阶段的模块功能。

比如:网络读写、存储读写、内容传输、外出过滤,以及将请求发往上游服务器等。

而其代码的模块化设计,也使得我们可以根据需要对功能模块进行适当的选择和修改,编译成具有特定功能的服务器。

4. 事件驱动模型

基于异步及非阻塞的事件驱动模型,可以说是Nginx得以获得高并发、高性能的关键因素,同时也得益于对Linux、Solaris及类BSD等操作系统内核中事件通知及I/O性能增强功能的采用,如kqueue、epoll及eventports。

5. 代理(proxy)设计

代理设计,可以说是Nginx深入骨髓的设计,无论是对于HTTP,还是对于FastCGI、Memcache、Redis等的网络请求或响应,本质上都采用了代理机制。所以,Nginx天生就是高性能的代理服务器。

6.2-Nginx的模块化设计

高度模块化的设计是Nginx的架构基础。Nginx服务器被分解为多个模块,每个模块就是一个功能模块,只负责自身的功能,模块之间严格遵循“高内聚,低耦合”的原则。

如下图所示:

1. 核心模块

核心模块是Nginx服务器正常运行必不可少的模块,提供错误日志记录、配置文件解析、事件驱动机制、进程管理等核心功能。

2. 标准HTTP模块

标准HTTP模块提供HTTP协议解析相关的功能,比如:端口配置、网页编码设置、HTTP响应头设置等等。

3. 可选HTTP模块

可选HTTP模块主要用于扩展标准的HTTP功能,让Nginx能处理一些特殊的服务,比如:Flash多媒体传输、解析GeoIP请求、网络传输压缩、安全协议SSL支持等。

4. 邮件服务模块

邮件服务模块主要用于支持Nginx的邮件服务,包括对POP3协议、IMAP协议和SMTP协议的支持。

5. 第三方模块

第三方模块是为了扩展Nginx服务器应用,完成开发者自定义功能,比如:Json支持、Lua支持等。

6.3-Nginx的请求方式处理

Nginx是一个高性能的Web服务器,能够同时处理大量的并发请求。它结合多进程机制和异步机制,异步机制使用的是异步非阻塞方式,接下来就给大家介绍一下Nginx的多线程机制和异步非阻塞机制。

1. 多进程机制

服务器每当收到一个客户端时,就有服务器主进程(master process)生成一个子进程(worker process)出来和客户端建立连接进行交互,直到连接断开,该子进程就结束了。

使用进程的好处是各个进程之间相互独立,不需要加锁,减少了使用锁对性能造成影响,同时降低编程的复杂度,降低开发成本。

其次,采用独立的进程,可以让进程互相之间不会影响,如果一个进程发生异常退出时,其它进程正常工作,master进程则很快启动新的worker进程,确保服务不会中断,从而将风险降到最低。

缺点是操作系统生成一个子进程需要进行内存复制等操作,在资源和时间上会产生一定的开销。当有大量请求时,会导致系统性能下降。

2. 异步非阻塞机制

每个工作进程使用异步非阻塞方式,可以处理多个客户端请求。

当某个工作进程接收到客户端的请求以后,调用IO进行处理,如果不能立即得到结果,就去处理其他请求(即为非阻塞),而客户端在此期间也无需等待响应,可以去处理其他事情(即为异步)

当IO返回时,就会通知此工作进程,该进程得到通知,暂时挂起当前处理的事务去响应客户端请求。

6.4-Nginx事件驱动模型

在Nginx的异步非阻塞机制中,工作进程在调用IO后,就去处理其他的请求,当IO调用返回后,会通知该工作进程。

对于这样的系统调用,主要使用Nginx服务器的事件驱动模型来实现,如下图所示:

如上图所示,Nginx的事件驱动模型由事件收集器、事件发送器和事件处理器三部分基本单元组成。

  • 事件收集器:负责收集worker进程的各种IO请求;
  • 事件发送器:负责将IO事件发送到事件处理器;
  • 事件处理器:负责各种事件的响应工作。

事件发送器将每个请求放入一个待处理事件列表,使用非阻塞I/O方式调用事件处理器来处理该请求。

其处理方式称为“多路IO复用方法”,常见的包括以下三种:select模型、poll模型、epoll模型。

6.5-Nginx进程处理模型

Nginx服务器使用 master/worker 多进程模式,多线程启动和执行的流程如下:

主程序Masterprocess启动后,通过一个for循环来接收和处理外部信号

主进程通过fork()函数产生worker子进程,每个子进程执行一个for循环来实现Nginx服务器对事件的接收和处理

一般推荐worker进程数与CPU内核数一致,这样一来不存在大量的子进程生成和管理任务,避免了进程之间竞争CPU资源和进程切换的开销。

而且Nginx为了更好的利用多核特性,提供了CPU亲缘性的绑定选项,我们可以将某一个进程绑定在某一个核上,这样就不会因为进程的切换带来Cache的失效。

对于每个请求,有且只有一个工作进程对其处理。首先,每个worker进程都是从master进程fork过来。在master进程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。

所有worker进程的listenfd会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有worker进程在注册listenfd读事件前抢占accept_mutex

抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。

当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,一个完整的请求就是这样。

我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。

如下图所示:

在Nginx服务器的运行过程中,主进程和工作进程需要进程交互。交互依赖于Socket实现的管道来实现。

1. 主进程与工作进程交互

这条管道与普通的管道不同,它是由主进程指向工作进程的单向管道,包含主进程向工作进程发出的指令工,作进程ID等。同时主进程与外界通过信号通信;每个子进程具备接收信号,并处理相应的事件的能力。

2. 工作进程与工作进程交互

这种交互和主进程-工作进程交互基本一致,但是会通过主进程间接完成,工作进程之间是相互隔离的。

所以当工作进程W1需要向工作进程W2发指令时,首先找到W2的进程ID,然后将正确的指令写入指向W2的通道,W2收到信号采取相应的措施。

参考:

https://www.jianshu.com/p/5026ab611d4e

https://www.jb51.net/article/143985.htm

https://mp.weixin.qq.com/s/RfaPtO3VWDozIZvGxN2ASQ

https://mp.weixin.qq.com/s/bOhypYveaRbAbDPWUEdFfg