压测和限流

go压测

http服务压测的方法比较多,主要使用第三方的软件即可实现,推荐使用 go-wrk,

go-wrk使用

go get github.com/adeven/go-wrk  # 安装命令
go-wrk [flags] url               # 使用方法

go-wrk -t=8 -c=100 -n=10000 "http://127.0.0.1:8080/api/v1/posts?size=10"  # 测试

常用参数

-H="User-Agent: go-wrk 0.1 bechmark\nContent-Type: text/html;"  # 由'\n'分隔的请求头
-c=100          # 使用的最大连接数
-k=true         # 是否禁用keep-alives
-i=false        # if TLS security checks are disabled
-m="GET"        # HTTP请求方法
-n=1000         # 请求总数
-t=1            # 使用的线程数
-b=""           # HTTP请求体
-s=""           #如果指定,它将计算响应中包含搜索到的字符串s的频率

限流限速

分布式系统中进场会提到限速和降级的概念。所谓限流,可以认为是服务降级的一种,限流就是限制系统的输入和输出流量, 以达到保护系统的目的 系统上线之前,一般都会进行压测,压测之后吞吐量是可以被测算的,为了保证系统的稳定运行, 一旦达到了设定限制的阔值,就需要限制流量并采取一些措施以完成限制流目的。常见的限流方案为:延迟处理、拒绝处理和部分拒绝处理等。

常见的限流方案

常见的限流方案为:延迟处理、拒绝处理和部分拒绝处理等。

常用的限流方案

  1. 使用带缓冲的chann,当chan塞满以后,拒绝服务或者给一个友好的提示或跳转到一个友好的页面等。

  2. 计数器,在10秒内只接受100 请求,当超过100请求后,拒绝服务,当超过10秒后,计数归零,重新接受请求。

  3. 使用 httpserver 的频率限制,无需自己实现。

  4. 令牌桶算法:以恒定的速度往令牌桶中放入令牌,当有请求过来则从令牌桶中获取令牌进行后续请求,当获取令牌失败后则进行友好处理

计数器算法

计数器算法是限流算法里最简单也是最容易实现的一种算法。比如我们规定,对于A接口来说,我们1分钟的访问次数不能超过100个。那么我们可以这么做:在一开 始的时候,我们可以设置一个计数器 counter,每当一个请求过来的时候,counter 就加1,如果 counter 的值大于 100 并且该请求与第一个 请求的间隔时间还在1分钟之内,那么说明请求数过多;如果该请求与第一个请求的间隔时间大于1分钟,且counter的值还在限流范围内,那么就重置 counter

滑动窗口

滑动窗口,又称rolling window。为了解决这个问题,我们引入了滑动窗口算法。如果学过TCP网络协议的话,那么一定对滑动窗口这个名词不会陌生。下面这张图,很好地解释了滑动窗口算法:

漏桶算法

漏桶算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出(请求被拒绝),可以看出漏桶算法可以平滑突发流量,即无论多少请求过来,服务器的处理速度都是一样的。

漏桶算法存在的缺陷

在某些情况下,漏桶算法不能够有效地使用网络资源。因为漏桶的漏出速率是固定的参数,所以即使网络中不存在资源冲突(没有发生拥塞),漏桶算法也不能使某一个单独的流突发到端口速率。因此,漏桶算法对于存在突发特性的流量来说缺乏效率。而令牌桶算法则能够满足这些具有突发特性的流量。通常,漏桶算法与令牌桶算法可以结合起来为网络流量提供更大的控制。

令牌桶算法

对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。如图2所示,令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。

go实现令牌桶限流代码

借助第三方库 github.com/juju/ratelimit,该库的方法是使用令牌桶原理来实现的限流。

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/juju/ratelimit"
)

// 限流的中间件
func RateLimitMiddleware(fillInterval time.Duration, cap int64) func(c *gin.Context) {
	// 定义桶
	bucket := ratelimit.NewBucket(fillInterval, cap) //每过多长时间,往里面放多少令牌
	return func(c *gin.Context) {
		// 如果取不到令牌,就返回相应
		if bucket.TakeAvailable(1) == 0 { //等于0,表示没有取到
			c.JSON(http.StatusOK, gin.H{
				"msg": "限流了",
			})
			c.Abort()
		}
		// 取到令牌就返回
		c.Next()
	}
}

适用场景

并不能说明令牌桶一定比漏洞好,她们使用场景不一样。令牌桶可以用来保护自己,主要用来对调用者频率进行限流,为的是让自己不被打垮。所以如果自己本身有处理能力的时候,如果流量突发(实际消费能力强于配置的流量限制),那么实际处理速率可以超过配置的限制。而漏桶算法,这是用来保护他人,也就是保护他所调用的系统。主要场景是,当调用的第三方系统本身没有保护机制,或者有流量限制的时候,我们的调用速度不能超过他的限制,由于我们不能更改第三方系统,所以只有在主调方控制。这个时候,即使流量突发,也必须舍弃。因为消费能力是第三方决定的。

总结起来:如果要让自己的系统不被打垮,用令牌桶。如果保证别人的系统不被打垮,用漏桶算法

参考文献

[1]https://www.cnblogs.com/hcy-fly/p/10832948.html
[2]https://www.cnblogs.com/aspirant/p/9093437.html
[3]https://www.bilibili.com/video/BV18s411p7kj?share_source=copy_web
[4]https://www.cnblogs.com/xuwc/p/9123078.html

posted @ 2021-07-30 15:34  沧海一声笑rush  阅读(617)  评论(0编辑  收藏  举报