前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版

Nginx极简教程,反向代理负载均衡与跨域常用配置

首页2019-03-10 10:35:08Back-End
Nginx反向代理运维

前端项目打包完扔到服务器上,接下来就是绕不开的 Nginx。配反向代理、配 HTTPS、配跨域、配 SPA 的 history 路由,这几件事几乎每个项目都要做一遍。但网上的配置片段大多是复制来复制去的,注释还停留在好几年前,照抄下来能跑,出了问题就完全不知道该动哪一行。

这篇把我平时用得最多的几套 Nginx 配置整理出来,每个配置块都说清楚它在解决什么问题、哪些参数是关键、哪些是可以不管的。原文里有几处写错的地方(拼错的 header 名、永远匹配不上的跨域判断、注释和实际行为相反的 SSL 配置)我也一并改正并说明了原因。

在本篇文章中,我们将从浅入深,和大家一起学习以下知识:

  • 反向代理和正向代理到底反在哪
  • Nginx 常用命令,以及 -t 为什么每次改配置都该敲一遍
  • 一份完整的 http 反向代理配置,逐段解释每一项
  • 负载均衡的 upstream 配置与几种分发策略
  • 一个域名挂多个 webapp,以及 proxy_pass 结尾那个斜杠的坑
  • https 反向代理需要额外配什么
  • 静态站点配置,以及 SPA 刷新 404 的正确解法
  • 用 autoindex 快速搭一个文件服务器
  • CORS 跨域配置,以及原文那段配置为什么不生效

# 一、概述

# 1.1 什么是 Nginx

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

Nginx 架构与工作模式示意

它出名的地方在于用很小的内存开销扛住很高的并发。原因是它用的是事件驱动加异步非阻塞的模型,一个 worker 进程可以同时处理成千上万个连接,而不是传统那种一个连接一个线程的做法。这也是为什么静态资源交给 Nginx 处理比交给 Node 或者 Java 应用要划算得多。

# 1.2 什么是反向代理

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

这个定义读起来有点绕,我一直觉得用「代理谁」来记最清楚。

正向代理是代理客户端。你翻墙用的那种代理,服务端不知道真实的你是谁,只知道请求是从代理服务器来的。客户端是知情的,是它主动配了代理。

反向代理是代理服务端。客户端以为自己在跟 www.helloworld.com 说话,实际上背后可能是三台机器在轮流回应。客户端完全不知情,它眼里只有那一个入口。

所以反向代理的价值就出来了:隐藏真实的后端拓扑、在入口处统一做 SSL 终止和日志、可以随时增减后端机器而不影响外部、静态资源在这一层就直接返回不用打到应用服务器。

# 二、常用命令

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 -t 是每次改完配置必须敲的。它只做语法检查不重启服务,配置写错了它会告诉你错在第几行。跳过这一步直接 reload,如果配置有语法错误,Nginx 会拒绝加载新配置,虽然老配置还在跑不至于挂,但你以为改生效了实际没有,这种情况排查起来最费时间。

stop 和 quit 的区别在于对现有连接的处理。stop 是立刻终止,正在传输的请求会被掐断;quit 会等当前请求处理完再退出。线上操作一律用 quit。

reload 是热加载,它会用新配置起一批新的 worker 进程,老的 worker 处理完手上的请求后自然退出,整个过程对用户无感。日常改配置用它就够了,基本不需要重启服务。

reopen 用在日志切割之后。你把 access.log 改名归档了,Nginx 手里还攥着原来那个文件句柄,会继续往改了名的文件里写,敲一下 reopen 它才会重新打开新文件。用 logrotate 做日志切割的时候,切完必须配这一步。

如果不想每次都敲命令,可以在 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 脚本,大同小异。不过 Linux 上现在更常见的做法是交给 systemd 管理,systemctl reload nginx 这类命令由发行版的包维护者配好,不用自己写脚本。

# 三、http 反向代理配置

先实现一个小目标:不考虑复杂的配置,仅仅是完成一个 http 反向代理。

conf/nginx.conf 是 Nginx 的默认配置文件,你也可以使用 nginx -c 指定你的配置文件。

先看全局和 events 这两段。

#运行用户
#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 {
    worker_connections 1024;    #单个后台worker process进程的最大并发链接数
}
@前端进阶之旅: 代码已经复制到剪贴板

worker_processes 现在一般直接写 auto,Nginx 会自己按 CPU 核心数来定,比写死数字省心。注意在容器里跑的时候,它读到的可能是宿主机的核心数,如果给容器限了 CPU,这里最好按限额写死。

worker_connections 乘以 worker_processes 就是这台机器的理论最大连接数。1024 这个值在真实业务下偏小,压力上来会看到 worker_connections are not enough 这个错。但光改这个不够,还得同时把系统的文件描述符上限(ulimit -n)提上去,不然改了也白改。这个我踩过,只改了 Nginx 配置没改系统限制,压测时连接数怎么都上不去。

error_log 写了三行不同级别,实际是最后一条生效。Nginx 的 error_log 在同一个层级下只有最后一次声明有效,前面两条是被覆盖掉的。想同时记多个级别,得写在不同的 server 块里。

接着是 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;
@前端进阶之旅: 代码已经复制到剪贴板

include mime.types 这行别删。它负责根据文件后缀设置正确的 Content-Type,删了之后所有文件都按 default_type 也就是 application/octet-stream 返回,浏览器会把 CSS 和 JS 当成二进制文件下载而不是执行。「样式全丢了但资源请求是 200」这种诡异现象,十有八九是 MIME 类型的问题。

log_format 里的 $http_x_forwarded_for 很关键。经过反向代理之后,后端看到的 $remote_addr 是 Nginx 自己的 IP,真实客户端 IP 藏在这个头里。做访问统计和风控的时候取错了字段,你会发现所有请求都来自同一个 IP。

sendfile on 让内核直接把文件从磁盘送到网卡,不经过用户态,省掉两次内存拷贝。传静态文件时收益明显。

gzip on 这行被注释掉了,实际用的时候强烈建议打开。文本类资源(HTML、CSS、JS、JSON)压缩后一般能小到原来的三分之一以下,对首屏加载的影响比大多数前端优化手段都大。后面静态站点那节会给出完整的 gzip 配置。

最后是 upstream 和 server。

fe
  • 一、概述
    • 1.1 什么是 Nginx
    • 1.2 什么是反向代理
  • 二、常用命令
  • 三、http 反向代理配置
  • 四、负载均衡配置
  • 五、一个域名挂多个 webapp
  • 六、https 反向代理配置
  • 七、静态站点与 SPA 路由
  • 八、搭建文件服务器
  • 九、跨域解决方案
  • 总结
  • 参考

← Vue CLI 3 项目构建基础 vue.config.js 配置详解重新认识Koa2,洋葱模型与ctx到底是怎么实现的 →