当前位置: 首页 > Linux

如果Nginx的流量控制不好,少不了背黑锅跑路!

时间:2023-04-06 06:12:58 Linux

链接:https://www.cnblogs.com/zjfja...作者:雪山上的蒲公英前几天,一位老同事在微信上向我诉苦。服务器报警,其中一台服务器宕机,影响了公司部分业务的运行。事后发现是前端Nginx流控配置不够科学,不得不怪罪,影响了本月和本年度的KPI考核。加薪指标。可见Nginx流控的配置还是很重要的。因此,本文将介绍Nginx限流的基础知识和高级配置。“流量限制”在NginxPlus中也适用。速率限制是Nginx中一个非常有用但经常被误解和错误配置的功能。我们可以使用它来限制用户在给定时间可以发出的HTTP请求的数量。该请求可以是对简单网站主页的GET请求,也可以是对登录表单的POST请求。流量限制可用于安全目的,例如减慢暴力破解密码的速度。它还可以通过将传入请求的速率限制为真实用户的典型值并识别目标URL地址(通过日志)来防御DDOS攻击。更常见的是,此功能用于保护上游应用程序服务器不被太多并发用户请求淹没。Nginx如何限流Nginx的“限流”使用了漏桶算法(leakybucketalgorithm),广泛应用于通信和包交换计算机网络中,以应对带宽受限时的突发事件。就像一个桶,嘴在倒,桶底在漏。如果从桶口倒水的速度大于桶底漏水的速度,则桶中的水就会溢出;同样,在请求处理方面,水代表来自客户端的请求,水桶代表等待按照“先进先出调度算法”(FIFO)处理的请求队列,水从底部漏出水桶的水代表离开缓冲区等待服务器处理的请求,水桶溢出的水代表被丢弃未处理的请求。配置基本流量限制“TrafficLimit”主要配置两个指令,limit_req_zone和limit_req,如下:proxy_passhttp://my_upstream;}}limit_req_zone指令定义了与流量限制相关的参数,limit_req指令在它出现的上下文中启用流量限制(在示例中,针对所有对“/login/”的请求)。limit_req_zone指令通常在HTTP块中定义,使其在多个上下文中可用,它采用以下三个参数:Key-定义要应用的限制的请求特征。示例中的Nginx变量remote_addr占用空间较小)Zone——定义了一个共享内存区域,用于存储各个IP地址的状态和受限请求URL的访问频率。内存共享区中保存的信息意味着它可以在Nginx工作进程之间共享。该定义分为两部分:由zone=关键字标识的区域名称,以及后跟冒号的区域大小。16000个IP地址的状态信息大约需要1MB,所以例子中的区域可以存储160000个IP地址。速率-定义最大请求速率。在示例中,速率不能超过每秒10个请求。Nginx实际上是以毫秒为粒度跟踪请求的,所以速率限制相当于每100毫秒1个请求。因为不允许“突发”(请参阅??下一节),这意味着在前一个请求的100毫秒内到达的请求将被拒绝。当Nginx需要添加新条目时,存储空间不足,旧的条目将被删除。如果释放的空间仍然不足以容纳新记录,Nginx将返回503状态码(ServiceTemporarilyUnavailable)。另外,为了防止内存被耗尽,Nginx每次创建一个新条目时,最多删除两个在60秒内没有被使用过的条目。limit_req_zone指令为共享内存区域设置流量限制和参数,但实际上并不限制请求速率。因此,需要通过添加limit_req命令将流量限制应用到特定位置或服务器块。在上面的示例中,我们限制了/login/请求的流量。每个IP地址现在限制为每秒只能请求10次/login/,更准确地说,不能在上次请求的100毫秒内请求URL。处理突发如果我们在100毫秒内收到2个请求怎么办?对于第二次请求,Nginx会返回状态码503给客户端。这可能不是我们想要的,因为应用程序在本质上往往是突发的。相反,我们希望缓冲任何多余的请求并及时处理它们。让我们更新配置并使用limit_req中的burst参数:location/login/{limit_reqzone=mylimitburst=20;proxy_passhttp://my_upstream;}burst参数定义了速率超过指定zone的情况(示例中的mylimitzone,ratelimitedto10requestspersecond,oronerequestper100milliseconds),还有多少请求客户可以发起。在前一个请求的100毫秒内到达的请求将被排队,我们将队列大小设置为20。这意味着如果从给定IP地址发送21个请求,Nginx将立即将第一个请求发送到上游服务器群,然后将剩余的20个请求放入队列中。然后每100毫秒转发一个排队的请求,只有当传入的请求使队列中排队的请求数超过20时,Nginx才会向客户端返回503。配置无延迟排队的burst参数可以使通信更顺畅,但可能不切实际,因为配置会使站点看起来很慢。在上面的示例中,队列中的第20个数据包需要等待2秒才能被转发,此时返回给客户端的响应可能不再有用。解决这种情况,可以在burst参数后面加上nodelay参数:location/login/{limit_reqzone=mylimitburst=20nodelay;proxy_passhttp://my_upstream;}使用nodelay参数,Nginx仍然会根据burst参数location分配队列,并应用配置的速率限制,而不是清除等待转发的请求队列。相反,当请求“过早”到达时,Nginx会在队列中分配到位置后立即转发请求。将队列中的该位置标记为“已占用”,直到一段时间后(在本例中为100毫秒后)才会为另一个请求释放。如前所述,假设队列中有20个空槽,并且来自给定IP地址的21个请求同时到达。Nginx会立即转发这21个请求,并标记队列中占用的20个位置,然后每100毫秒释放一个位置。如果同时有25个请求到达,Nginx会立即转发其中的21个,标记队列中的20个位置,并返回一个503状态码拒绝剩下的4个请求。现在假设在第一组请求转发后101毫秒,另外20个请求同时到达。队列中只有一个插槽会被释放,因此Nginx转发一个请求并拒绝其他19个请求,并返回503状态码。如果在20个新请求到达之前已经过去了501毫秒,5个位置被释放,那么Nginx立即转发5个请求并拒绝另外15个。效果相当于每秒10个请求的“流量限制”。如果你想在不限制两次请求之间的允许间隔的情况下实现“流量节流”,则nodelay参数很有用。注意:对于大多数部署,我们建议使用burst和nodelay参数来配置limit_req指令。高级配置示例通过使用基本的“流量限制”和其他Nginx功能,我们可以实现更细粒度的流量限制。白名单以下示例将展示如何对不在白名单中的任何请求强制执行“流量限制”:geo$limit{default1;10.0.0.0/80;192.168.0.0/640;}map$limit$limit_key{0"";1$binary_remote_addr;}limit_req_zone$limit_keyzone=req_zone:10mrate=5r/s;server{location/{limit_reqzone=req_zoneburst=10nodelay;#...}}此示例同时使用了geo和map指令。geoblock会将白名单中IP地址对应的$limit变量赋值为0,不在白名单中的IP地址将赋值为1。然后我们使用映射将这些值转换为键,如下所示:如果变量的值为二进制形式的客户端IP地址两个命令一起使用时,白名单中IP地址的$limit_key变量赋值为空字符串,客户端IP地址赋值给不在白名单中的IP地址白名单。当limit_req_zone后的第一个参数为空字符串时,不执行“流量限制”,因此白名单中的IP地址(10.0.0.0/8和192.168.0.0/24网段)不会被限制。所有其他IP地址限制为每秒5个请求。limit_req指令将限制应用于/的位置块,允许在不延迟转发的情况下突发最多10个超出配置限制的数据包。location包含多个limit_req指令我们可以在一个location块中配置多个limit_req指令。当应用与给定请求匹配的所有限制时,这意味着将使用最严格的限制。例如,如果多条指令指定了延迟,则将使用最长的延迟。同样,即使其他指令允许,受某些指令影响的请求也会被拒绝。扩展前面对白名单中的IP地址应用“流量限制”的示例:http{#...limit_req_zone$limit_keyzone=req_zone:10mrate=5r/s;limit_req_zone$binary_remote_addrzone=req_zone_wl:10mrate=15r/s;server{#...location/{limit_reqzone=req_zoneburst=10nodelay;limit_reqzone=req_zone_wlburst=20节点布局;#...}}}白名单中的IP地址将不匹配第一个“流量”限制”,它将匹配第二个req_zone_wl并限制为每秒15个请求。不在白名单中的IP地址匹配两个限制,所以具有更强限制的应用:每秒5个请求。配置相关功能日志记录Nginx默认会在日志中记录因流量限制而延迟或丢弃的请求,如下:2015/06/1304:20:00[error]120315#0:*32086limitingrequests,excess:1.000byzone"mylimit",client:192.168.1.2,server:nginx.com,
request:"GET/HTTP/1.0",host:"nginx.com"日志条目中包含的字段:limitingrequests-表示日志条目记录的请求是“限流”excess-每毫秒超过相应“限流”配置的请求数zone-定义实施“限流”的区域client-的IP地址发起请求的clientserver-服务器IP地址或主机名request-客户端实际发起的HTTP请求host-HTTP头中host的值默认情况下,Nginx在error级别记录被拒绝的请求,如[上面例子中的error](Ngin开始时下层记录了延迟的请求,一般是info层)。如果你想改变Nginx的日志级别,你需要使用limit_req_log_level指令。在这里,我们将拒绝请求的日志记录级别设置为警告:location/login/{limit_reqzone=mylimitburst=20nodelay;limit_req_log_level警告;proxy_passhttp://my_upstream;}发送给客户端的错误码一般情况下,当客户端超过配置的流量限制时,Nginx会响应状态码503(ServiceTemporarilyUnavailable)。可以使用limit_req_status命令设置其他状态码(比如下面的444状态码):location/login/{limit_reqzone=mylimitburst=20nodelay;limit_req_status444;}指定location拒绝所有请求如果要拒绝对指定URL地址的所有请求,而不是只限速,只需要在location块配置denyall命令:location/foo.php{denyall;}总结上一篇已经介绍了Nginx和NginxPlus提供的“限流”的很多功能,包括针对HTTP请求的不同位置设置请求速率,以及为“限流”配置burst和nodelay参数。它还涵盖了对客户端IP地址的白名单和黑名单应用不同“流量限制”的高级配置,并解释了如何记录被拒绝和延迟的请求。