想了解Nginx的靓仔靓妹看过来

前言

原本以为nginx方面出了问题,都是运维的事情。事不关己,可以高高挂起。可是在项目中频频遇到运维几乎去每个甲方公司部署网站, 配置完之后,不是网站因为资源路径配置不对页面出不来,就是接口报跨域或404。运维说是前端的问题,让前端排查。不懂nginx的前端,因为看不懂运维发的nginx配置内容,也无法自证清白,只能吃哑巴亏。为了不再吃闷亏,咱就静下心来,把nginx基础常用的知识点都扫盲一下,现在我们进入正题。

Nginx简介

Nginx 全称是"Engine X"(顾名思义的话,感觉全称和简称含义差别蛮大),Nginx是一个轻量级的、高性能的、静态web服务器。同类的web服务器还有:

web服务器 说明
Apache Apache是Apache开源组织开发的web服务器,免费,Apache在处理动态请求方面有优势,Apache的rewrite功能比Nginx强大,动态页面模块超多,基本想到的都可以找到
Tomcat Tomcat也是Apache开源组织开发的Java服务器,主要用来跑jsp、php、python等。
IIS IIS是微软开发的web服务器,是收费的,主要用来跑asp.net、asp、php,只能在windows下运行。

Nginx突出特点是:

  • 并发能力强

一个Nginx服务器在不做任何配置的情况下并发量可达1000左右,在硬件允许的前提下(主机还需做一些优化设置),Nginx可支持高达5-10万的并发量(在windows机器上没有这么高)。对比一下Apache,最大默认并发连接数是256;Tomcat服务器默认的并发量为150,当超过150个用户同时访问某Servlet时,Tomcat的响应就会变得非常慢;IIS默认的最大并发连接数是42亿之多, 给人感觉不靠谱,没有对比价值。

  • 占用内存少。

Nginx官方给出的测试数据是:10000个非活跃连接,在Nginx中仅消耗2.5M内存,应对一般的Dos攻击毫无压力。Apache是同步多进程模型,一个连接对应一个进程;Nginx是异步的,多个连接(万级别)可以对应一个进程。这就是Nginx并发数高, CPU内存占用低的原因。

nginx运行原理

nginx采用多进程工作方式,启动nginx后,会运行一个master进程和多个worker进程。其中master充当整个进程组与用户的交互接口,同时对进程进行监护,管理worker进程来实现服务重启、平滑升级、更换日志文件、配置文件实时生效等功能。worker进程用来处理网络事件,worker进程之间是平等的,它们共同处理来自客户端的请求。通俗理解就是master进程主要负责对外揽活,将活合理的分配给多个worker,每个worker进程负责干活。nginx的进程模型如下图所示:

设置多少个woker进程合适?

Nginx中每个worker都是一个独立的进程,每个进程里只有一个主线程,通过异步非阻塞的方式来处理请求,即使成千上万个请求也不在话下。每个worker的线程可以把一个CPU的性能发挥到极致。所以worker进程数和服务器的CPU数相等是最为适宜的。设少了会浪费CPU,设多了会造成CPU频繁切换上下文,带来性能损耗。

windows下查看CPU数量的方法是:

  1. 按下"Win + R "键打开"运行"窗口,输入"cmd",点击"确认"
  2. 输入"msinfo32"命令后按 Enter 键确认,即可查看 CPU 逻辑处理器详细信息。
  • 相信你看到上面显示4个内核,8个逻辑处理器的时候有点懵,到底是几个CPU呢?

操作系统可以使用逻辑CPU来模拟真实CPU。 在没有多核处理器的时候,一个物理CPU只能有一个物理内核, 有了多核技术,一个物理CPU可以有多个物理内核,可以把一个CPU当作多个CPU使用,即逻辑CPU。 没有开启超线程时,逻辑CPU的个数就是总的CPU物理内核数。 开启超线程后,逻辑CPU的个数就是总的CPU物理内核数的两倍。

