今天的课程安排 流控 ***** ### 流控 为什么要流控呢? > 一年一度的「双 11」又要到了,阿里的码农们进入了一年中最辛苦的时光。各种容量评估、压测、扩容让我们忙得不可开交。洛阳亲友如相问,就说我搞双十一。 如何让系统在汹涌澎湃的流量面前谈笑风生?我们的策略是不要让系统超负荷工作。如果现有的系统扛不住业务目标怎么办?加机器!机器不够怎么办?业务降级,系统限流! 正所谓「他强任他强,清风拂山岗;他横任他横,明月照大江」,降级和限流是大促保障中必不可少的神兵利器,丢卒保车,以暂停边缘业务为代价保障核心业务的资源,以系统不被突发流量压挂为第一要务。 设置网站最大流量为1W第一个用户进来返回正常信息第9999位用户进来返回正常信息第1W1个用户进来不提供服务 我们这里流控算法Token Bucket 令牌桶算法 ![](https://box.kancloud.cn/84db1e1bef847bc4c908b32f88b8d39c_373x446.png) 用户进来给他一个token,用户离开就回收token 是不是很简单啊! golang实现思路 初始化一个channel 用户进来判断channel中数据的len 再判断初始化max长度的大小 如果>=初始化长度就是流量过了 if < 初始化长度 就是流量还没有到红线 此时想channel添加数 如果用户结束链接就想channel取数 哈哈非常easy吧 大家先动手实践一下,再看我的代码 这个就放在router文件夹下吧flowControl.go ``` package router import "log" type BucketLimit struct { count int // 定义最大流量 bucket chan int // 定义令牌桶 } func NewBucketLimit(cc int) *BucketLimit { return &BucketLimit{ cc, make(chan int,cc), } } // 获得令牌 func (cl *BucketLimit) GetConn() bool { // 如果满了 if len(cl.bucket) >= cl.count { log.Println("满了") return false } // 没有满 就颁发令牌 cl.bucket <- 1 return true } // 回收令牌 func (cl *BucketLimit) ReleaseConn() { i := <-cl.bucket log.Printf("New connction coming:%d\n",i) } ``` 是不是非常简单啊! 问题来了,写是写出来了,但是没有和http关联啊! 我们先做一个粗粒度的限流把,限制整个服务的流量 新建一个middleware.go ``` package router import ( "GolangWebCourseware/response" "github.com/julienschmidt/httprouter" "net/http" ) type middleWareHandle struct { R *httprouter.Router L *BucketLimit // 流控 } func NewMiddleWareHandler(r *httprouter.Router,cc int) http.Handler { handle := &middleWareHandle{} handle.R =r handle.L = NewBucketLimit(cc) return handle } // 流控核心 func (m *middleWareHandle) ServeHTTP(w http.ResponseWriter,r *http.Request) () { // 从桶中获得令牌 if !m.L.GetConn() { response.SendNormalResponse(w,"Too many requests",http.StatusTooManyRequests) return } m.R.ServeHTTP(w,r) defer func() { // 当连接结束 令牌返回令牌桶中 m.L.ReleaseConn() }() } ``` 修改main方法 ``` func main() { registerRouter := router.RegisterRouter()// 注册路由 fmt.Println("server is runing ...") // middleware 接替 handler := router.NewMiddleWareHandler(registerRouter,1000) err := http.ListenAndServe(":8085", handler) if err != nil { fmt.Println("server error:",err.Error()) } } ``` 本次课程代码: [https://github.com/dollarkillerx/GolangWebCourseware/tree/%E6%B5%81%E6%8E%A7](https://github.com/dollarkillerx/GolangWebCourseware/tree/%E6%B5%81%E6%8E%A7)