目录
参考
推荐一个非常好的 NGINX WEB UI 可视化用户界面
Nginx为什么快到根本停不下来?
nginx原理
运行过程
Nginx 服务器,正常运行过程中:
多进程:一个 Master 进程、多个 Worker 进程。
Master 进程:管理 Worker 进程。对外接口:接收外部的操作(信号);对内转发:根据外部的操作的不同,通过信号管理 Worker;监控:监控 Worker 进程的运行状态,Worker 进程异常终止后,自动重启 Worker 进程。
Worker 进程:所有 Worker 进程都是平等的。实际处理:网络请求,由 Worker 进程处理。Worker 进程数量:在 nginx.conf 中配置,一般设置为核心数,充分利用 CPU 资源,同时,避免进程数量过多,避免进程竞争 CPU 资源,增加上下文切换的损耗。
监听流程
HTTP 连接建立和请求处理过程如下:
Nginx 启动时,Master 进程,加载配置文件。
Master 进程,初始化监听的 Socket。
Master 进程,Fork 出多个 Worker 进程。
Worker 进程,竞争新的连接,获胜方通过三次握手,建立 Socket 连接,并处理请求。
Nginx 高性能、高并发
Nginx 为什么拥有高性能并且能够支撑高并发?
Nginx 采用多进程+异步非阻塞方式(IO 多路复用 Epoll)。
请求的完整过程:建立连接→读取请求→解析请求→处理请求→响应请求。
请求的完整过程对应到底层就是:读写 Socket 事件。
处理http请求
Request:Nginx 中 HTTP 请求。
基本的 HTTP Web Server 工作模式:
接收请求:逐行读取请求行和请求头,判断段有请求体后,读取请求体。
处理请求。
返回响应:根据处理结果,生成相应的 HTTP 请求(响应行、响应头、响应体)。
反向代理
通过Nginx我们可以实现反向代理,这也是我在项目中使用到的一个功能,那么什么是反向代理呢?我们用户对代理是无感知的,因为我们不需要进行任何配置就可以进行访问。我们只需要将请求发送至反向代理服务器,由反向代理服务器帮我们决定需要访问哪个资源并且将资源返回给我们。此时反向代理服务器和目标服务器对外就是服务器,但是只对外暴露了代理服务器的地址,目标服务器的地址是隐藏的。
去掉前缀
场景1:去掉一层前缀
正常情况下:在nginx配置文件中中设置了 location /api/
时 浏览器访问 /api/test
反向代理到后端服务后,后端服务接收到的url地址实际还是/api/test
需要实现的功能需求:访问url路径/api/test
经过nginx反向代理后,到达后端服务的实际url地址为/test
。
location ^~/api/ {
proxy_pass http://localhost:12345/;
}
需要注意和修改的是:在proxy_pass的最后加一个/即可。
修改完成后执行 nginx -s reload 刷新nginx配置文件,然后打开浏览器测试即可
场景2:去掉两层前缀
比如把/gatway/api/test
转发到后端是/test
location ^~/gateway/api/ {
add_header Cache-Control 'no-store';
client_max_body_size 100m;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Nginx-Proxy true;
proxy_redirect off;
proxy_connect_timeout 1;
proxy_send_timeout 20;
proxy_read_timeout 30;
proxy_pass http://192.168.0.88:8208/;
}
映射指定文件
location /WW_verify_U2CnZvzr2lMh0HaI.txt{
root /usr/local/packages;
}
返回固定json
location = /time2 {
default_type application/json;
add_header 'Access-Control-Allow-Origin' '*';
autoindex_localtime on;
add_header 'Access-Control-Allow-Credentials' 'true';
return 200 '{"code":0,"year":"2022","month":"05","day":"31","hour":"11","minute":"20","second":"21"}';
}
增加后缀映射
情况一
这个结果说明代理的时候,按照请求路径中的/api匹配到了对应的代理,代理到服务端的时候,把请求的ip和port替换为代理服务的ip和port,而对应的uri则完全没有变化,是整体的复制过去。
请求nginx:http://192.168.0.105:8087/api/system/common/doLogin
Nginx代理替换之后:http://192.168.0.105:8089/api/system/common/doLogin
代理过程:将图中红色部分替换为绿色部分,其他都不变,然后发送到服务端:
情况二
location后的路径为“/api”,代理服务的上下文为“/”(http://192.168.0.105:8089/),看下图:
服务端接收的uri为:
这个结果说明按照请求路径中的/api匹配到了对应的代理之后,把请求中的“/api”替换为代理服务上下文路径“/”,“/api”之后的路径不变,替换如下:
请求nginx:http://192.168.0.105:8087/api/system/common/doLogin
Nginx代理替换之后:http://192.168.0.105:8089/ /system/common/doLogin
代理过程:将红色的部分替换为绿色的部分,其他都不变,然后发送服务端:
情况三
location后的路径为“/api/”,代理服务的上下文为空(http://192.168.0.105:8089),看下图:
服务端接收的uri为:
这个结果和情况一是一样的,按照请求路径中的/api/匹配到了对应的代理,代理到服务端的时候,把请求的ip和port替换为代理服务的ip和port,而对应的uri则完全没有变化,是整体的复制过去。
请求nginx:http://192.168.0.105:8087/api/system/common/doLogin
Nginx代理替换之后:http://192.168.0.105:8089/api/system/common/doLogin
情况四
location后的路径为“/api/”,代理服务的上下文为“/”(http://192.168.0.105:8089/),看下图:
服务端接收的uri为:
这个结果说明和情况二是一样的,按照请求路径中的/api/匹配到了对应的代理之后,把请求中的“/api/”替换为代理服务上下文路径“/”,“/api/”之后的路径不变,替换如下:
请求nginx:http://192.168.0.105:8087/api/system/common/doLogin
Nginx代理替换之后:http://192.168.0.105:8089/system/common/doLogin
代理过程:将红色部分替换为绿色部分,其他部分不变:
配置多个前后端分离
location /exam/api {
add_header Cache-Control 'no-store';
client_max_body_size 100m;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Nginx-Proxy true;
proxy_redirect off;
proxy_connect_timeout 1;
proxy_send_timeout 20;
proxy_read_timeout 30;
##proxy_pass http://192.168.0.88:8208;
#proxy_pass http://192.168.0.44:8082/;
#proxy_pass http://web_rtc:8082/;
proxy_pass http://192.168.0.88:8101;
#proxy_pass http://192.168.0.55:9001;
}
location /exam{
alias /usr/local/packages/edevp-exam/web/;
#root /usr/local/packages/edevp-exam/web;
#try_files $uri $uri/ @rewrite;
try_files $uri $uri/ /exam/index.html;
index index.html;
charset utf-8,gbk; # 避免中文乱码
}
配置打印http请求完整请求body
配置文件上传
nginx 请求 是一个请求的内容全部接受后再转发 还是边收边发?比如请求体很大情况?
在 Nginx 中,默认情况下,请求是边收边发的,也就是说,Nginx 在接收请求的同时会将请求转发给后端服务器。这是因为 Nginx 是一个事件驱动的异步服务器,它能够高效地处理多个并发连接。对于请求体较大的情况,Nginx会使用流水线方式进行传输,边接收请求体的同时边将数据转发给后端服务器。这样可以节省内存,提高性能,避免将整个请求体全部加载到内存中再进行转发。
可以通过 proxy_request_buffering:on|off 参数控制,默认开启。
on:请求体被完全接收后再做转发
off:立即转发,不等待请求体完全接收,边接收边转发
正向代理
Nginx不仅可以实现反向代理,还能实现正向代理,那么什么是正向代理呢?前面说到反向代理我们是无感知的,我们并不知道我们需要访问的目标,我们只是知道代理的地址。而正向代理则不同,我们是很清楚我们需要访问的目标的,但是访问过程中出现了一个代理去帮我们完成这个请求并将结果返回。 此时服务器只知道请求来自哪个代理服务器,而不知道是哪个客户端发起的请求,正向代理模式屏蔽或者隐藏了真实客户端的信息。
负载均衡
Nginx提供了六种不同的负载均衡策略,分别为:
- 轮询(默认):最基本的配置方法,它是upstream模块默认的负载均衡默认策略。每个请求会按时间顺序逐一分配到不同的后端服务器。
来了一个订单会按照时间顺序来分发,比如现在是A分店接的单,再来一个订单就将订单交给B分店,这样循环。 - weight(权重):权重方式,在轮询策略的基础上指定轮询的几率。
这种方式会按照每个分店的权重来分发订单,比如A分店最大,能够处理的订单最多,这时候就应该将A分店的权重设置大一些,分发的订单就多一点。 - ip_hash:指定负载均衡器按照基于客户端IP的分配方式,这个方法确保了相同的客户端的请求一直发送到相同的服务器,以保证session会话。这样每个访客都固定访问一个后端服务器,可以解决session不能跨服务器的问题。
这种方式可以理解成根据用户的地址信息来选择将订单分发给哪家店,当然这并不意味着哪个地址近我就将这个订单分发给哪家店,因为在实际情况中我们是无法通过IP地址来判定客户端离目标服务器的距离的,你可以理解成外卖小哥也不知道这个地址离哪个分店近,他只是按照一定的规则按照订单地址分发,比如有四家分店,我可以按照地址最后一个字的音调来决定选择哪家,第一声我就选择A分店,以此类推。 - least_conn:把请求转发给连接数较少的后端服务器。轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用的时间很长,会导致其所在的后端负载较高。这种情况下,least_conn这种方式就可以达到更好的负载均衡效果。
这种方式选择的是订单数最少(连接数最少)的店来分配订单,轮询方案是将订单均匀分发给不同的店,但是不同的订单制作时间不一样,比如分店A接到的订单全是要加葱的,这时候分店A刚好没有葱了,还得花时间准备葱,这样采用轮询方案会导致订单在分店A堆积。这时候假如来了个订单按照轮询应该给A分店,但是我选择了最少订单的分店B来处理这个订单就会达到更好的负载效果。 - fair(第三方):按照服务器端的响应时间来分配请求,响应时间短的优先分配。
这种方式可以理解成按照店的处理速度来选择,哪家店能够最快处理这笔订单就交给谁。 - url_hash(第三方):按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,要配合缓存命中来使用。同一个资源多次请求,可能会到达不同的服务器上,导致不必要的多次下载,缓存命中率不高,以及一些资源时间的浪费。而使用url_hash,可以使得同一个url(也就是同一个资源请求)会到达同一台服务器,一旦缓存住了资源,再此收到请求,就可以从缓存中读取。
这种方式要和ip_hash理解分开,ip_hash确保的是同一个用户的订单是交给固定的分店处理的,而url_hash虽然也是按照某一信息来决定分发,但是却跟ip_hash有很大的区别,可以理解成url_hash是根据点的火锅种类来分发的。试想这样一个场景,分店A做了很多汤卤火锅,分店B做了很多清炖火锅,分店C做了很多清水火锅,这时候假如来了一个订单是点汤卤火锅的,假如按照ip_hash是将该订单交给分店B,这样显然不太合适,而且假如分店B做了这份订单,但是一天下来分到的订单就只有这笔订单是汤卤火锅的,这样就会造成时间和资源上的浪费,这时候url_hash的优势就出来了,我可以根据客户请求的资源来决定要分发到哪个位置。
正则匹配
~ /item/(\d+)
location ~ /item/(\d+) {
}
- ~ 表示正则匹配
- **()**表示一个分组
- \d表示数字
- + 表示任意数量
灰度发布
方法一
参考:https://www.toutiao.com/article/7206699270601376296/?log_from=f0bb656c0aa24_1678347456537
upstream backend {
server 192.168.0.1:8080;
server 192.168.0.2:8080;
server 192.168.0.3:8080;
}
server {
listen 80;
server_name example.com;
# 用户ID在灰度发布组1中
if ($cookie_gray_release_group = "1") {
set $backend "http://192.168.0.2:8080";
}
# 用户ID在灰度发布组2中
if ($cookie_gray_release_group = "2") {
set $backend "http://192.168.0.3:8080";
}
# 其他情况
if ($cookie_gray_release_group = "") {
set $backend "http://192.168.0.1:8080";
}
location / {
proxy_pass $backend;
}
}
高可用
可视化
默认登录用户名:admin
初始密码为:admin