一、3W问题
1.1 Nginx是什么
Nginx本质上是一个轻量级的Web服务器、高性能的HTTP服务器、反向代理服务器、同时也是一个IMAP/POP3/SMTP代理服务器。
1.2 为什么要使用Nginx
单体服务场景下可以不使用Nginx,但是集群环境为了让请求合理地分布到各个服务器上,就需要再加上一层负载层,负责对请求进行转发。(没有什么架构难题是加一层不能解决的,如果有,那就再加一层)
Nginx最大的特点就是性能稳定,支持的并发数高,占用的资源较少。除此之外,还包含如下的功能:
- HTTP服务器;可以作为一个HTTP服务器提供HTTP请求访问;
- 反向代理;代理用户要访问的目标服务器;
- 负载均衡;高并发场景下分流请求到多个服务器上;支持的方式有轮询、权重、ip哈希、url哈希、按响应时间等;
- 动静分离;应用于前后端分离的项目,将前端和后端的请求分离;
1.3 如何使用
Windows系统下,下载zip包,解压缩后,进入文件夹,使用命令行执行命令./nginx.exe
即可,此时访问localhost:80
即可。
Linux系统下安装可以参考《Linux安装nginx》,本文强烈推荐使用Docker拉取nginx镜像进行学习。
docker pull nginx:latest
docer run -d -p 80:80 --name my-nginx nginx:latest
如下内容都是基于Linux下的nginx进行讲解。
如下是一些常用的命令来实现nginx的不同功能:
./nginx
、./nginx -c path/nginx.conf
,使用默认配置文件或者使用指定的配置文件启动;-
nginx -t
、nginx -t -c path/nginx.conf
,验证配置是否正确;# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
-
nginx -v
,查看版本号;# nginx -v nginx version: nginx/1.21.4
nginx -s quit
,正常停止和关闭nginx;nginx -s stop
,强制快速停止和关闭nginx;-
nginx -s reload
,配置文件修改后重新装载启动;# nginx -s reload 2021/11/23 01:14:51 [notice] 40#40: signal process started
二、文件夹分类
2.1 Windows
- conf,用来存放配置的目录,其中最主要的就是nginx.conf文件;
- html,默认的静态资源目录,index.html作为默认主页;
- logs,默认的日志目录,主要有access.log和error.log;
2.2 Docker
-
/usr/share/nginx/html/
,默认的静态资源目录,index.html作为默认主页; -
/etc/nginx/
,用来存放配置的目录,其中最主要的就是nginx.conf文件; -
/var/log/nginx/
,用来存放日志的目录,主要有access.log和error.log;
三、默认配置文件
配置文件内容可以有很多很复杂,但是总体来说分为如下几个模块:
- 全局块,配置影响nginx全局的指令。一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等;
- 事件块,配置影响nginx服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等;
- HTTP块,可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等;
- server块,配置虚拟主机的相关参数,一个http中可以有多个server。
- location块,配置请求的路由,以及各种页面的处理情况。
- server块,配置虚拟主机的相关参数,一个http中可以有多个server。
如下是初始状态下nginx.conf
配置的内容:
########### 每个指令必须有分号结束。#################
#####全局块#####
#配置的用户或者组
user nginx;
#配置工作进程数量
worker_processes auto;
#配置错误日志目录和级别
error_log /var/log/nginx/error.log notice;
#主进程ID记录
pid /var/run/nginx.pid;
#####全局块#####
#####事件块#####
events {
#配置最大连接数
worker_connections 1024;
}
#####事件块#####
#####HTTP块#####
http {
#包含其它配置文件,mime.types是MIME类型映射表,根据返回文件的后缀名在该配置中找到MIME类型放到"Content_Type"中
include /etc/nginx/mime.types;
#默认文件类型,即MIME类型的"Content_Type"
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;
#配置启用最小传输限制功能
#tcp_nopush on;
#配置连接超时时间
keepalive_timeout 65;
#配置开启gizp压缩
#gzip on;
#引入其它配置项内容
include /etc/nginx/conf.d/*.conf;
}
#####HTTP块#####
引入的mime.types
默认配置如下:
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
......
text/mathml mml;
text/plain txt;
......
image/avif avif;
image/png png;
......
application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
......
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;
......
}
引入的conf.d
文件夹下面就一个default.conf
配置文件,内容如下:
#配置一个虚拟主机
server {
#配置监听的端口
listen 80;
listen [::]:80;
#配置当前server的虚拟服务器识别路径,nginx根据请求头中的Host字段来匹配具体的server进行处理
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
#配置匹配的路径
location / {
#寻找文件的目录
root /usr/share/nginx/html;
#默认返回的文件资源
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
如此,一个默认的配置就是如此。我们启动nginx之后,访问localhost:80
就能访问到index.html的内容了。
四、实例演示——反向代理
目标:使用Docker启动两个nginx服务:service1和service2,代表两个后端服务。然后再启动一个nginx服务作为反向代理服务器,根据请求路径的不同将请求分发给service1和service2。
创建文件夹nginx-demo,里面创建service1文件夹、service2文件夹、my-nginx文件夹;
service1下面文件内容分别如下:
-
index.html
,代表service1服务的内容;Service1 Message from Service1!
-
default.conf
,代表service1的nginx-server的配置;server { listen 80; server_name localhost; #匹配/service1/路径 location /service1 { # 去/usr/share/nginx/html/下面找文件 alias /usr/share/nginx/html/; # 默认返回文件 index index.html; } # redirect server error pages to the static page /50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
-
Dockerfile
,镜像打包文件;FROM nginx:latest COPY ./default.conf /etc/nginx/conf.d/ COPY ./index.html /usr/share/nginx/html/ EXPOSE 80
此时执行如下打包镜像和运行容器的命令:
docker build -t zhangxun/service1:1.0.1 .
docker run -d -p 8081:80 --name service1 zhangxun/service1:1.0.1
此时访问http://localhost:8081/service1/
就能访问到service1的服务内容了。
service2文件夹下的内容和service1基本雷同,此处不再赘述,通过http://localhost:8082/service2/
就能访问到service2的服务内容了。
最后,需要配置反向代理服务器,在my-nginx文件夹下创建如下文件内容:
-
default.conf
,代表反向代理服务器的nginx-server的配置;server { listen 80; server_name localhost; location /service1 { # 需要填写宿主机的地址 ,而不是localhost,不然就是my-nginx容器内部 proxy_pass http://172.21.32.1:8081/service1/; } location /service2 { # 需要填写宿主机的地址 ,而不是localhost,不然就是my-nginx容器内部 proxy_pass http://172.21.32.1:8082/service2/; } # redirect server error pages to the static page /50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
-
Dockerfile
,镜像打包文件;FROM nginx:latest COPY ./default.conf /etc/nginx/conf.d/ EXPOSE 80
此时执行如下打包镜像和运行容器的命令:
docker build -t zhangxun/my-nginx:1.0.1 .
docker run -d -p 80:80 --name my-nginx zhangxun/my-nginx:1.0.1
此时访问http://localhost:80/service1
就能得到service1的内容,访问http://localhost:80/service2
就能得到service2的内容;如此反向代理的目标就实现了。大致实现原理图如下。
注意事项:
location有两种写法,
location /path
是模糊匹配,location = /path
是精确匹配;root /path
会去/path/location/
目录下面去找文件;alias /path
会在/path/
下面去找文件;它们的区别就是alias不会带上location的路径;my-nginx配置中代理的服务器地址不能写localhost,否则就还是访问自己容器内部的服务;
-
反向代理服务器中只配置了一个server,两个location代表两个服务,也可以配置成两个server,如下配置所示,效果是一样的。
server { listen 80; server_name localhost; location /service1 { # 需要填写宿主机的地址 ,而不是localhost,不然就是my-nginx容器内部 proxy_pass http://172.21.32.1:8081/service1/; } # redirect server error pages to the static page /50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } server { listen 80; server_name localhost; location /service2 { # 需要填写宿主机的地址 ,而不是localhost,不然就是my-nginx容器内部 proxy_pass http://172.21.32.1:8082/service2/; } # redirect server error pages to the static page /50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
五、实例演示——动静分离
动静分离和反向代理的原理及配置几乎都是相同的,只不过静态资源并不算是一个服务,而是存放在nginx上的资源,通过请求路径来区分,究竟是把请求转发给服务,还是从nginx上的某个路径下去寻找资源,由于比较简单,不再重复演示。
六、实例演示——负载均衡
目标:使用Docker启动两个nginx服务:service1和service2,代表两个后端服务。然后再启动一个nginx服务作为负载均衡服务器,将请求分发给service1和service2。
4.1 准备工作
复用第四节的案例,但是service1和service2的default.conf更改如下:
server {
listen 80;
server_name localhost;
#匹配/路径
location / {
# 去/usr/share/nginx/html/下面找文件
alias /usr/share/nginx/html/;
# 默认返回文件
index index.html;
}
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
其余内容保持不变,构建镜像并启动容器后测试下访问http://localhost:8081
和http://localhost:8082
能否正常返回内容。
4.2 负载均衡服务搭建
不同于反向代理,需要根据不同的请求路径转发请求到不同的服务;负载均衡则是将相同的请求按照负载规则转发给某个服务集群中的一个服务。我们新建一个nginx.conf
配置文件,用来替换etc/nginx/nginx.conf
的默认配置文件。
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
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;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
# my-service用于两个上游服务
upstream my-service {
server 172.21.32.1:8081;
server 172.21.32.1:8082;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://my-service;
}
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
Dockerfile稍作改变:
FROM nginx:latest
COPY ./nginx.conf /etc/nginx/
EXPOSE 80
然后执行构建和启动容器的命令,我们访问localhost:80
就会发现,service1和service2被轮询为我们提供服务,如此就实现了负载均衡的实例。
4.3 负载策略
Nginx负载均衡是通过upstream模块来实现的,内置实现了三种负载策略:
-
轮询(默认策略),根据请求次数,将每个请求均匀地或者按照权重分配到每个服务上;
upstream my-service { server 172.21.32.1:8081 weight=1; server 172.21.32.1:8082 weight=2; }
weight表示权重,数值越大,被分到请求的次数越多。
-
最少连接,将请求分配给连接数最少的服务,Nginx会统计哪些服务器的连接数最少;
upstream my-service { least_conn; server 172.21.32.1:8081; server 172.21.32.1:8082; }
-
IP Hash,绑定处理请求的服务器;第一次请求时,根据该客户端的IP算出一个HASH值,将请求分配到集群中的某一台服务器上;后面该客户端的所有请求,都将通过HASH算法,找到之前处理这台客户端请求的服务器,然后将请求交给它来处理;
upstream my-service { ip_hash; server 172.21.32.1:8081; server 172.21.32.1:8082; }
除了内置的负载策略,还有其它第三方插件提供的负载策略,这些策略需要在nginx服务器上单独安装:
-
Fair,按照后端服务器的响应时间来分配请求,响应时间短的会被分配更多请求;
upstream my-service { fair; server 172.21.32.1:8081; server 172.21.32.1:8082; }
-
URL Hash,按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存服务器时比较有效;
upstream my-service { hash $request_uri; hash_method crc32; server 172.21.32.1:8081; server 172.21.32.1:8082; }
4.4 负载状态
- weight,默认为1,表示服务的权重,数值越大,被分到的请求越多;
- max_fails,默认为1,该服务被允许请求失败的次数,一旦超过则会选择其它服务;
- fail_timeout,默认10秒,服务在请求失败max_fails次数后,在fail_timeout时间内不会再给它分配请求;
- backup,表示备份服务,所有服务都不可用是才分配请求给备份服务;
- down,表示下线,该服务不参与负载;
- max_conns,该服务允许的最大连接数量,超过时将不会再分配请求给它,默认为0表示不限制;
- 等等其它状态可以参考官网文档;
七、参考文档
Using nginx as HTTP load balancer
Module ngx_http_upstream_module (nginx.org)