一、限流
漏桶算法和 JMeter 压测
漏桶算法限流
压测部分仅作了解。
为何限制?安全稳定少出事。
JMeter 是 java 开发的工具,需要装 java,在 win 解压后打开 jmeter.bat,用这个压测服务器即可。

压测部分仅用视频内容做演示。
在 http 块 limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
变量是远程连接客户端 IP 地址($remote_addr 也可,前者是二进制数据到 zone 配置的缓存,后者是字节格式),zone 是该限速配置的名称,10MB 是记录远程地址的缓冲区,rate 是接收请求速率,1s 一个 request。
启用配置后配置压测参数测试:

如果并发量很多,直接丢弃请求。
漏桶算法顾名思义,把请求都放到里面,然后按照速率处理,超出桶的部分丢弃,上图很明显不符合配置要求,因为没有配置"桶"。
在 location 块 limit_req zone=one burst=5;
测试(并发不大于 5):

(上图)相同请求类似于队列请求的状态(先放到桶再按速率处理请求),如果不设置 burst,有速率限制就会全部丢弃请求,直到符合速率才会继续处理请求。
处理请求速度是固定的,当用户请求量变大,桶也严重超出,就会有很多请求超时,此时用到快速失败。
limit_req zone=one burst=5 nodelay; nodelay 表示后边的队列请求,不在桶里,直接失败(此时才是真"桶",装得下就处理,装不下扔)。

(上图)可以看到瞬间处理了桶内 5 个请求,其余的全失败了,因为配置的速率太低,可以改为 limit_req_zone $binary_remote_addr zone=one:10m rate=500r/s;
此时报错的概率会很低:

并发的速率限制一定在产品上线前要测试好(测试干的),一般都是基于测试产品(先行服)进行数据统计然后基于日志测试。
一个网站的用户日志就在 /var/log/nginx/,其中 access.log 是用户的所有行为(日志很大需要进行管理,之后会了解到)。

分别是用户请求 IP、时间、uri。
error.log 是错误日志。
具体配置区分
limit_req zone=one burst=5 nodelay; 和 limit_req zone=one burst=5;
加了 nodelay; 就是不看 rate 速率限制了,一下子处理桶内的请求。
加了 burst=5 nodelay 之后,前 5 个并发瞬间处理,第 6 个开始 rate 生效,按速率一个个处理。如果没 rate,桶外没任何限制,DDoS 直接打爆服务器。

令牌桶算法
介绍
nginx 的限速方式基于漏桶算法实现,令牌桶和前者的区别就在于限速指标。漏桶算法限速很严格,只能按照配置的处理请求,令牌桶有一些区别。
每个用户到系统有"申请令牌"的操作,假设开始每秒生成 5 个令牌放到桶,用户带着牌子再请求。

并发量大了,就没有牌子了,系统会按照固定速率向桶内放牌。
和漏桶算法很像,但是区别有很多,令牌桶算法有更灵活的处理方式,比如让一个人拿走很多牌子,针对于牌子区分用户,假设用户拿 1 个牌子分配 100M 带宽,2 个就是 200M(不同用户不同速),牌子不够,就等待(其实牌子就是 token)。
这个算法不匹配限制用户请求的需求,因为单位时间用户只会请求一次。令牌桶算法适用于限制带宽的需求,比如百度网盘会员和普通用户的限制。
限制网络带宽
官方文档只有两个配置:
limit_rate 1k; 发送请求传输文件的速率(以字节为单位,1k 就是 1KB,注意大小写)

可以看到 900 多 b/s,这只是浏览器的显示速度(稍快的浏览器比较不要脸,显得很快而已)。
这是单线程的请求,多线程的请求每一个线程都能拿到一个牌子,就可能突破速率的限制,每一个令牌和用户并发的请求是不冲突的,所以 limit_req 和 limit_rate 是可以相互配合使用的。
另外一个配置 limit_rate_after 1m; 在下载指定数据之后对用户限速(单位依旧是比特),先快后慢,常用于多媒体网站,先让用户看视频,然后限制一下,不能让一个用户一直占据很多带宽。
客户端并发数限制
限制用户请求的线程个数,配置和 qps 限制配置很像。
limit_req_zone $binary_remote_addr zone=one:10m rate=500r/s;
配置选项 限流选项 命名空间:分配的缓冲内存空间 速率限制
limit_conn_zone $binary_remote_addr zone=two:10m;
配置选项 限流选项 命名空间:分配的缓冲内存空间
在 location 块配置
limit_conn two 1; 唯一的区别就是多了命名空间的配置。
限速配置一共三块,针对 req 漏桶算法限制,针对带宽的令牌桶算法限制,针对客户端并发线程的计数器算法限制,三个应用实际上很广,主要是测试方面配置的参数。
二、日志
Nginx 日志基本配置
简单了解
日志是低价值数据,是用户的行为,不是个人信息数据,商业行为中可以调整产品策略,比如页面停留时间,购买商品喜好等构建大数据推荐系统。
/var/log/ 日志相比于文件、目录等权限分配要复杂很多,所以这里了解到的肯定是不深入的,仅仅作为入门的引子来理解就可以。
像是浏览器控制台 size 很小的请求,很可能是在收集用户的行为(在某个位置停留时长、鼠标滑过的都可以通过 js 前端脚本访问日志接口,与 AI 也相关)。
数据标注可能用到日志,比如人机验证点图片,把用户的点击数据收集起来,然后放到特定的数据中心(比如红绿灯的人机验证就放到自动驾驶领域)。
access.log 相关配置
在官方文档直接搜 ngx_http_log_module,记录日志请求都是 http 协议,https 增加了四次握手很低效。

