connection和request
- connection 就是一个连接, TCP连接
- 客户端和服务器想要进行通信的话,有很多种方式
- 比如说, TCP的形式或者是UDP形式的
- 通常很多应用都是建立在这个TCP之上的
- 所以, 客户端和服务器通信,使用了TCP协议的话,必然涉及建立TCP连接
- 也就是通常所说的三次握手
- TCP协议它是一个有状态的一个协议,它是有一个完整的状态机的
- 通过那个状态位的不同变化来判断分析的
- 如果先建立了这个TCP连接之后,接下来就可以发送应用层的数据信息
- 比如 HTTP 请求,这就是一个 request
- 这个 request 它是必须建立在 我们的connection之上的
- HTTP协议, 它又是一个无状态的协议
- 所以说connection和request他们连个基本的关系是
- 首先要建立 connection 之后才需要去发送 request
- 这个 connection 和 request 之间对应的关系并非一对一的
- 在打开长连接的过程中,可能在一个 connection 中会发送很多的HTTP的requests请求
- 请求示意图
- 我的客户端想要跟服务器通信去获取服务器上某一些网站的信息
- 就简单以浏览网页这种形式来说,送一个请求
- 也就是在浏览器中去刷新一个URL, 它会发生哪些事呢?
- 第一个肯定需要去建立一个connection,也就发起一个TCP的一个连接
- 客户端首先会给服务器发送一个带有SYN的一个数据包
- 服务器收到这个数据包之后就知道客户端是想发起一个TCP连接
- 它会回应客户端发送的SYN包,并且也发送一个ACK同时它也有一个SYN的标志
- 客户端收到以后给服务器发送一个ACK
- 至此,三次握手就完成了,建立了一个TCP连接
- 那TCP连建立完成之后,这个客户端就可以去发送具体的一个HTTP请求
- 比如我们在 www.baidu.com 中访问
- 这个时候, 请求的是提供的web服务器上的某一个或多个文件
- 默认是80端口,对应我们服务器上的一个外部应用
- 这个时候网站首页中可能包含了很多信息,会发起多个 request 请求
- 这个时候在一个TCP连接之中,可能会对应多个 request 的请求
- 开启了这个长连接的情形下,它为了加速效率
- 会允许你一个connection连接中发送很多个 request
- 同时服务器回应很多 response 报文
- request请求都是要建立在connection之上
limit_conn 模块
1 )基本功能
- 用于限制客户端并发连接数
- 默认编译进 nginx, 通过 --without-http_limit_conn_module 禁用
- 使用共享内存,对所有worker子进程生效
- 如果需要对客户端的并发连接进行限制的话
- 对于一个特定的客户端来说,他第一次发起一个HTTP的请求
- 这个请求被某一个worker子进程所处理
- 假如, 他下一次再发起的这个HTTP请求, 有可能受会到另外一个 worker 子进程
- 因此, 想要对所有客户端连接进行一个统一的控制
- 不同的worker子进程之间必须知道某一个客户端发送过来的连接有几个
- 必须要有一块共享内存, 在共享内存中,我们需要去维护这样一个数据结构
- 这个数据结构中能够保存特定客户端所发送过来的已经建立的这个连接数
- 因此,即使某一次请求被录入到不同的work子进程之后
- 我们的worker子进程也能够去这样一个共享内存中去取出这块数据结构中
- 已经存储的客户端所建立的连接数,从而对客户端的一个连接数进行限制
2 )常用指令
limit_conn_zone
, 其实这个zone指令就是用来定义共享内存的limit_conn_status
, 主要是用来定义限制行为发生的时候,返回给客户端的一个状态limit_conn_log_level
,限制行为发生的时在日志中所记录的这个日志等级limit_con
,这个才是真正去定义客户端的限制的一个并发连接数
那我们来逐个的看每一个指令,它的一个具体用法
2.1 ) limit_conn_zone
- 语法: limit_conn_zone key zone=name:size
- 这是一个固定写法,后面空格会跟一个key
- 这个 key 是一个唯一性的标识,用来标识客户端的唯一性的
- 比如说, 可以根据客户端的一个IP来做客户端的唯一性标识
- 通常情况下,我们会使用一个变量叫一个
$remote_addr
这样一个变量 - 这个是在Nginx 中所内置的一个变量
- 也就是说,当我们部署好Nginx, 你的客户端发送一个请求过来以后
- 在请求中会带上这个
$remote_addr
, Nginx会将客户端的IP赋值给$remote_addr
- 因此用来识别客户端的一个键,可以根据客户端唯一性标识这个IP来对客户端进行限制
- 当然还有很多一些其他的限制,比如说我们可以根据客户端建立的时候
- 有很多token信息来进行一个唯一的token信息来进行一个限速
- 当然不同的形式,需要根据自己的应用场景去定义
- 在实际的应用场景,通常用的最多的都是这个
$remote_addr
- 那在很多情形下,如果存在反向代理的情形,可能通过反向代理中
- 加一个指定的 real_ip 的指令,获取到 real_ip 对 real_ip 进行限速
- 总之,key就是用来定义所限速客户端那个唯一性标识
- 之后 zone=name:size 是一个固定写法,这个name是任意名称都行
- 后面跟一个冒号 和 size,这个size是定义共享内存的空间,以M为单位
- 默认值: 无
- 上下文: http (只能定义在 http 段中,即在http的全局配置信息中定义)
- 示例:
limit_conn_zone $binary_remote_addr zone=addr:10m
- $binary_remote_addr 和 $remote_addr 都是标记客户端ip信息的
- $binary_remote_addr 好处是只使用4个字节的空间,提升处理效率
- $remote_addr 会占用 7 - 15 个字节
- 这里 10m 一般足够使用
2.2 )limit_conn_status
- 语法:limit_conn_status code
- code 是状态码,比如:201, 302,404,…
- 默认值: limit_conn_status 503;
- 上下文:http、server、location
2.3 ) limit_conn_log_level
- 语法: limit_conn_log_level info | notice | warn | error;
- 比如说当我们这个限制行为发生的时候,根据客户的IP来限速
- 我们把它限速为一个并发连接,超过一个并发连接的时候, 不处理,返回503状态码
- 同样,在对应的 log 中进行记录
- 默认值:limit_conn_log_level error;
- 上下文:http、server、location
2.4 ) limit_conn
- 语法:limit_conn zone number;
- 前面的 limit_conn_zone 已经定义了某一个名称的一个共享内存,指定了大小空间
- 这个时候可以直接使用, 这个 zone 其实是要引用前面定义的addr的名称的那个定义
- 之后number就是限速多少
- 默认值: 无
- 上下文:http、server、location
3 ) 配置示例
- 我们想要根据
$binary_remote_addr
也就是客户端的IP去限速 - 并且把每一个客户端IP限速为2,只要超过2个链接,就给它限制住
- 超过2个链接,就直接给他返回 503
示例
http {limit_conn_zone $binary_remote_addr zone=limit_addr:10m # 这个只能在 http 段中server {listen 80;server_name localhost;location / {root html;index index.html index.htm;limit_status 503;limit_conn_log_level warn;limit_conn limit_addr 2;limit_rate 50;}}
}
重启
- $
/opt/nginx/sbin/nginx -s reload
- 开启 多个 curl 终端 进行
curl localhost
,前2个窗口有结果,后面就会 503
limit_req 模块
1 ) 基本功能
-
用于限制客户端处理请求的平均速率
-
默认编译进 nginx, 通过
--without-http_limit_req_module
禁用 -
使用共享内存, 对所有 woker 子进程生效
-
限流算法: leaky_bucket
上图是算法思想思路
- 它所使用的一个限流算法是一个 leaky_bucket 的算法,limit_req 模块本身类似于一个限流的模块
- 有一些很经典的一些限流算法:比如说像 leaky_bucket 算法,计算器算法(固定窗口),令排桶算法等等
- 这些都是在高并发系统中经常使用的一个限流的一个算法
- 在 Bursty data 图表中,横坐标是一个时间轴,纵坐标是一个流量的轴
- 假如说,随着时间的推移,流量是不均匀的,前两秒钟,它的流量达到了 12 Mbps
- 之后第2秒到第7秒中间没有任何流量, 之后第7秒到第10秒之间, 又平均是2 Mbps的一个流量
- 在这十秒钟,它一共有 12 * 2 + 2 * 3 = 30M 的流量,但是分布很不均匀
- 前2秒突发比较大,2 - 7 s 没有流量, 7 - 10 s 流量较少
- 这个 lkeybucket 算法就是实现了将流量削平来进行限流的动作
- 它把这些突发的流量,包括中间没有的流量给强行的限制到某一个速率上来进行处理
- 也就是 30 / 10 = 3Mbps 的速率,它类似于一个水龙头,使用这样一个漏斗来进行处理
- 从水龙头滴出来的水类似于我们的流量,水龙头的开关大小都用来模拟流量的一个大小
- 对于在这漏斗下面出水的一个速率,它是一个恒定的
- 这个漏桶算法最重要的功能就是实现一个削平流量
- 将突发的流量和不均匀的流量来进行一个强行恒定到某一个数据上来进行处理
- 上面有一个 Bursty flow, Bursty的参数类似于用来定义这样一个桶的大小
- 现在以 3Mbps 的速率来消费这些流量,但是这个流量峰值可能远远超过了3M这样一个流量
- 只要我这个桶没有满的情形下,这个服务是不会被拒绝的
- 只有说这个漏斗中的水完全满了之后,流量完全超过了这个桶的大小之后的才会被拒绝
2 )常见指令
2.1 limit_req_zone
- 用于定义共享内存
- 语法:limit_req_zone key zone=name:size rate=rate
- 默认值: 无
- 上下文:http
- 示例: limit_req_zone $binary_remote_addr zone=one:10m rate=2r/m
- rate=2r/m 每分钟2个请求
- 它会平均处理,比如30s之后再处理第二个
2.2 limit_req_status
- 语法:limit_req_status code;
- 默认值:limit_req_status 503;
- 上下文: http、server、location
2.3 limit_req_log_level
- 语法: limit_req_log_level info | notice | warn | error;
- 默认值: limit_req_log_level error;
- 上下文: http、server、location
2.4 limit_req
- 语法:limit_req zone=name [burst=number] [nodelay | delay=number];
- 这里 burst 就是用来定义桶的大小
- 想要它立即返回的话,必须加个 nodelay 选项
- 默认值: 无
- 上下文: http、server、location
- 示例
- limit_req zone=one;
- limit_conn zone=one burst=5 nodelay;
3 ) 配置示例
http {# 注意下面的定义limit_req_zone $binary_remote_addr zone=limit_req:15m rate=2r/m; # 这里rate设置的很小,用于演示server {listen 80;server_name localhost;location / {root html;index index.html index.htm;error_log logs/limit_req_error.log info; # 这个配置是特意加上的,用于查看错误输出# 注意下面的定义limit_req_status 504;limit_req_log_level notice;limit_req zone=limit_req; # 0s - 30s 内第一次,30s - 1m内 第二次,如果在0s-30s收到第二次请求则返回504# limit_req zone=limit_req burst=7 nodelay; # 定义 burst 选项,30s之内会继续返回,不会504, 直到 7次之后}}
}