Nginx学习笔记

笔记哥 / 03-30 / 45点赞 / 0评论 / 320阅读
# 简介 为什么要用Nginx,比如ASP.NET的kestrel本身就可以作为Web服务器使用。 主要从两点角度考虑: 1. 高可用 nginx的高可用主要体现在反向代理实现负载均衡,并衍生出当web发版更新后,实现滚动更新(`热更新`)。使得用户无感 2. 对运维友好 通过简单的配置即可实现负载均衡 3. 其它 并发请求,实现不停机更新,负载均衡,以及处理流量高峰期。此外,Nginx还可以帮助更新网站域名,证书更新,限制用户访问和爬虫等。 而以上几个优点,对于kestrel/tomcat 是比较复杂的。 Nginx主要支持如下几个功能 1. HTTP服务器 web服务器,性能非常高 2. 动静分离 术业有专攻,kertrel服务器擅长处理http请求和响应,静态资源需要app.UseStaticFiles()来启用,其侧重点在于动态请求的处理。在生产环境中面对大量的静态资源,Nginx更擅长处理静态/FTP等资源。因此可以使用Nginx来实现动静分离 3. 反向代理 什么是正向代理?类似VPN,你需要访问google,但是被墙。所有你需要一个代理来帮你访问,然后再把内容转发给你。在浏览器中配置代理服务器的相关信息。 什么是反向代理?对比正向代理,屏蔽了细节。浏览器不需要配置代理服务器,也不需要知道原来服务器的地址。通过nginx反向代理,nginx帮你全权管理好后续的操作。 4. 负载均衡 web服务器集群,将请求相对均匀的分配到不同的服务器上。 # 常见的nginx命令 1. nginx -v/V 查看版本 2. systemctl enable nginx 设置自启动 3. nginx -t 检查文件是否有误 4. nginx -s reload 重新加载配置 5. nginx -s stop 终止,也可通过Kill命令杀掉 # 核心配置文件 nginx的核心配置文件为nginx.conf主要分为四个部分 1. 全局块 这一部分主要是设置一些全局生效的配置,比如工作进程,日志存放位置等 ![image](https://cdn.res.knowhub.vip/c/2503/31/651314f7.png?G1cAAMTsdJzIJ1Gh26hD2jvFHc2ARRpBpYT1es9Z%2byb6fhdIis9offr%2b8JvWpxOjJGUjgVxQhCCVCyCWzQKzZgOsWlzDAQ%3d%3d) 2. events事件块 主要是设置单个进程的最大并发数 ![image](https://cdn.res.knowhub.vip/c/2503/31/54617ee9.png?G1cAAMTydJz4v%2fOUbqMO2kSR0AxYpBFUSliv95y1b5Hvb1Saf0bts%2b0Pv6l9NoFmC7iEyqgBLrAgK2CMxQEKJlgyv0YD) 3. http块 这里就是配置最多的地方了,比如协议支持,gzip压缩,端口监听,请求转发等 ![image](https://cdn.res.knowhub.vip/c/2503/31/1d3a5c9e.png?G1cAAMTydJz4%2b4O%2bbqMO2kSR0AxYpBFUSliv95y1b5HvbwSjf0bts%2b0Pv6l9NlFYDHoJwYQAF1jUQC05mSNKBmFmfo0G) 4. mail块 nginx的额外功能,也可以当作邮件代理服务器 ![image](https://cdn.res.knowhub.vip/c/2503/31/48304236.png?G1UAAGSd87ygk1r8TjyqCQIJNAMSWQSVEtbrPWftG%2bD7g5E1P6P1GfvDX1qfAYSuQhcwckFB0lzJUVStUjIu1cg9rxE%3d) # Nginx反向代理 ![image](https://cdn.res.knowhub.vip/c/2503/31/d831f04f.png?G1YAAMS22TiV6hlJ29gP%2fkNfQTUDElkElRLW6713n0b0%2faGslp%2fZx4rz4S99rCBhN0glZS0MJA8WlAK4axJDZYVb3jMA) ```csharp location / { proxy_pass http://127.0.0.1:5000/;} ``` ![image](https://cdn.res.knowhub.vip/c/2503/31/89de8872.png?G1YAAMTsdJzIJ6Gh26hD2jvFHc2ARBZBpYT1es9Z%2byb6fhdIis9offr%2b8JfWpxOjJGUjgWQoglewZuOa5QpcSzEGLK7h) ```csharp location /abc { proxy_pass http://127.0.0.1:5000/;}location /def { proxy_pass http://127.0.0.1:5001/;} ``` ## 奇技淫巧:反向代理实现域名免备案 原理:如果你的服务器是国内云服务器,那么通过域名访问就需要备案。但国外服务器不需要。因此可以通过国外服务器的反向代理,实现免备案访问。 ```csharp server { listen 80; server_name xxxx.com;#你的域名 location / { proxy_pass xxxx #你国内服务器的ip } } ``` # Nginx负载均衡 ![image](https://cdn.res.knowhub.vip/c/2503/31/1254ec7e.png?G1cAAETd9Ly0SWuZ7Dvd0Q2cCtoMWKQRVEpYr%2fectW%2bR7w%2bFlvyM1mfsD79pfYYQtRgvUajDkIKB5tVJlERQHXBnXiMA) ```csharp server {listen 80 default_server;listen [::]:80 default_server; upstream TestServer{ server 127.0.0.1:5000; server 127.0.0.1:5001; server 127.0.0.1:5002;}location / { proxy_pass http://TestServer/;} } ``` ## 负载均衡策略 1. 轮询(Round Robin) 原理:这是 Nginx 默认的负载均衡策略。它按顺序依次将客户端请求分发到后端服务器列表中的每一台服务器,当所有服务器都被轮询过一次后,又会重新从第一台服务器开始。 适用场景:服务器性能相近、处理能力相当的场景。 ```csharp upstream backend { server backend1.example.com; server backend2.example.com; } server { location / { proxy_pass http://backend; } } ``` 1. 加权轮询(Weighted Round Robin) 原理:在轮询策略的基础上,为每台后端服务器分配一个权重值。权重值越高的服务器,被分配到请求的概率就越大。这使得性能更强的服务器能够处理更多的请求。 适用场景:硬件配置、性能等存在差异时,使用加权轮询可以更合理地分配请求 ```csharp upstream backend { server backend1.example.com weight=3; server backend2.example.com weight=1; } server { location / { proxy_pass http://backend; } } ``` 1. IP 哈希(IP Hash) 原理:根据客户端的 IP 地址进行哈希计算,将相同 IP 地址的客户端请求始终分发到同一台后端服务器上。这样可以保证客户端与服务器之间的会话一致性。 适用场景:适用于需要保持会话状态的应用,如购物车、用户登录等`单机部署`场景。 ```csharp upstream backend { ip_hash; server backend1.example.com; server backend2.example.com; } server { location / { proxy_pass http://backend; } } ``` 1. 最少连接 原理:将新的客户端请求分发到当前连接数最少的后端服务器上。这样可以确保每台服务器的负载相对均衡,避免某些服务器因连接过多而性能下降。 适用场景:适用于处理请求时间差异较大的场景,比如不同的后端服务器处理复杂业务逻辑的能力不同。 ```csharp upstream backend { least_conn; server backend1.example.com; server backend2.example.com; } server { location / { proxy_pass http://backend; } } ``` 1. 加权最少连接(Weighted Least Connections) 这是目前最优解,综合考虑了服务器性能差异与业务逻辑性能占用差异 原理:结合了加权轮询和最少连接的思想。在选择连接数最少的服务器时,还会考虑服务器的权重。权重高的服务器即使连接数稍多,也更有可能被选中。 适用场景:在后端服务器性能有差异且请求处理时间不同的情况下使用。 ```csharp upstream backend { least_conn; server backend1.example.com weight=3; server backend2.example.com weight=1; } server { location / { proxy_pass http://backend; } } ``` 1. 随机(Random) 原理:随机地将客户端请求分发到后端服务器列表中的某一台服务器上。 适用场景:在对负载均衡的精确性要求不高,且后端服务器性能差异不大的场景中可以使用。 ```csharp upstream backend { random; server backend1.example.com; server backend2.example.com; } server { location / { proxy_pass http://backend; } } ``` 1. 随机加权(Random with Weight) 原理:在随机分配的基础上,考虑服务器的权重。权重高的服务器被随机选中的概率更大。 适用场景:综合了随机和加权的优点,适用于多种复杂场景。 ```csharp upstream backend { random two least-time=header; server backend1.example.com weight=3; server backend2.example.com weight=1; } server { location / { proxy_pass http://backend; } } ``` ## 健康度检查 当我们配置负载均衡后,如果某一台服务器挂了。Nginx不会主动感知服务器是否挂掉,所以请求可能会超时无响应。 因此可以通过 Nginx 的 ngx\_http\_upstream\_module 模块配置`主动健康检查`,或者使用第三方模块(如 ngx\_http\_upstream\_check\_module)来实现更完善的健康检查功能 ```csharp http { upstream backend { # 定义后端服务器 server backend1.example.com; server backend2.example.com; # 配置健康检查 # interval=3000:每隔3秒对后端服务器进行一次健康检查 # rise=2:连续两次检查结果正常时,才标记为可用 # fall=3:连续3次检查结果异常,标记为不可用 # timeout=1000:检查的超时时间 # type=http:使用htpp协议进行检查 check interval=3000 rise=2 fall=3 timeout=1000 type=http; # 对应服务器的检查检查地址 check_http_send "HEAD /healthcheck HTTP/1.0\r\n\r\n"; # 设置当服务器返回2xx 3xx 状态码时,认为服务器正常 check_http_expect_alive http_2xx http_3xx; } server { listen 80; server_name example.com; location / { proxy_pass http://backend; } } } ``` # 动静分离 ![image](https://cdn.res.knowhub.vip/c/2503/31/6a39c346.png?G1cAAETn9LwUCjDNvtMdbIlTE20GLNIIKiWs13vO2jfR94ewWH5G6zP2h9%2b0PoPA1RSFhMVZkYIyDG5mXhMgBkXBldcI) ```csharp http { # 定义后端应用服务器地址 upstream backend { server 127.0.0.1:8080; # 假设后端应用服务器监听 8080 端口 } server { listen 80; server_name example.com; # 替换为你的域名 # 处理静态资源请求 location ~* \.(jpg|jpeg|png|gif|css|js|ico)$ { root /var/www/static; # 静态资源存放目录 expires 30d; # 设置缓存时间为 30 天 add_header Cache-Control "public, no-transform"; } # 处理动态资源请求 location / { proxy_pass http://backend; # 将请求转发到后端应用服务器 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进程机制 Nginx是以多进程的方式来运行。 ![image](https://cdn.res.knowhub.vip/c/2503/31/4883a60a.png?G1YAAMT0bJzotSXKNvqh%2f4lHQjMgkUVQKWG93nv3aUTf7wJJ8Zl9LD8f%2ftLHcmJYUi4kkAxF8ApOChQ2C7VCJVvWuKcD) 1. master进程 管理配置文件、启动 / 停止工作进程、监控子进程状态(worker进程意外退出负责重启)、接收外界信号(nginx -s reload) 2. worker进程 监听客户端请求,具体处理网络请求,多个worker进程之间互相独立,采用异步非阻塞(事件驱动)机制 ![image](https://cdn.res.knowhub.vip/c/2503/31/c8bc7bb5.png?G1cAAER17rxg7TYV%2fU48pgkCCTYDFmkElRLW6%2fn%2fuS6R9wuFer57bSPWh9%2fUNkKI4sZDFLrBkIKB7ju8EIlUmp1unmcP) ## reload 如何实现热更新 1. master进程对配置文件进行语法检查 2. 通过后使用新的配置 3. 修改配置成功后,新建worker进程 4. 新建成功,给旧的worker进程发送关闭信息 5. 旧worker进程收到信号后会继续服务,直到处理完接收到的请求 6. 关闭旧worker进程 ### 眼见为实 ![image](https://cdn.res.knowhub.vip/c/2503/31/4af51ad2.png?G1cAAMTsdJzIJwmq26hD2jvFHc2ARRpBpYT1es9Z%2byb6fheIxWe0Pn1%2f%2bE3r04lxmXIhgSQoQlCwWU2mwoGl1pKhynENBw%3d%3d) 当我们执行nginx -s reload时,会发现worker进程id变了。这说明被重启过。 ## worker进程如何避免竞争 master进程创建后,会建立好要监听的socket。 然后再创建worker进程。因此当worker进程创建成功时,当listenfd新连接到达时,所有worker进程都会一拥而上造成`惊群效应`。 因此nginx使用进程互斥锁来保证只有一个worker进程能够处理请求。来解决惊群效应带来的`fd竞争`。 # 几个常用实践的简单介绍 ## 合并请求 ![image](https://cdn.res.knowhub.vip/c/2503/31/882c83f7.png?G1cAAMTsdJzIJ6Gh26hD2jvFHc2ARRpBpYT1es9Z%2byb6fhdIis9offr%2b8JvWpxOjJGUjgWQoQlBwFsAqc2CzdGWVWuIaDg%3d%3d) 举个例子,当我们打开百度时,光一个js文件旧加载了36个。也就是36个tcp连接。这对服务器的压力也是不小的。 因此,当多个客户端同时请求`相同或相似的资源`时,能否将这些资源合并为一个,从而降低服务器的资源消耗呢? Nginx提供了ngx\_http\_slice\_module/ngx\_http\_v2\_module模块,可以多个用户同时下载大文件,CSS,JS,IMG等静态资源时,将请求合并,从而降低服务器tcp/ip压力与网络带宽 当然,模块还有很多第三方支持,比如nginx-http-concat/ nginx-merge-filter-module/ngx\_http\_combined\_static\_module 等 ```csharp #ngx_http_slice_module http { # 开启请求合并功能 slice 1m; server { listen 80; server_name example.com; location /large-file { # 对 /large-file 路径下的请求启用请求合并 slice 1m; proxy_pass http://backend_server; } } } ``` ```csharp #nginx-http-concat http { server { listen 80; server_name example.com; # 合并 JS 文件 location ~* ^/js/(.*)\.js$ { concat on; # 开启合并功能 concat_types application/javascript; # 指定合并文件类型 concat_max_files 10; # 最多合并 10 个文件 concat_unique on; # 去重相同文件 root /var/www/static; # 文件根目录 } # 合并 CSS 文件 location ~* ^/css/(.*)\.css$ { concat on; concat_types text/css; concat_max_files 5; root /var/www/static; } } } ``` ## 限流 ### 常见限流算法 1. 固定窗口算法 将时间划分为固定窗口,统计窗口内请求数,超过阈值则限流。 优点:实现简单,内存占用低。 缺点:窗口切换时可能触发双倍突发(如窗口最后 1 秒和新窗口前 1 秒各 100 次)。 应用场景:对精度要求不高的轻量级限流。 2. 滑动窗口算法 将时间窗口划分为多个子窗口,统计滑动时间范围内的请求数。 比如将 1 分钟分为 6 个 10 秒的子窗口,统计最近 6 个子窗口的总和。 优点:解决临界问题,限流更平滑。 缺点:实现复杂度较高,需维护多个子窗口数据。 应用场景:高并发且对精度要求高的场景。 3. 漏桶算法 请求进入漏桶后,以固定速率流出,超出容量的请求被丢弃。 优点:有效削峰填谷,保证稳定输出。 缺点:无法利用突发流量,可能降低资源利用率。 应用场景:严格限制平均速率的场景(如 API 接口限流)。 4. 令牌桶算法 以固定速率生成令牌存入桶中,请求需消耗令牌才能通过,允许突发。 优点:支持突发流量,同时控制平均速率。 缺点:实现复杂度高于固定窗口。 应用场景:允许瞬时高峰但需控制整体速率的场景(如 CDN 加速)。 ### 限流模块 1. ngx\_http\_limit\_conn\_module 限制同一客户端的并发连接数,保护服务器资源。 ```csharp http { limit_conn_zone $binary_remote_addr zone=addr:10m; # 按IP分组,共享内存10MB server { location / { limit_conn addr 2; # 每个IP最多2个并发连接 proxy_pass http://backend; } } } ``` 1. ngx\_http\_limit\_req\_module 基于漏桶算法限制请求速率,支持突发请求缓冲。 ```csharp http { limit_req_zone $binary_remote_addr zone=req:10m rate=10r/m; # 每分钟10请求 server { location /api { limit_req zone=req burst=5 nodelay; # 允许5个突发请求 proxy_pass http://api_backend; } } } ``` 1. ngx\_http\_limit\_rate\_module 限制客户端下载速度 ```csharp location /downloads { limit_rate 200k; # 200KB/s alias /data/downloads; } ``` > > > 限流模块还有很多,可以自行查阅AI。 > ## 防盗链 原理:通过Request Header中的referer是否是自己的域名,如果是,说明请求合法。如果不是,说明资源被其它网站盗链 ```csharp server { location ~* \.(jpg|jpeg|png|gif|css|js)$ { valid_referers none blocked example.com www.example.com; if ($invalid_referer) { return 403; } } } ``` ### 反防盗链 防盗链的本质,是设置一个标识,不仅仅是referer,也可以是签名等其它信息。http协议注定了,这些信息都是可以篡改的。 比如直接加入<meta name='referer' content='no-referrer'/ > ,或者通过反向代理篡改referer信息,都是可以破解的 因此,更多的时候,我们都是通过`url签名+有效期控制,访问限流,IP白名单`的形式来进行高级防盗链 ## 压缩 ### gzip gzip是一种广泛使用的数据压缩算法。当客户端向 Nginx 服务器请求资源时,若客户端支持gzip压缩(通过Accept-Encoding请求头表明),Nginx 会对响应内容进行gzip压缩,然后将压缩后的数据发送给客户端。客户端接收到数据后,再进行解压缩。 1. 优点: 兼容性好,几乎所有现代浏览器都支持gzip压缩。 配置简单,Nginx 默认支持gzip模块,无需额外编译。 2. 缺点: 压缩比相对 Brotli 较低,在某些情况下,数据压缩后的大小不如 Brotli。 ```csharp http { # 开启gzip压缩 gzip on; # 最小压缩文件大小,小于该值的文件不进行压缩 gzip_min_length 1k; # 压缩级别,取值1 - 9,值越大压缩比越高,但CPU消耗也越大,一般设置为6 gzip_comp_level 6; # 允许压缩的文件类型 gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; # 为老的代理服务器添加Vary头 gzip_vary on; # 禁用对IE 6以下版本的压缩 gzip_disable "msie6"; server { listen 80; server_name example.com; location / { root /var/www/html; } } } ``` ![image](https://cdn.res.knowhub.vip/c/2503/31/cbbe80a2.png?G1cAAOQ8bZy4F8c26nBtokhoBizSCColrNd77z4N4PudkTU%2bs4%2fl58Nv%2blgOhKZCBRg5oSAEQcoqUrNZILJKiZOUuKcD) ### brotli Brotli 是 Google 开发的一种新的压缩算法,它基于 LZ77 算法的现代变体、霍夫曼编码和二阶上下文建模。与gzip相比,Brotli 通常能提供更高的压缩比,从而进一步减少数据传输大小。 1. 优点: 压缩比高,能显著减少数据传输大小,加快页面加载速度。 对于文本类文件(如 HTML、CSS、JavaScript)的压缩效果尤其明显。 2. 缺点: 兼容性不如gzip,虽然大多数现代浏览器都支持 Brotli,但仍有一小部分旧浏览器不支持。 需要额外安装ngx\_brotli模块,配置相对复杂。 要在 Nginx 中使用 Brotli 压缩,需要安装ngx\_brotli模块 ```csharp http { # 开启Brotli压缩 brotli on; # 压缩级别,取值0 - 11,值越大压缩比越高,但CPU消耗也越大,一般设置为6 brotli_comp_level 6; # 允许压缩的文件类型 brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; server { listen 80; server_name example.com; location / { root /var/www/html; } } } ```