access_log 记录了配置,buffer 缓冲区大小,gzip 压缩日志,以 gzip 格式放到文档,需要解压查看,flush 触发写日志的间隔,if 判断条件,如果匹配就不记录。
这里定义了全局路径但是 location 的块优先级高于全局,所以路径配置不能省略。
添加新的目录必须先建目录不然报错。

其他的一些具体内容可以看官方文档,上述内容是正常访问日志的配置。
tail -f /var/log/nginx 是查看日志更新的常用命令,-f 可以滚动到最后一条。
关于 nginx 文档建议自己看,查看官方文档的能力是至关重要的。
官方样例配置如下:

# 指定locaton块和access_log
location / {
# 日志配置
access_log /var/log/nginx/test_access.log test buffer=32k;
# buffer的配置是超出了32k缓冲就写入磁盘目录
# 所以配置这个选项,刚开始目录没东西
}
# 在nginx.conf的http块配置log_format
# 定义日志格式,决定日志记录内容
http {
# 日志配置
log_format test '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
# 官方文档说明了每个变量的功能
日志压缩和解压 & JSON 格式输出
压缩配置
运行过程内存缓冲区可能堆积不满,不写入磁盘,flush 就是强制写入磁盘的周期时间,一般以 h、min 为单位。
access_log /var/log/nginx/test_access.log test buffer=32k flush=1h;
如果日志太大,想缩小可以配置 gzip,但是需要注意日志是在缓冲区压缩的,所以想看日志必须解压出来才行,tail 也不行了。
access_log /var/log/nginx/test_access.log test buffer=32k gzip=6 flush=1h; 如果 gzip 什么都不写,就默认压缩等级为 1,等级过高会消耗性能的。
此时 tail 的文件是压缩形式,不可读:

开启 gzip 最好是开一个新文件从头开始记录。
以 gzip 压缩的文件,以 gzip 算法解压即可,解压时候最好复制一份再解压。

补充
官方还有其他的配置:缓存相关(buffer 是缓冲)

不打开配置,每次写日志都会重新打开磁盘文件,追加然后关掉。
max 最大打开文件描述符,后面三个分别是活动时间、至少几个请求再缓冲、缓冲区的过期时间(之前遇到过这些选项,属于代码复用了)。
JSON 格式输出日志
默认的格式是文本格式,可以指定为 json 格式(不能配置 gzip)。

可以做一些转译。
log_format test json '{
"timestamp":"$time_iso8601",
"source":"$server_addr",
"hostname":"$hostname",
"remote_user":"$remote_user",
"ip":"$http_x_forwarded_for",
"client":"$remote_addr",
"request_method":"$request_method",
"scheme":"$scheme",
"domain":"$server_name",
"referer":"$http_referer",
"request":"$request_uri",
"requesturl":"$request",
"args":"$args",
"size":"$body_bytes_sent",
"status":"$status",
"responsetime":"$request_time",
"upstreamtime":"$upstream_response_time",
"upstreamaddr":"$upstream_addr",
"http_user_agent":"$http_user_agent",
"http_cookie":"$http_cookie",
"https":"$https"
}';
access_log /var/log/nginx/test_access.log test buffer=32k flush=1s;
# 记得把之前的配置删去,两个配置同样的名字组会冲突的
json 格式还是很有用的,如果想做数据分析,直接导入日志就很方便,也可以存入大数据的一些产品框架,很方便。
日志必须开,追根溯源很有用的。
error 日志与日志分割
error_log 配置
官方文档:没有独立的模块,在 core 模块下。
nginx.org/en/docs/ngx_core_module.html#error_log

file 是记录位置,level 是错误等级,配置低了 debug 调试信息都会被记录,可以被收集和分析,不能自定义格式只能按照 nginx 自己的格式,或者导出后处理格式。
日志分割
脚本按照日期切割生成新的文件存放。
Logrotate 也可以。
三、健康检查
upstream 重试机制(被动)
位置:nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream

当发生了哪些错误之后启用 upstream 的下一个 server 服务器。
upstream httpget {
server 172.16.0.9;
server 10.1.4.16;
}
location / {
proxy_next_upstream error timeout;
# 如果超时了就尝试匹配10.1.4.16
# 官方也写了这是默认配置
proxy_pass http://httpget;
}
}

允许上游服务器失败重试的总时间,超出就直接告诉用户失败了。

总时间内尝试多少次失败重试,直到被允许的最后一次(不能给很长时间,否则在一台机器很可能卡死)。
失败的次数包含第一次请求。

简单来说就是对于一次请求,按照什么规则请求下一台服务器的配置。
如果下一次请求打到了,还是访问了刚才访问失败的机器,会降低效率,所以用到了额外的两个配置。
upstream httpget {
server 172.16.0.9 max_fails=5 fail_timeout=10s;
# 这台机器总共允许失败多少次,之后标记为不可用,不让用户访问
# 假设它之后又修复了,需要重新被访问,就是后面起作用
# 下线10s之后再上线一次。第二层含义:10s之内失败5次就下线
server 10.1.4.16;
}
健康检查使用 tengine 模块(主动)
这部分内容了解即可,现在改为用 k8s 了。

又有 Tengine 版本和 nginx 商业版,此处内容演示的是 Tengine。
官方文档有好多模块都是商业版本的。

这里的 patch 要和 nginx 版本对应。
Nginx 部分,至此完结,后续自己深化学习即可。
