初识Nginx
概述
-
Nginx适用于哪些场景:
-
- 静态资源服务
- 通过本地文件系统提供服务- 反向代理服务
- Nginx的强大性能
- 缓存
- 负载均衡
- Api服务
- OpenResty
- OpenResty
- 反向代理服务
- 静态资源服务
-
Nginx出现的历史背景(Nginx为什么出现?)
-
低效的Apeche:一个连接对应一个进程,处理完一个请求才会处理下一个,无法支持“高并发”。
-
新增服务器绝大多数都是Nginx
-
-
Nginx的优点
- 高并发 & 高性能
- 主流服务器32核64G可达数千万并发连接(RPS, Requests Per Second)
- 处理静态资源请求可达一百万RPS
- 可拓展性好
- 高可靠性
- 可持续运行数年
- 热部署
- 不停止服务,升级Nginx
- BSD许可证
- 可定制Nginx且商用
- 高并发 & 高性能
-
Nginx的四个组成部分:
- Nginx二进制可执行文件(由各模块源码编译出的一个文件)
- Nginx.conf配置文件(控制Nginx的行为)
- access.log访问日志(记录每一天http请求)
- error.log错误日志(定位问题)
安装Nginx
-
安装方式:
- apt/yum
- 编译安装:方便安装第三方模块
-
选择哪一个Nginx发行版:
- 开源版即可
- Mainline:有新功能
- Stable:稳定版
编译安装
- 下载地址:nginx.org
- 官方文档:nginx docs
- 下载:
wget http://nginx.org/download/nginx-1.14.2.tar.gz
tar -xzf nginx-1.14.2.tar.gz
cd nginx-1.14.2 # 进入源码目录
-
源码目录结构
-
auto目录
:编译、lib库、操作系统判断 -
CHANGS文件
:功能改动和BUGFIX文档 -
conf文件
:conf配置示例 -
configure脚本
:执行编译前的动作 -
contrib目录
:vim不支持nginx语法# 配置vim,支持nginx语法 cp -r contrib/vim/* ~/.vim/
-
html目录
:html示例50x.html
:500错误示例index.html
:首页示例
-
man文件
:帮助文件 -
src目录
:源代码
-
-
configure
支持的参数:./configure --help | more
# 安装在/opt/nginx目录,可能报错,安装对应依赖即可
./configure --prefix=/opt/nginx
# 编译
make
# 首次安装使用
make install
-
升级Nginx时,不能执行make install:
- 把
objs/nginx
复制进安装目录中 - 安装动态模块的中间文件也在
objs目录
下
- 把
-
安装完成后,
/opt/nginx
的结构- sbin:nginx程序
- conf:nginx配置,均复制于
/nginx/nginx-1.14.2
- logs:日志
-
可将nginx目录配置进环境变量,方便使用:
export PATH="$PATH:/opt/nginx/sbin"
Nginx配置文件的通用语法
-
配置文件由指令与指令块构成
-
每条指令以
;
分号结尾,指令与参数间以空格符号分割。 -
指令块以
{}
大括号将多条指令组织在一起 -
include语句允许多个配置文件以提升可维护性
-
使用
#
符号添加注释,提高可读性 -
使用
$
符号使用变量 -
部分指令的参数支持正则表达式
-
配置参数:时间单位与空间单位
-
http配置的指令块
Nginx命令行
- 格式:
nginx -s command
:nginx -s reload
- 帮助:
-? -h
- 使用指定配置指令:
-g
- 指定运行目录:
-p
- 发送信号:
-s
- 立刻停止服务:
stop
- 优雅地停止服务:
quit
- 优雅地重载配置文件:
reload
- 重新开始记录日志文件:
reopen
,常用于日志切割
- 立刻停止服务:
- 测试配置文件是否有语法错误:
-t -T
- 打印nginx的版本信息、编译信息等:
-v -V
热部署
# 替换nginx二进制执行文件
cp /home/jay/lxj/nginx-1.14.2/objs/nginx /opt/nginx/sbin/nginx -f
# 发送信号给nginx,13426为nginx master的PID,结果如示例
kill -USR2 13426
# 优雅地关闭老nginx进程
kill -WINCH 13426
# 老master不会关闭,可reload再次 拉起新worker(我没有实现)
- 示例:新老nginx同时运行,但老nginx不再监听对应端口80/443
Nginx搭建静态资源服务器
- 在nginx.conf中配置
alias
类似root
,但更常用。- alias:url路径与指定目录路径相同。
# 静态文件服务器
server {
listen 8080;
server_name geek.lxj.pub;
# charset koi8-r;
access_log logs/geek.access.log main;
location / {
alias dlib/; # 根目录与dlib/后相同
autoindex on;
set $limit_rate 1k;
index index.htm;
}
#error_page 404 /404.html;
}
-
即可在浏览器中获取
dlib
目录下对应路基的文件:http://10.211.55.8:8080/MyGit/django_course/Day01/01-animate.html
-
autoindex模块:把文件目录结构分享给访问者。
location / {
autoindex on;
}
-
set $limit_rate 1k;
:限制Nginx对浏览器的响应速度,避免个别大文件占满带宽。示例为 每秒1K。 -
配置access日志:
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
用Nginx搭建一个具备缓存功能的反向代理服务
- 把静态服务器作为“上游服务”
- 通过Nginx反向代理
- 方便通过“负载均衡”增加“上游”服务器,来提升处理性能。
- 也方便在上游服务出现问题时,Nginx把请求转交给正常服务器。
# 静态服务(上游服务)
server {
listen 127.0.0.1:8080; # 只能本机访问,上游服务不对外提供服务
server_name geek.lxj.pub;
# charset koi8-r;
access_log logs/geek.access.log main;
location / {
alias dlib/;
autoindex on;
set $limit_rate 1k;
#index index.htm;
}
#error_page 404 /404.html;
}
# 反向代理
include vhost/*.conf;
# 上游服务
upstream local {
server 127.0.0.1:8080;
}
server {
server_name geektime.lxj.pub; # 需要配置本地的/etc/hosts文件
listen 80;
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;
proxy_cache my_cache; # 再配置好缓存后,打开缓存功能
proxy_cache_key $host$uri$is_args$args; # 配置key:用户+资源等=整体key
proxy_cache_valid 200 304 302 1d; # 这些不处理
proxy_pass http://local; # 默认监听80
}
}
配置Nginx缓存
proxy_cache_path /tmp/nginxcache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off
:指定路径,配置10MB的缓存。
注意
- Nginx反向代理和上游服务应该配置为两台Nginx。
- 关闭上游服务后,开启缓存的反向代理Nginx还可以返回缓存的上游服务的内容。
用免费SSL证书实现一个HTTPS站点(没实现)
- 背景:编译安装nginx-2
# 安装工具
sudo apt install python-certbot-nginx
# 生成证书
certbot --nginx --nginx-server-root=/opt/nginx-2/conf/ -d geektime.lxj-2.pub
Nginx架构基础
-
Nginx的请求处理流程
-
Nginx进程结构
-
多进程结构:多线程会共用地址空间,发生地址越界时 会导致多进程挂掉,不利于Nginx的“健壮性”。
-
进程间通讯使用“共享内存”。
-
Worker进程:每个W进程与一个CPU绑定,方便使用每个CPU缓存来减少“缓存失效”的命中率。
使用信号管理Nginx的父子进程
Nginx进程管理:信号
- Master进程:
- 监控worker进程:CHLD
- 管理worker进程
- 接收信号:
- TERM, INT:立刻停止Nginx进程
- QUIT:优雅停止,不立刻断开用户的连接
- HUP:重载配置文件
- USR1:重新配置日志文件,用于切割日志
- USR2:
kill USR2 NGINX_PID
,用于热部署 - WINCH:
kill WINCH NGINX_PID
,用于热部署
- Worker进程
- 接收信号:不直接对worker进程发送信号,一般通过master进程进行管理
- TERM, INT
- QUIT
- USR1
- WINCH
- 接收信号:不直接对worker进程发送信号,一般通过master进程进行管理
- nginx命令行:本质是发送信号
- reload:HUP
- reopen:USR1
- stop:TERM
- quit:QUIT
reload重载配置文件的真相
-
reload的流程:
- 想master进程发送HUP信号(同reload命令)
- master进程校验配置语法是否正确
- master进程打开新的监听端口
- master进程用新配置启动新的worker子进程
- master进程向老worker子进程发送QUIT信号
- 老worker进程关闭监听句柄,处理完当前连接后结束进程
-
不停机载入新配置
热升级的完整流程
-
流程:
- 将就Nginx文件替换成新Nginx文件(注意备份)
- 想master进程发送USR2信号
- master进程修改PID文件名,加后缀.oldbin
- master进程用心Nginx文件启动新master进程
- 向老master进程发送QUIT信号,关闭老master进程
- 回滚:向老master发送HUP,向新master发送QUIT
-
不停机更新Nginx二进制文件
优雅地关闭worker进程
- 针对HTTP请求:因为Nginx无法识别TCP、WeSocket连接一共需要多少报文才能完成该连接。
- 使用方法:配置定时器(woker_shutdown_timeout),其内部过程如下
- 关闭句柄
- 关闭空闲连接
- 在循环中等待全部连接关闭
- 退出进程
网络收发与Nginx事件的对应关系
-
网络传输:以一次GET请求为例
-
TCP流与报文:每个“小报文”(MSS)即为一次“网络事件”
-
TCP协议与非阻塞接口:左读右写
29-41 (# TODO)
HTTP模块
冲突的配置指令以谁为准?
-
指令的合并
- 值指令(存储配置项的值):可以合并,如root, access_log, gzip
- 动作类指定(指定行为):不可合并,
- 如rewrite, proxy_pass
- 生效阶段server_rewrite阶段, rewrite阶段,content阶段
-
储存值的指令继承规则:向上覆盖
- 子配置不存在时,直接使用父配置块;子配置存在时,直接覆盖父配置块。
server {
listen 8080; # 只能出现在server的context中
root /home/geek/nginx/html;
access_log logs/geek.access.log main;
location /test {
root /home/geek/nginx/test; # 覆盖父配置
access_log logs/access.test.log main; # 覆盖父配置
}
location /dlib {
alias dlib/;
}
location / { # 使用父配置块的root
}
}
- HTTP模块合并配置的实现:
- 指令在哪个块下生效?
- 指令允许出现在哪些块下?
- 在server块内生效,从http向server合并指令:
char *(*merge_srv_conf)(ngx_conf_t*cf, void *prev, void *conf);
- 配置缓存在内存:
char *(*merge_loc_conf)(ngx_conf_t*cf, void *prev, void *conf);
Listen指令的用法
-
语法
-
示例
# 监听套接字
listen unix:/var/run/nginx.sock;
# 监听ip地址和端口
listen 127.0.0.1:8000;
listen 8000;
listen *:8000; # 所有ip
listen localhost:8000 bind;
listen [::]:8000 ipv6only=on; # 只ipv6
listen [::1]; #???
Nginx中的正则表达式
-
元字符与重复
-
示例
-
测试工作:使用pcretest
如何找到处理请求的Server指令块
- server_name指令
- 指令后可以跟多个域名,第1个是主域名
Syntax server_name_in_redirect on | off;
Default server_name_in_redirect off;
Context http, server, location
-
*泛域名:仅支持在最前面或者最后
server_name *.taohui.tech
-
正则表达式:加~前缀
server_name www.taohui.tech ~^www\d+\.taohui\.tech$;
-
Server匹配顺序:
- 精确匹配
- *在前的泛域名
- *在后的泛域名
- 按文件中的顺序匹配正则表达式域名
- default server:第1个,listen指定default
-
示例
- 配置
server { server_name primary.lxj.tech second.lxk.tech; server_name_in_redirect on; return 302 /redirect; }
- 开启后,指向主域名
详解HTTP请求的11阶段
- 流程图:(右图为 阶段对应指令块)
11个阶段的顺序处理
- 示意图
postread阶段:realip模块获取真实ip地址
-
如何拿到真实的用户IP地址?
- TCP连接四元组(src ip, src port, dst ip, dst port)
- HTTP头部X-Forwarded-For用于传递IP:可多个
- HTTP头部X-Real-IP用于传递用户IP:只有一个
- 网络中存在许多反向代理(前提)
-
示例场景
-
拿到真实用户IP后如何使用?
- 基于变量
- 如binary_remote, remote_addr这样的变量,其值就为真实的IP。这样做链接限制(limit_conn模块)才有意义。
realip模块
-
realip模块
- 默认不会编译进Nginx:通过
--with-http_realip_module
启用功能 - 功能:修改客户端地址
- 指令:set_real_ip_from, real_ip_header, real_ip_recursive
- 变量:realip_remote_addr, realip_remote_port
- 默认不会编译进Nginx:通过
-
realip模块的指令
-
示例配置
server {
server_name realip.lxj.tech;
error_log logs/myerror.log debug;
set_real_ip_from 10.211.55.8;
#real_ip_header X-Real-IP;
real_ip_recursive off;
#real_ip_recursive on;
real_ip_header X-Forwarded-For;
location /{
return 200 "Client real ip: $remote_add\n";
}
}
反向代理与负载均衡
-
负载均衡示例图
-
Nginx在AKF扩展立方体上的应用
-
反向代理
-
反向代理与缓存
负载均衡策略:round-robin
-
指定上游服务地址的upstream与server指令
- 指定一组上游服务器地址,其中地址可以是域名、IP或者unix socket地址。可以在域名或者IP地址后加端口,如果不加端口,那么默认使用80端口。
- 通用参数:
- backup:指定当前server为备份服务,仅当非备份server不可以时,请求才会转发到该server
- down:标识某服务已经下线,不提供服务
-
加权Round-Robin负载均衡算法
- 功能:在加权轮询的方式访问server指令指定的上游服务。集成在Nginx的upstream框架中。
- 指令:
- weight:服务访问的权重,默认是1。
- max_conns:server的最大并发连接数,仅作用于但worker今次。默认是0,表示没有限制。
- 在fail_timeout时间段内,最大的失败次数。当达到最大失败时,会在fail_timeout秒内这台server不允许再次被选择。
- fail_timeout:单位为秒,默认值为10秒。具有2个功能:指定一段时间内,最大失败次数max_fails。达到max_fails后,该server不能访问的时间。
-
对上游服务使用keepalive长连接
-
功能:通过复用连接,降低nginx与上游服务器建立、关闭连接的消耗,提升吞吐量的同时降低时延。
-
模块:ngx_http_upstream_keepalive_module,默认编译进nginx。
-
对上游连接的http头部设定:
proxy_http_version 1.1; proxy_set_header Connetion "";
-
upstream_keepalive的指令
-
-
指定上游服务域名解析的resolver指令
负载均衡哈希算法:ip_hash与hash模块
- 基于客户端IP地址的Hash算法实现负载均衡:upstream_ip_hash模块
- 功能:以客户端的IP地址作为hash算法的关键字,映射到特定的上游服务器中。
- IPV4地址使用前3个字节作为关键字,对IPV6则使用完整地址。
- 可以基于realip模块修改用于执行算法的IP地址。
- 模块:ngx_http_upstream_ip_hash_module。
- 语法:ip_hash, 用于upstream块。
- 功能:以客户端的IP地址作为hash算法的关键字,映射到特定的上游服务器中。