再看看nginx.conf中默认的worker_processes设置值, 按照上面的理论,worker_processes设置成8才是最佳值。

bash 复制代码
worker_processes  1;

events {
    worker_connections  1024;
}

最大连接数计算方法(理论值)

worker_ connection表示每个worker进程所能建立连接的最大值(不是随便设置的,与内存和操作系统级别的"进程最大可打开文件数"有关联),从用户的角度看,http1.1协议下,由于浏览器默认使用两个并发连接(连接是没有方向的,请求是双向的), 因此一个nginx 能建立的最大连接数,计算方法为:

  • nginx作为http服务器的时候
ini 复制代码
max_clients = worker_processes * worker_connections / 2
  • nginx作为反向代理服务器的时候
ini 复制代码
max_clients = worker_processes * worker_connections / 4

因为作为反向代理服务器,客户端和nginx要建立连接,nginx和后端服务器也要建立连接。

nginx并发数压测方法

可使用ApacheBench (简称ab)或wrk等工具测试Nginx的并发性能。这类工具可以生成大量并发的HTTP请求,可测量出请求的响应时间和错误率。

使用ab进行简单压力测试的步骤为:

  1. 安装ab工具,windows系统去Apache官网下载zip包,解压zip包,添加一个ab工具的环境变量,即可在任意目录下执行ab压测命令。
  2. 启动Nginx服务器, 进入Nginx解压目录,双击nginx.exe可执行文件。本例中nginx.conf的配置如下(未贴出的部分采用的都是默认配置):
bash 复制代码
    server {
        listen       12000;
        server_name  localhost;

        index  index.html;
        root html;
    }
  1. 运行ab工具,使用以下命令运行ab工具发送HTTP请求:
bash 复制代码
ab -c 1000 -n 10000 http://localhost:12000/

其中-c参数指定并发请求数,-n参数指定要发送的总的请求数,上例中相当于1000个人访问10次http://localhost:12000/这个站点(发送10次主要是为了测试稳定性)。

测试结果如下:

bash 复制代码
This is ApacheBench, Version 2.3 <$Revision: 1903618 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests

#服务器信息和版本
Server Software:        nginx/1.20.0 
#服务器的域名
Server Hostname:        localhost
#端口
Server Port:            12000

#访问的路径
Document Path:          /
# http响应的正文长度
Document Length:        612 bytes

