系统限流的意义 & Golang 代码实现

2,973 阅读1分钟

这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战

如果❤️我的文章有帮助,欢迎点赞、关注。这是对我继续技术创作最大的鼓励。更多往期文章在我的个人专栏

系统限流的意义 & Golang 代码实现

限流的意义

高并发系统三大利器:缓存、降级、限流

  • 缓存:提升系统访问速度和增大处理容量,为相应业务增加缓存。
  • 降级:当服务器压力剧增时,根据业务策略降级,以此释放服务资源保证业务正常。
  • 限流:通过对并发限速,达到拒绝服务、排队、等候、降级等处理

漏桶限流 time/rate

  • rate.NewLimiter(limit,burst)
  • limit表示每秒产生token数、burst最多存token数
  • Allow判断当前是否可以取到token
  • Wait阻塞等待直到取到token
  • Reserve返回等待时间,再去取token

time/rate 源代码窥探

  • 计算上次请求和当前请求时间差
  • 计算时间差内生成的token数+旧token数
  • 如果token为负,计算等待时间
  • token为正,则请求后token-1

给网关代理插上限流的翅膀

package middleware

import (
	"fmt"
	"golang.org/x/time/rate"
)

func RateLimiter() func(c *SliceRouterContext) {
	l := rate.NewLimiter(1, 2)
	return func(c *SliceRouterContext) {
		if !l.Allow() {
			c.Rw.Write([]byte(fmt.Sprintf("rate limit:%v,%v", l.Limit(), l.Burst())))
			c.Abort()
			return
		}
		c.Next()
	}
}

接入到中间件

package main

import (
	"github.com/e421083458/gateway_demo/proxy/middleware"
	"github.com/e421083458/gateway_demo/proxy/proxy"
	"log"
	"net/http"
	"net/url"
)

var addr = "127.0.0.1:2002"

// 熔断方案
func main() {
	coreFunc := func(c *middleware.SliceRouterContext) http.Handler {
		rs1 := "http://127.0.0.1:2003/base"
		url1, err1 := url.Parse(rs1)
		if err1 != nil {
			log.Println(err1)
		}

		rs2 := "http://127.0.0.1:2004/base"
		url2, err2 := url.Parse(rs2)
		if err2 != nil {
			log.Println(err2)
		}

		urls := []*url.URL{url1, url2}
		return proxy.NewMultipleHostsReverseProxy(c, urls)
	}
	log.Println("Starting httpserver at " + addr)

	sliceRouter := middleware.NewSliceRouter()
	sliceRouter.Group("/").Use(middleware.RateLimiter())
	routerHandler := middleware.NewSliceRouterHandler(coreFunc, sliceRouter)
	log.Fatal(http.ListenAndServe(addr, routerHandler))
}

测试

$ curl '127.0.0.1:2002/abo'
http://127.0.0.1:2004/base/abo
RemoteAddr=127.0.0.1:62366,X-Forwarded-For=127.0.0.1,X-Real-Ip=
headers =map[Accept:[*/*] Accept-Encoding:[gzip] User-Agent:[curl/7.69.1] X-Forwarded-For:[127.0.0.1]]

Administrator@DESKTOP-U15QB8I MINGW64 /d/Dev/workplace/golang/gateway_v1/pratise/proxy/limiter/rate_limiter (master)

$ curl '127.0.0.1:2002/abo'
rate limit:1,2
Administrator@DESKTOP-U15QB8I MINGW64 /d/Dev/workplace/golang/gateway_v1/pratise/proxy/limiter/rate_limiter (master)

$ curl '127.0.0.1:2002/abo'
http://127.0.0.1:2004/base/abo
RemoteAddr=127.0.0.1:62366,X-Forwarded-For=127.0.0.1,X-Real-Ip=
headers =map[Accept:[*/*] Accept-Encoding:[gzip] User-Agent:[curl/7.69.1] X-Forwarded-For:[127.0.0.1]]

Administrator@DESKTOP-U15QB8I MINGW64 /d/Dev/workplace/golang/gateway_v1/pratise/proxy/limiter/rate_limiter (master)

$ curl '127.0.0.1:2002/abo'
rate limit:1,2