前端视角:Nginx从入门到实践一
文章首发:https://fujia.site
在最开始,我们先明确一个认知,在大前端的环境下,Nginx不仅是运维工程师和后端工程师需要掌握的基础技能,也成为了前端工程的核心技能之一。 当然,根据「最小化」原则和自身情况,从前端视角来看,掌握的深度需要根据自身情况来调整。
前言
最近在个人站点(https://fujia.site)上,添加了基于egg.js + GitHub的第三方登录功能,千辛万苦的终于在本地调通了,结果部署到服务器上,cookies竟然失效了,简单梳理了下,就发现本地和服务器上区别就在于多了一层Nginx层,首要考虑问题是Nginx的配置问题,且在egg.js的官网上对这一块也是有说明的,见 https://www.eggjs.org/zh-CN/tutorials/passport :
如应用部署在 Nginx/HAProxy 之后,需设置插件 proxy 选项为 true, 并检查以下配置:代理附加 HTTP 头字段:x-forwarded-proto 与 x-forwarded-host 配置中 config.proxy 应设置为 true
于是开始调试Nginx的配置,由于没有系统学习Nginx基础语法, 因语法问题导致多次部署失败,就正好趁此机会将相关内容梳理下。
Nginx介绍
Nginx 是开源、高性能、高可靠的 Web 和反向代理服务器,而且支持热部署,几乎可以做到 7 * 24 小时不间断运行,即使运行几个月也不需要重新启动,还能在不间断服务的情况下对软件版本进行热更新。性能是 Nginx 最重要的考量,其占用内存少、并发能力强、能支持高达 5w 个并发连接数,最重要的是,Nginx 是免费的并可以商业化,配置使用也比较简单。
使用场景:
静态资源服务器,通过本地文件系统提供服务;
反向代理,进一步延伸如:缓存,负载均衡等;
API服务。
Nginx和node.js的很多理念类似,如:http服务器、事件驱动以及异步非阻塞等,但各自有自己擅长的领域,Nginx擅长底层服务器端资源的代理,node.js更擅长上层具体业务逻辑的处理。
正向代理和反向代理
反向代理(Reverse Proxy)对应的是正向代理(Forward Proxy),他们的区别:
正向代理: 一般的访问流程是客户端直接向目标服务器发送请求并获取内容,使用正向代理后,客户端改为向代理服务器发送请求,并指定目标服务器(原始服务器),然后由代理服务器和原始服务器通信,转交请求并获得的内容,再返回给客户端。正向代理隐藏了真实的客户端,为客户端收发请求,使真实客户端对服务器不可见;
举个具体的例子 ,你的浏览器无法直接访问谷哥,这时候可以通过一个代理服务器来帮助你访问谷哥,那么这个服务器就叫正向代理。
反向代理: 与一般访问流程相比,使用反向代理后,直接收到请求的服务器是代理服务器,然后将请求转发给内部网络上真正进行处理的服务器,得到的结果返回给客户端。反向代理隐藏了真实的服务器,为服务器收发请求,使真实服务器对客户端不可见。一般在处理跨域请求的时候比较常用。现在基本上所有的大型网站都设置了反向代理。
举个具体的例子 ,去饭店吃饭,可以点川菜、粤菜、江浙菜,饭店也分别有三个菜系的厨师 ,但是你作为顾客不用管哪个厨师给你做的菜,只用点菜即可,小二将你菜单中的菜分配给不同的厨师来具体处理,那么这个小二就是反向代理服务器。
简单地说,一般给客户端做代理的都是正向代理,给服务器做代理的就是反向代理。
安装
Mac M1 下安装
安装和校验
arch -arm64 brew install nginx
brew info nginx
nginx: stable 1.21.6, HEAD
HTTP(S) server and reverse proxy, and IMAP/POP3 proxy server
https://nginx.org/
/opt/homebrew/Cellar/nginx/1.21.6_1 (26 files, 2.2MB) *
Poured from bottle on 2022-03-26 at 09:24:26
From: https://mirrors.ustc.edu.cn/homebrew-core.git/Formula/nginx.rb
License: BSD-2-Clause
==> Dependencies
Required: [email protected] ✔, pcre2 ✔
==> Options
--HEAD
Install HEAD version
==> Caveats
Docroot is: /opt/homebrew/var/www
The default port has been set in /opt/homebrew/etc/nginx/nginx.conf to 8080 so that
nginx can run without sudo.
nginx will load all files in /opt/homebrew/etc/nginx/servers/.
To restart nginx after an upgrade:
brew services restart nginx
Or, if you don't want/need a background service you can just run:
/opt/homebrew/opt/nginx/bin/nginx -g daemon off;
NOTE: 可以从上面的信息找到很多基础的配置信息。
TIPS:一般来说,安装好nginx之后,我们主要关注两个文件夹
/etc/nginx/conf.d/ 文件夹,是进行子配置的配置项存放处,/etc/nginx/nginx.conf 主配置文件会默认把这个文件夹中所有子配置项都引入。
windows下,是对应的安装目录下的conf目录。Mac M1下,是/opt/homebrew/etc/nginx/servers目录。
/usr/share/nginx/html/ 文件夹,通常静态文件都放在这个文件夹
windows下, 对应的目录是在安装目录下的html目录。Mac M1下,是/opt/homebrew/var/www。
其它命令
sudo nginx 启动服务
sudo nginx -s stop 停止服务(直接走)
sudo nginx -s reload 重新加载
sudo nginx -s reopen 重新启动
sudo nginx -s quit 退出(处理完事情走)
open /opt/homebrew/etc/nginx/ 查看nginx安装目录
max下使用brew安装的nginx的命令如下:
tips: 使用brew services -h查看brew services的使用帮助。
# 查看服务运行列表
brew services list
# nginx none
brew services start nginx
# 现在可以在浏览器中访问:http://127.0.0.1:8080/
使用
配置语法校验
使用下面命令:
nginx -t -c /usr/local/nginx/conf/nginx.conf
-c: 指定需要检验的配置文件
-t: 测试配置文件是否正确,在运行时需要重新加载配置的时候,此命令非常重要,用来检测所修改的配置文件是否有语法错误。
语法
这里只讨论一些基础常用的配置语法。
配置语法
配置文件由指令与指令块组成。
每条指令以分号(;)结尾,指令与参数间以空格符号分割,值参数可以是一个或多个附加参数,取决于解析该条指令的模块。
指令块以大括号({})将多条指令组织在一起。
使用#添加注释。
使用$符号使用变量。
部分指令参数支持正则表达式。
一个示例,执行命令cat /opt/homebrew/etc/nginx/nginx.conf:
# worker进程运行的用户和组,如果没有提供则使用nginx的master进程的用户和用户组
#user nobody;
# nginx进程数,一般和cpu核数一致
worker_processes 1;
# 错误日志,级别有debug, info, notice, warn, error和crit(debug记录了全部日志,crit仅报告关键错误)
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
# 每个进程允许的最大并发数
worker_connections 1024;
}
http {
# 引入其它配置,mime.types文件存储的是文件扩展名与类型映射表
include 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 logs/access.log main;
# 开启sendfile配置提高文件的传输速率
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
# 指定KeepAlive的超时时间,即TCP可以保持的时长。
keepalive_timeout 65;
# 开启gzip压缩
#gzip on;
# 服务器配置
server {
# 监听套接字使用的端口号
listen 8080;
# 指定一个或多个主机名
server_name localhost;
#charset koi8-r;
# 开启访问日志
#access_log logs/host.access.log main;
# 访问地址
location / {
root 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 html;
}
#location ~ /\.ht {
# deny all;
#}
# 另一个虚拟主机使用基于IP,名称和端口的混合配置
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server - https服务配置
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# 加载子配置
include servers/*;
}
tips: 可以在阿里云、腾讯云等云服务器商申请免费的SSL证书,一般使用期是一年,个人开发者足够使用。
说明:
nginx收到http请求时,根据请求header中Host值与所有server区段比较,第一个与主机名匹配的server块将被命中,如果没有server区段与客户端请求的主机名匹配,nginx会选择第一个server区段,匹配监听参数(如listen *:80), 另外具有default选项的listen会被优先选择。
sendfile,指定nginx是否调用sendfile函数来输出文件,减少内核空间和用户空间的上下文切换。对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。
备注:
nginx指令
文档:http://nginx.org/en/docs/
Nginx的指令非常多,不建议死记硬背,使用时查询官方文档即可,实际上,可以通过模块名称大致了解到其功能。
常用的模块:
Alphabetical index of variables - nginx提供的所有变量。
Core functionality - 核心功能,主要针对main/event context的设置。
使用nginx -V查看nginx的配置参数,看除了核心模块之外还添加了哪些模块。
以ngx_http_gzip_module举个例子:
http://nginx.org/en/docs/http/ngx_http_gzip_module.html
该模块表示是否启用响应的gzip压缩。
示例配置:
gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain application/xml;
$gzip_ratio变量可以记录实现的压缩率。
指令语法:
Syntax: gzip on | off;
Default: gzip off;
Context: http, server, location, if in location
Syntax - 表示使用语法;
Default - 默认情况下未使用;
Context - 表示该指令可以出现的位置,如上面表示可以出现在location、http和server指令块中。
如果块指令可以在括号内包含其它指令,则称其为context。
一个指令出现在多个指令块中,哪个会生效呢?
Nginx中指令继承规则是向上覆盖。 当子配置存在时,直接覆盖父配置,子配置不存在才使用父配置块或默认配置。
指令都有作用域。
配置(指令)块能相互嵌套。在某些情况下不同配置块能够相互嵌套,如在http区段,可以声明一个或多个server区段,server区段又可以插入一个或多个location区段。
字符串的值,如果指令值中包含空格、分号或者是大括号等特殊字符,需要使用单引号或双引号将其括起。
基本模块
Nginx是由一系列模块组成的,大致可以分为:
核心模块
http核心模块
其它模块
核心模块在编译时不能被禁用。 核心模块包含:main模块,events模块和includes包含指令。
main模块位于配置文件顶层,提供如:进程管理和安全的能力,使用说明见上面。
events模块,事件模块提供的指令可以用来配置网络机制。
accept_mutex:on; # 默认值on,启用或禁用使用一个接受互斥锁来打开套接字监听
use epoll; # 指定nginx所使用的网络事件模型,可选值有/dev/poll,epoll,kqueue等,通常不需要显式指定它,默认情况下nginx将使用最有效方法
worker_connections 1024; # 定义一个worker进程能够同时连接的数量
include指令,用来包含子配置配置文件,在配置文件的任何地方均可插入该指令。注意,如果没有指定绝对路径,那么文件路径将和配置文件的目录相关。
参考资料
官网 - https://nginx.org/ 。
中文网 - https://www.nginx.cn/doc/ 。
Nginx基础语法 - https://zhuanlan.zhihu.com/p/340295336 。
4. 你不知道的 Nginx - https://zhuanlan.zhihu.com/p/139948268 。
大家加油 :)