#并发请求数
Concurrency Level:      1000
#整个测试持续的时间,默认秒
Time taken for tests:   9.009 seconds
#完成的请求数
Complete requests:      10000
#失败的请求数
Failed requests:        0
#传输的总数据量
Total transferred:      8450000 bytes
#HTML内容传输量
HTML transferred:       6120000 bytes
#平均每秒请求数
Requests per second:    1109.97 [#/sec] (mean)
# 平均每次并发请求时间
Time per request:       900.924 [ms] (mean)
#平均每次请求时间
Time per request:       0.901 [ms] (mean, across all concurrent requests)
#传输速率
Transfer rate:          915.94 [Kbytes/sec] received
# 连接时间
Connection Times (ms)
#            最小值 平均值±标准差 中位数 最大值
              min  mean[+/-sd] median   max
# 连接
Connect:        0    1  16.0      0     511
# 处理
Processing:    47  663 194.2    694    1227
# 等待
Waiting:        2  473 258.2    613     708
Total:         47  664 194.1    694    1228

#所有服务请求的百分比占用时间,这里50%的请求用时694ms,一般看90%的部分
Percentage of the requests served within a certain time (ms)
  50%    694
  66%    701
  75%    705
  80%    706
  90%    708
  95%    712
  98%   1181
  99%   1185
 # 最长请求时间 1228ms
 100%   1228 (longest request)

从测量结果可以看出, 不对nginx做任何优化配置的情况下,nginx并发量可以达到宣称的1000,没有失败的请求(Failed requests为0),平均每次请求的时间是0.901ms(这一点会受外部网络因素的影响),并发性能果然是不错的。

nginx配置文件

nginx配置文件分为5个配置块,每个块的配置内容如下表所示:

区块 说明
全局块 配置影响nginx全局的指令。一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,worker process数等。
events块 配置影响nginx服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。
http块 可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等。
server块 配置虚拟主机的相关参数,如监听端口,服务器地址,ssl 模块,连接请求上限次数,字符集,服务日志所在目录以及日志格式,错误页等,一个http中可以有多个server。
location块 配置请求的路由和接口,以及各种异常页面。

nginx区块的层级结构如下:

知道了nginx每个块要配置的内容,有时候你在网上搜索到一些nginx代码片段,就不会再犯迷糊,是应该配置在server块里面,还是location块里面(当然某些字段比如说root, 在这两个区块都可以放置,但作用范围不一样)。 另外,location的匹配规则比较重要,但限于篇幅,这里就不展开了。详细的location匹配规则和验证示例,请参考此文

nginx 版本说明

Nginx最初是由俄罗斯人Igor Sysoev(伊戈尔-塞罗耶夫)使用C语言为俄罗斯访问量第二的Rambler.ru站点开发的一款服务器,2004年10月发布第一个版本。windows下nginx只需下载无需安装,解压下载下来的压缩包就可以用了,下载的时候可以看到有三种版本可供选择。

  • 主线版本-会持续的加入新特性与修补bug。主线版本的更新周期,大约是4~6周一次,可以通过订阅Nginx官方的更新日志,获取更新内容。
  • 稳定版本-只修复有高安全风险的bug。
  • 历史版本,这个很好理解。

nginx在每年4月前后会进行一次版本号的推进,发布新的主线版本与稳定版本,例如今年的1.25.0与1.24.0(主线版本的版本号都是奇数,稳定版本的版本号都是偶数)。版本号推进后,旧的稳定版本将不继续更新,也不继续合入bug修复。官方推荐尽量使用主线版本。

windows下的nginx命令

你会发现, 其它命令都是以nginx开头的, 唯独start nginx命令比较特别,以start开头。带着好奇心查询了一下, 发现start原来是windows下的批处理命令,用于启动exe程序,这下终于理解了这条命令为什么比较特别了。

命令 含义
nginx -t 测试nginx配置文件语法是否正确
start nginx 启动nginx, windows下直接双击nginx.exe也能达到同样效果
nginx -s stop stop是快速停止nginx,可能并不保存相关信息;
nginx -s quit quit是完整有序的停止nginx,并保存相关信息
nginx -s reload 重新加载nginx, 当配置信息修改且在运行中,用于平滑的重新载入配置
nginx -v 查看nginx版本
nginx -V 查看nginx版本及编译配置参数

注意,windows下nginx命令只能在nginx的解压目录执行,配置一个环境变量,在别的路径启动时,会出现各种异常。

  • 比如说执行start nginx,用tasklist命令查询会发现nginx并未启动
  • 再比如说执行 nginx -s reload命令, 会报找不到相对路径logs/error.log文件

为了省心省事, 建议在windows操作系统上,给nginx文件夹建一个桌面快捷方式,方便操作。另外,再补充几个与nginx进程相关的命令:

命令 用途
tasklist | findstr nginx 查看nginx是否已经启动
tasklist | findstr 9088 查看PID对应的应用程序
taskkill /pid 9088 -f -t 杀死指定的PID进程及其子进程
netstat -aon | findstr 8081 查看某个端口是否被占用

高频使用配置

开启静态资源缓存

添加以下几行开启缓存静态资源,如css文件、js文件, 图片、图标、视频,字体文件,模板文件,软件包等,不同的文件类型设置不同的有效期。

bash 复制代码
# 经常变更的资源文件缓存时间设置短一些
location ~* \.(css|js)$ {
    access_log off;
    expires 7d;
}
location ~* \.(gif|jpg|png|ico|otf|sng|xls|doc|exe|jpeg|tgx)$ {
    access_log off;
    expires max;
}

开启gzip静态压缩

gzip文件一般比源文件体积要小好几倍(如下图所示),服务器将gzip文件返回给浏览器,可以减少网络传输量,提高传输速率和效率。一般我们都会在项目打包构建的时候开启gzip压缩功能,生成对应的gzip文件。

将静态资源部署到服务器上,在nginx.conf中开启下面的配置,nginx才会自动去寻找网站根目录下.gz结尾的文件,返回给客户端,这就是所谓的静态压缩;

bash 复制代码
http {
    gzip_static on;
}

需要注意的是:nginx默认是没有安装ngx_http_gzip_static_module静态压缩模块的,需手动开启。进入到nginx安装路径的configure文件夹下,执行下面的命令,就能开启gzip静态压缩功能。

bash 复制代码
./configure --with-http_gzip_static_module && make

如果响应报文中出现Content-Encoding:gzip字段,说明gzip静态模块开启成功。

将网站从http升级到https协议

为什么要升级到https协议, 因为不升级,用户访问的时候有些浏览器会向用户提示你的网站不安全(如下图所示),影响网站用户体验和SEO排名。

SSL 证书通常需要购买(可以在云服务商上购买),也有免费的,通过第三方 SSL 证书机构颁发。但是一般免费的 SSL 证书只支持单个域名。假设现在已经有SSL证书了,开启https协议的配置方法如下:

bash 复制代码
server{
        listen 443 ssl http2;
        # 网站根目录
        root /home/www/xxx/dist;
        # 将www.example.com替换成自己的域名
        server_name www.example.com;
      
        # 将example.top.pem替换成已上传的证书文件的名称。
        ssl_certificate /etc/nginx/cert/example.top.pem;
        # 需要将example.top.key替换已上传的证书密钥文件的名称。
        ssl_certificate_key /etc/nginx/cert/example.top.key;
        # 会话超时时间
        ssl_session_timeout 5m;
        # 定义算法
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        # 协议版本
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        # 优先采取服务器算法
        ssl_prefer_server_ciphers on;

        # ...
}

# 设置HTTP请求自动跳转HTTPS
server {
        listen 80;
        # 将www.example.com替换成自己的域名
        server_name www.example.com;
        rewrite ^(.*)$ https://$host$1;
        
        # ...
}

history路由模式配置

我们都知道, 单页应用的路由模式有两种: hash 和 history 模式。hash 模式通用性好,但显示在浏览器地址栏的url不够优雅。相比于 hash 模式来说,history 模式的url更加美观, 所以我们通常倾向于使用history模式。使用history 模式会遇到一个问题,就是当页面刷新时,如果url没有匹配上服务器中的任何资源路径,页面会出现 404 错误。因此需要加一条url找不到时,让页面返回到首页index.html的配置。

bash 复制代码
server {
    listen       80;
    server_name  www.example.com;

    location /demo {    
        index  index.html;
        alias  D:/web/dist;
        try_files $uri $uri/ /index.html;
    } 
}

try_files 会去尝试到网站目录读取用户访问的文件,如果第一个变量存在(即用户访问的地址),就直接返回;不存在继续读取第二个变量(uri/代表访问的是一个目录),如果第二个变量存在,直接返回;不存在直接跳转到第三个参数上。另外,你可能不知道alias这个字段的含义,想了解的话,请阅读此文

接口转发

将不同前缀的接口url转到不同机器去处理。如下例所示, 让http://192.168.1.100:9000/h5/xxx的接口请求转发到http://192.168.10.1:9100处理,让http://192.168.1.100:9000/admin/xxx的接口请求转发到http://192.168.10.2:9200处理。

bash 复制代码
server {
  listen       9000;   
  server_name  192.168.1.100;      
  
  location  ~ /h5/ {  
      proxy_pass http://192.168.10.1:9100;         
  } 

  location  ~ /admin/ {  
      proxy_pass http://192.168.10.2:9200;         
  } 
}

负载均衡

在浏览器地址栏输入 http://192.168.1.100:9000/example/test.html ,平均分配请求到192.168.10.1主机的9100 和 192.168.10.2主机的9200 端口服务,实现负载均衡效果。

bash 复制代码
   # 负载均衡服务器列表
    upstream balance_server {   
      server 192.168.10.1:9100;
      server 192.168.10.2:9200;
    }
    

    server {
        listen       9000;  
        server_name  192.168.1.100;  
   
        location  / {       
           root D:/web/dist; 
           index index.html index.htm;  
           #请求转向 balance_server 定义的服务器列表 
           proxy_pass  http://balance_server;       
        } 
    }

nginx 负载均衡策略有:

  • 轮询 (默认)

    这种策略的优点是实现简单,将接收到的请求按顺序循环的分配下去,服务器接收请求的比例是 1:1,如果某个服务器挂掉,会自动剔除。

  • 权重

    服务器性能不均衡时根据每个服务器的性能设置权重值,服务器接收请求的比例就是各自配置的weight比例,在下示例中8次请求有三次会被分配到9100上,其它5次分配到9200上。backup是指热备,只有当9100和9200都宕机的情况下才走9300。

    bash 复制代码
          upstream balance_server {   
            server 192.168.10.1:9100 weight=3;
            server 192.168.10.2:9200 weight=5;
            server 192.168.10.3:9300 weight=4 backup;
          }
  • ip_hash

    上面2种方式都有一个问题,当程序不是无状态的时候(采用session保存登陆态),这时候就会出问题,比如把登录信息保存到了这台服务器的session中,那么请求转到另外一台服务器的时候就需要重新登录了,所以很多时候我们需要一个客户只访问一个服务器,那么就需要用ip_hash了,ip_hash的每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session问题。

    bash 复制代码
          upstream balance_server { 
            ip_hash;  
            server 192.168.10.1:9100;
            server 192.168.10.2:9200;
          }
  • fair(第三方模块,nginx本身不支持)

    按服务器的响应时间来分配请求,响应时间短的优先分配, 这种配置能给用户较快的响应。注意:使用第三方fair时,nginx需要添加fair模块,添加fair模块的方法参照此文

    bash 复制代码
          upstream balance_server { 
            fair;  
            server 192.168.10.1:9100;
            server 192.168.10.2:9200;
          }
  • url_hash(第三方模块,nginx本身不支持)

按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器有缓存时比较有效。在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method表示使用的hash算法;注意:需要安装第三方模块url_hash,安装方法参见此文

ini 复制代码
upstream balance_server {
    hash $request_uri;
    hash_method crc32;
    server 192.168.10.1:9100;
    server 192.168.10.2:9200;
}

获取用户真实IP的配置方法

后端的Web服务器可以通过X-Forwarded-For获取用户真实IP,获取原理参见此文的第三章节

bash 复制代码
location / {
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

最后

经过一番学习, 我们掌握了nginx一些常用的配置,运维以后再想甩锅给我们,难度可能要上升。说不定还会被我们反杀🤣(此处纯属开玩笑)。我们的价值观是止于至善,与人为善。不断增强自身的实力才是正途,我们知道的多了,对自己,对别人都有益处(减少和别人的一些纠纷和扯皮),何乐而不为。

相关推荐
qiyi.sky9 分钟前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~13 分钟前
分析JS Crash(进程崩溃)
java·前端·javascript
安冬的码畜日常22 分钟前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
l1x1n01 小时前
No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史
前端·http·html
昨天;明天。今天。1 小时前
案例-任务清单
前端·javascript·css
zqx_72 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己3 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称3 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
大G哥3 小时前
记一次K8S 环境应用nginx stable-alpine 解析内部域名失败排查思路
运维·nginx·云原生·容器·kubernetes
长天一色3 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript