Nginx高级篇
一、Nginx扩容
1、前言
扩容包括单机垂直扩容和水平扩展
2、单机垂直扩容:硬件资源增加
- 云服务资源增加
- 整机:IBM、浪潮、DELL、HP等
- CPU/主板:更新到主流
- 网卡:10G/40G网卡
- 磁盘:SAS(SCSI) HDD(机械)、HHD(混合)、SATA SSD、PCI-e SSD、 MVMe SSD
- SSD
- 多副本机制
- 系统盘/热点数据/数据库存储
- HDD
- 冷数据存储
3、水平扩展:集群化
4、Nginx模块升级
1 | # nginx下载地址 |
二、Nginx高级配置介绍
1、会话管理与sticky模块
1 | upstream httpds { |
1.1 介绍
Sticky是nginx的一个模块,它是基于cookie的一种nginx的负载均衡解决方案,通过分发和识别cookie,来使同一个客户端的请求落在同一台服务器上,默认标识名为route
使用参考**:**http://nginx.org/en/docs/http/ngx_http_upstream_module.html#sticky
tengine中有session_sticky模块我们通过第三方的方式安装在开源版本中。sticky是第三方模块,需要重新编译Nginx,他可以对Nginx这种静态文件服务器使用基于cookie的负载均衡
1.2 下载与安装
项目官网:https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/src/master/
下载:https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/get/1.2.6.zip
上传解压到服务器,然后重新编译Nginx,依赖openssl-devel,进到源码目录重新编译
1 | #重新编译 |
配置方法
1 | upstream httpget { |
1.3 错误处理
如果遇到以下错误,可能是版本问题
打开 ngx_http_sticky_misc.c文件,在12行添加
1 | #include <openssl/sha.h> |
备份之前的程序mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old
把编译好的Nginx程序替换到原来的目录里(平滑升级也是如此)cp objs/nginx /usr/local/nginx/sbin/
升级检测make upgrade
检查程序中是否包含新模块nginx -V
2、KeepAlive
2.1 介绍
官网:https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive
在http协议header中可以看到当前连接状态,使用KeepAlive可以在有效期内复用Tcp连接。举个例子,当进行TCP连接后,如果在 65s 内没有活动,则关闭长连接,即在点击一个链接后,在65s内没有点击另一个链接,则会关闭当前TCP连接,如果在65s内点击了其它链接,则会复用当前的TCP连接,不用进行三次握手。
什么时候使用
- 明显的预知用户会在当前连接上有下一步操作
- 复用连接,有效减少握手次数,尤其是https建立一次连接开销会更大
什么时候不用
- 访问内联资源一般用缓存,不需要keepalive
- 长时间的tcp连接容易导致系统资源无效占用
2.2 测试工具charles
抓包工具,可以查看传输次数,模拟重放等
下载地址:https://www.charlesproxy.com/assets/release/4.6.2/charles-proxy-4.6.2-win64.msi?k=fc1457e312
官网:https://www.charlesproxy.com
2.3 KeepAlive使用
1 | http { |
2.4 对上游服务器使用keepalive
首先需要配置使用http1.1协议。以便建立更高效的传输,默认使用http1.0,在http1.0中需要配置header才可以。在Upstream中所配置的上游服务器(即Tomcat)默认都是用短连接,即每次请求都会在完成之后断开,因此需要请求复用
1 | upstream tomcat-web { |
2.5 AB压测
ab 是 ApacheBench 命令的缩写。会创建多个并发线程访问,模拟多个访问者同时对某一 URL 地址进行访问。它的测试目标是基于 URL 的,因此,它既可以用来测试 apache 的负载压力,也可以测 试nginx、lighthttp、tomcat、IIS 等其它 Web 服务器的压力
1 | # linux安装 |
参数说明:
- -n 即requests,用于指定压力测试总共的执行次数
- -c 即concurrency,用于指定的并发数
- -t 即timelimit,等待响应的最大时间(单位:秒)
- -b 即windowsize,TCP发送/接收的缓冲大小(单位:字节)
- -p 即postfile,发送POST请求时需要上传的文件,此外还必须设置-T参数
- -u 即putfile,发送PUT请求时需要上传的文件,此外还必须设置-T参数
- -T 即content-type,用于设置Content-Type请求头信息,例如:application/x-www-form-urlencoded,默认值为text/plain
- -v 即verbosity,指定打印帮助信息的冗余级别
- -w 以HTML表格形式打印结果
- -i 使用HEAD请求代替GET请求
- -x 插入字符串作为table标签的属性
- -y 插入字符串作为tr标签的属性
- -z 插入字符串作为td标签的属性
- -C 添加cookie信息,例如:“Apache=1234”(可以重复该参数选项以添加多个)
- -H 添加任意的请求头,例如:“Accept-Encoding: gzip”,请求头将会添加在现有的多个请求头之后(可以重复该参数选项以添加多个)
- -A 添加一个基本的网络认证信息,用户名和密码之间用英文冒号隔开
- -P 添加一个基本的代理认证信息,用户名和密码之间用英文冒号隔开
- -X 指定使用的和端口号,例如:“126.10.10.3:88”
- -V 打印版本号并退出
- -k 使用HTTP的KeepAlive特性
- -d 不显示百分比
- -S 不显示预估和警告信息
- -g 输出结果信息到gnuplot格式的文件中
- -e 输出结果信息到CSV格式的文件中
- -r 指定接收到错误信息时不退出程序
- -h 显示用法信息,其实就是ab -help
3、反向代理核心流程
参考:https://nginx.org/en/docs/stream/ngx_stream_proxy_module.html
3.1 UpStream工作流程
proxy_pass 向上游服务器请求数据共有6个阶段
- 初始化
- 与上游服务器建立连接
- 向上游服务器发送请求
- 处理响应头
- 处理响应体
- 结束
3.2 服务端和客户端的限制
针对上游服务端的限制,可以在http/server/location配置,一般在location里进行配置
1 | # 设置header |
针对客户端的限制,可以在http/server/location中进行配置,可以针对不同请求分别进行配置
1 | # 对客户端请求中的body缓冲区大小。默认32位8k 64位16k |
3.3 重试机制
参考文档:http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream
1 | upstream tomcat-web { |
3.4 上游服务器健康检查
tengine版:https://github.com/yaoweibin/nginx_upstream_check_module
nginx商业版:http://nginx.org/en/docs/http/ngx_http_upstream_hc_module.html
tengine需要版本匹配,具体可以上网找,在nginx目录patch -p1 < /home/nginx_upstream_check_module-master/check_1.20.1+.patch
1 | upstream backend { |
4、资源压缩
4.1 gzip动态压缩
1 | # 开关,默认关闭 |
4.2 gzip静态压缩
参考:https://nginx.org/en/docs/http/ngx_http_gunzip_module.html
https://nginx.org/en/docs/http/ngx_http_gunzip_module.html
ngx_http_gzip_static_module
模块允许发送带有“ .gz”文件扩展名的预压缩文件;ngx_http_gunzip_module
帮助不支持gzip的客户端解压本地文件(帮助节省磁盘空间),也可以直接传输事先在本地压缩好的文件,需要重新编译nginx
1 | ./configure --with-http_gzip_static_module \ |
一般和gunzip配合使用,这样就不需要在客户端进行解压;
1 | # 和下面always配合 |
4.3 Brotli压缩
Brotli 是 Google 开发的一种压缩格式,它通过内置分析大量网页得出的字典,实现了更高的压缩比率,同时几乎不影响压缩 / 解压速度。它可以和nginx本身的gzip共存
官网:https://github.com/google/ngx_brotli / https://github.com/google/brotli
1 | # 下载两个项目,然后解压 |
5、Nginx并发限制
官方文档:http://nginx.org/en/docs/http/ngx_http_limit_req_module.html
测试工具:https://jmeter.apache.org/
5.1 漏桶算法限制
配置文件
1 | # 漏桶算法 |
5.2 令牌桶限制带宽
1 | localtion /{ |
5.3 计数器算法限制并发数
1 | # 计数器算法 |
6、Nginx日志
6.1 ngx_http_empty_gif_module
参考文档:http://nginx.org/en/docs/http/ngx_http_empty_gif_module.html
该模块返回的是一个像素点的图片,一般嵌入到前端,用来收集用户操作信息等
1 | location = /_.gif { |
6.2 ngx_http_log_module
参考文档:http://nginx.org/en/docs/http/ngx_http_log_module.html
errorlog:http://nginx.org/en/docs/ngx_core_module.html#error_log
1 | # 在http模块,默认开启 |
6.3 Json日志格式举例
1 | log_format ngxlog json '{"timestamp":"$time_iso8601",' |
6.4 日志分隔
- 脚本分隔
- **logrotate **
1 | # 一般都会有安装,vim /etc/logrotate.d/nginx |
6、Concat请求合并与资源静态化
6.1 请求合并
Tengine是由淘宝网发起的Web服务器项目。ngx_http_concat模块就是其中之一,作用为在同一个请求里返回多个文件拼接,使用??表示,js/??a.js,b.js,c.js
Nginx官方介绍:https://www.nginx.com/resources/wiki/modules/concat/
git地址:https://github.com/alibaba/nginx-http-concat
下载源码解压缩编译安装,下面是配置
1 | location /static/css/ { |
6.2 资源静态化
解决的方法
- 高并发系统资源静态化方案
- 一致性问题
- 合并文件输出
- 集群文件同步
SSI模块合并资源静态化
利用SSI就可以解决,在静态页面中嵌入个人信息的动态页,由于是服务器端的嵌入,所以用户浏览的时候都是一个嵌入后的页面
官方文档:http://nginx.org/en/docs/http/ngx_http_ssi_module.html
1 | # 是否打开ssi,默认是关闭 |
6.3 Rsync静态文件同步方案
1. rsync介绍
remote synchronize是一个远程数据同步工具,可通过 LAN/WAN 快速同步多台主机之间的文件。也可以使用 rsync 同步本地硬盘中的不同目录。rsync 是用于替代 rcp 的一个工具,rsync 使用所谓的 rsync算法 进行数据同步,这种算法只传送两个文件的不同部分,而不是每次都整份传送,因此速度相当快。rsync 基于inotify 开发
rsync官方文档:https://www.samba.org/ftp/rsync/rsync.html
Rsync有三种模式:
- 本地模式(类似于cp命令)
- 远程模式(类似于scp命令)
- 守护进程(socket进程:是rsync的重要功能)
选项 | 含义 |
---|---|
-a | 包含-rtplgoD |
-r | 同步目录时要加上,类似cp时的-r选项 |
-v | 同步时显示一些信息,让我们知道同步的过程 |
-l | 保留软连接 |
-L | 加上该选项后,同步软链接时会把源文件给同步 |
-p | 保持文件的权限属性 |
-o | 保持文件的属主 |
-g | 保持文件的属组 |
-D | 保持设备文件信息 |
-t | 保持文件的时间属性 |
–delete | 删除DEST中SRC没有的文件 |
–exclude | 过滤指定文件,如–exclude “logs”会把文件名包含logs的文件或者目录过滤掉,不同步 |
-P | 显示同步过程,比如速率,比-v更加详细 |
-u | 加上该选项后,如果DEST中的文件比SRC新,则不同步 |
-z | 传输时压缩 |
2. rsync安装与启动(无权限认证)
1 | # 注意该软件传输的两台机器都需要安装 |
配置文件可能不会生成,需要我们手动生成,sudo vim /etc/rsyncd.conf
添加配置文件,然后在需要同步的机器上启动(默认rsync是不启动的,同时关闭需要kill)
1 | # /etc/rsyncd: configuration file for rsync daemon mode |
运行命令,注意rsync服务器需要开启873端口
1 | # 在需要同步的机器运行,可自定义端口 |
3. 带权限认证的同步传输
首先创建用户密码对,sudo echo "shawn:www" >> /etc/rsyncd.pwd
,同时设置权限为600,sudo chmod 600 /etc/rsyncd.pwd
,然后修改配置。修改完成后kill进程然后启动
1 | secrets file = /etc/rsyncd.pwd |
在被同步的机器访问rsync --list-only shawn@xx::ftp
,加上用户名和密码即可。当时可以进行免密操作
1 | # 存入密码 |
4. 实时监控与推送
推送端安装inotify
1 | # 编码编译安装,需要gcc,make相关依赖 |
这里可以写个自动化脚本,一个目录一旦发送变化,就进行同步推送
1 |
|
inotify常用参数
参数 | 说明 | 含义 |
---|---|---|
-r | –recursive | 递归查询目录 |
-q | –quiet | 打印很少的信息,仅仅打印监控事件信息 |
-m | –monitor | 始终保持事件监听状态 |
–excludei | 排除文件或目录时,不区分大小写 | |
–timefmt | 指定事件输出格式 | |
–format | #打印使用指定的输出类似格式字符串 | |
-e | –event[ -e|–event … ]accessmodifyattribcloseopenmove_tomove createdeleteumount | #通过此参数可以指定要监控的事件 #文件或目录被读取#文件或目录的内容被修改#文件或目录属性被改变#文件或目录封闭,无论读/写模式#文件或目录被打开#文件或目录被移动至另外一个目录#文件或目录被移动另一个目录或从另一个目录移动至当前目录#文件或目录被创建在当前目录#文件或目录被删除#文件系统被卸载 |
5. rsync配置文件详解
1 | # sample rsyncd.conf configuration file |
三、多级缓存
1、浏览器缓存
进入开发者模式,刷新即可看见,点击disable_cache即可不使用本地缓存
- memorycache
字面理解是从内存中,其实也是字面的含义,这个资源是直接从内存中拿到的,不会请求服务器一般已经加载过该资源且缓存在了内存当中,当关闭该页面时,此资源就被内存释放掉了,再次重新打开相同页面时不会出现from memory cache的情况
- diskcache
是从磁盘当中取出的,也是在已经在之前的某个时间加载过该资源,不会请求服务器但是此资源不会随着该页面的关闭而释放掉,因为是存在硬盘当中的,下次打开仍会from disk cache
- Age
是CDN添加的属性表示在CDN中缓存了多少秒
- via
用来标识CDN缓存经历了哪些服务器,缓存是否命中,使用的协议
2、协商缓存
发送请求header中携带Last-Modified,服务器可能会返回304 Not Modified
Nginx配置对应Last-Modified和ETag。http1.1支持,在HTTP协议中If-Modified-Since和If-None-Match分别对应Last-Modified和ETag(last-modified 与ssi会发生冲突)
Entity Tag 的缩写,中文译过来就是实体标签的意思。HTTP中并没有指定如何生成ETag,哈希是比较理想的选择。在计算Etag的时候,会产生CPU的耗费,所以也可以用时间戳,但这样直接使用Last-Modified即可。ETag 用来校验用户请求的资源是否有变化,作用和lastmodified很像,区别是lastmodified精确到秒,ETag可以用hash算法来生成更精确的比对内容。当用户首次请求资源的时候返回给用户数据和200状态码并生成ETag,再次请求的时候服务器比对ETag,没有发生变化的话返回304
3、浏览器强制缓存
直接从本机读取,不请求服务器,http1.1的规范,使用max-age表示文件可以在浏览器中缓存的时间以秒为单位,在浏览器和服务器端验证文件是否过期的时候,浏览器在二次请求的时候会携带IF-Modified-Since属性
Cache-Control直接是通过不请求来实现,而ETag是会发请求的,只不过服务器根据请求的东西的内容有无变化来判断是否返回请求的资源
标记 | 类型 | 功能 |
---|---|---|
public | 响应头 | 响应的数据可以被缓存,客户端和代理层都可以缓存 |
private | 响应头 | 可私有缓存,客户端可以缓存,代理层不能缓存(CDN,proxy_pass) |
no-cache | 请求头 | 可以使用本地缓存,但是必须发送请求到服务器回源验证 |
no-store | 请求和响应 | 应禁用缓存 |
max-age | 请求和响应 | 文件可以在浏览器中缓存的时间以秒为单位 |
s-maxage | 请求和响应 | 用户代理层缓存,CDN下发,当客户端数据过期时会重新校验 |
max-stale | 请求和响应 | 缓存最大使用时间,如果缓存过期,但还在这个时间范围内则可以使用缓存数据 |
min-fresh | 请求和响应 | 缓存最小使用时间, |
must-revalidate | 请求和响应 | 当缓存过期后,必须回源重新请求资源。比no-cache更严格。因为HTTP 规范是允许客户端在某些特殊情况下直接使用过期缓存的,比如校验请求发送失败的时候。那么带有must-revalidate的缓存必须校验,其他条件全部失效。 |
proxy-revalidate | 请求和响应 | 和must-revalidate类似,只对CDN这种代理服务器有效,客户端遇到此头,需要回源验证 |
stale-while-revalidate | 响应 | 表示在指定时间内可以先使用本地缓存,后台进行异步校验 |
stale-if-error | 响应 | 在指定时间内,重新验证时返回状态码为5XX的时候,可以用本地缓存 |
only-if-cached | 响应 | 那么只使用缓存内容,如果没有缓存 则504 getway timeout |
Expires过期时间,现在一般用Cache-Control
1 | expires 30s; #缓存30秒 |
强制缓存与协商缓存总结
- cache-control expires 强制缓存
页面首次打开,直接读取缓存数据,刷新,会向服务器发起请求
- etag lastmodify 协商缓存
没发生变化 返回304 不发送数据
1 | location / { |
4、浏览器缓存原则
- 多级集群负载时last-modified必须保持一致
- 还有一些场景下我们希望禁用浏览器缓存。比如轮训api上报数据数据
- 浏览器缓存很难彻底禁用,大家的做法是加版本号,随机数等方法。
- 只缓存200响应头的数据,像3XX这类跳转的页面不需要缓存。
- 对于js,css这类可以缓存很久的数据,可以通过加版本号的方式更新内容
- 不需要强一致性的数据,可以缓存几秒
- 异步加载的接口数据,可以使用ETag来校验。
- 在服务器添加Server头,有利于排查错误
- 分为手机APP和Client以及是否遵循http协议
- 在没有联网的状态下可以展示数据
- 流量消耗过多
- 提前下发 避免秒杀时同时下发数据造成流量短时间暴增
- 兜底数据 在服务器崩溃和网络不可用的时候展示
- 临时缓存 退出即清理
- 固定缓存 展示框架这种,可能很长时间不会更新,可用随客户端下发
- 首页有的时候可以看做是框架 应该禁用缓存,以保证加载的资源都是最新的
- 父子连接 页面跳转时有一部分内容不需要重新加载,可用从父菜单带过来
- 预加载 某些逻辑可用判定用户接下来的操作,那么可用异步加载那些资源
- 漂亮的加载过程 异步加载 先展示框架,然后异步加载内容,避免主线程阻塞
5、GEOip
Geo是geographic的缩写,意思是地理的,GeoIP即为IP地理位置数据库,可以根据IP获得地理位置信息,这样就可以进一步加快用户的访问速度。(现在不怎么使用,现在可以使用DNS配置)
1 | # 官网需注册登录 |
6、正向代理
基于nginx的正向代理,在服务器A搭建一个代理服务器,使得BC可以通过服务器A的nginx 代理进行访问外网
代理https请求需要第三方模块:https://github.com/chobits/ngx_http_proxy_connect_module
http正向代理配置
1 | proxy_pass $scheme://$host$request_uri; |
https正向代理配置
1 | server { |
7、反向代理缓存
7.1 proxy缓存
官方文档:http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache
反向代理和正向代理都可以使用缓存,但是使用这个缓存后就不会在去访问上游服务器,因此要考虑好缓存与更新的问题
1 | #http模块 |
7.2 缓存的清理
purger需要第三方模块支持:https://github.com/FRiCKLE/ngx_cache_purge
1 | location ~ /purge(/.*) { |
- proxy_cache_key
默认$scheme$proxy_host$request_uri
;缓存的key
- proxy_cache_revalidate
如果缓存过期了,向上游服务器发送“If-Modified-Since” and “If-None-Match来验证是否改变,如果没有就不需要重新下载资源了
- proxy_cache_valid
可以针对不容http状态码设置缓存过期时间;不设置状态码会默认200, 301, 302
1 | proxy_cache_valid 200 302 10m; |
7.3 断点续传缓存 range
在Headers里添加Range bytes=0-10
可以截取部分数据,返回206。当有完整的content-length之后即可断点续传,在反向代理服务器中需向后传递header
1 | # proxy_cache_key中增加range |
7.4 一些功能参数
1 | # 默认 head get |
- proxy_cache_use_stale
默认off,在什么时候可以使用过期缓存
可选error
| timeout
| invalid_header
| updating
| http_500
| http_502
| http_503
| http_504
| http_403
| http_404
| http_429
| off
- proxy_cache_background_update
默认off,运行开启子请求更新过期的内容。同时会把过期的内容返回给客户端
- proxy_no_cache proxy_cache_bypass
指定什么时候不使用缓存而直接请求上游服务器
1 | proxy_no_cache $cookie_nocache $arg_nocache$arg_comment; |
如果这些变量如果存在的话不为空或者不等于0,则不使用缓存
- proxy_cache_convert_head
默认 on,是否把head请求转换成get请求后再发送给上游服务器 以便缓存body里的内容。如果关闭 需要在 cache key 中添加 $request_method 以便区分缓存内容
- proxy_cache_lock
默认off,缓存更新锁
- proxy_cache_lock_age
默认5s,缓存锁超时时间
8、其他
- Nginx内存缓存
- 外置内存缓存
- 上游服务器应用缓存
四、Nginx其他缓存
1、Nginx 文件元数据缓存
1.1 Strace内核执行过程追踪
strace一般应用为静态文件元数据信息缓存
1 | #查找nginx进程 |
sendfile执行过程结果
1 | [{EPOLLIN, {u32=2648526864, u64=139941272965136}}], 512, -1) = 1 |
1.2 open_file_cache配置优化
sendfile = off 时,应用程序读取磁盘中的文件以字节流的形式从磁盘中加载文件,然后再将文件以字节流的形式复制到内核中。内核在把文件推送到NC;
sendfile = on 时,应用程序直接向内核发送指令,让内核去读文件。读完文件内核直接推送给NC。只有一次复制操作,实现异步网络IO形式。因此,性能会有很大的提升。具体还要根据实际使用情况来配置sendfile。当读取服务不能解析大量的文件时。还是建议将sendfile配置为off。open_file_cache需要和sendfile配合使用,他会告诉 Nginx 去缓存打开的文件
1 | http { |
2、Nginx外置缓存
2.1 error_page错误码
nginx的error_page的作用是当发生错误的时候能够显示一个预定义的uri
1 | # 放在server里 |
2.2 匿名location
1 | error_page 404 = @anonymous; |
2.3 nginx + memcached
参考文档:http://nginx.org/en/docs/http/ngx_http_memcached_module.html
nginx的memcached_module模块可以直接从memcached服务器中读取内容后输出,后续的请求不再经过应用程序处理,如php-fpm、django,大大的提升动态页面的速度。nginx只负责从memcached服务器中读取数据,要往memcached写入数据丕得需要后台的应用程序来完成,主动的将要缓存的页面缓存到memcached中,可以通过404重定向到后端去处理的。
1 | # ubuntu安装 |
nginx配置文件
1 | upstream tomcat-web { |
2.4 Nginx+Redis
参考文档(很详细):https://www.nginx.com/resources/wiki/modules/redis2/
redis2-nginx-module是一个支持 Redis 2.0 协议的 Nginx upstream 模块,它可以让 Nginx 以非阻塞方式直接防问远方的 Redis 服务,同时支持 TCP 协议和 Unix Domain Socket 模式,并且可以启用强大的 Redis 连接池功能,需要添加编译
https://github.com/openresty/redis2-nginx-module
1 | # 具体的可以看文档,很详细 |
3、Stream模块
参考文档:http://nginx.org/en/docs/stream/ngx_stream_core_module.html
nginx支持了四层代理,即传输层,就是我们常说的TCP/UDP层,没有协议解析,就是简单的TCP/UDP转发,该功能依赖于ngx_http_upstream_module
和ngx_stream_upstream_module
,互联网公司将其作为入口代理来工作
3.1 Nginx配置Mysql负载均衡
Mysql集群跨域通过Mycat代理,也可以通过Nginx代理 ,Mysql透明化多主高可用,负载均衡,这样应用就无法感知服务端机器数量,首先要编译Stream模块
1 | # 和http块同级 |