今天的课程安排
流控
*****
### 流控
为什么要流控呢?
> 一年一度的「双 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)
- 初认GOlang Web
- 关于环境配置
- 路由
- 路由进阶与目录架构
- 静态文件服务器
- 自定义Middleware
- 与MySQL起舞
- 主从模式概念
- 部署主从集群
- 分库分表
- 补充:事务
- 补充:常用SQL示例
- Template使用
- 一些小的,但是要知道的东西
- 调度任务
- 流控算法
- 鉴权
- JWT鉴权前置知识:加密解密
- session
- 文件上传与下载
- 带缓存读写拷贝io
- 参考
- 写好的文件上传
- 文件下载
- 拓展:秒传功能实现
- 扩展:分块上传和断点续传
- 扩展:分块上传
- 扩展:断点续传
- 扩展:分布式存储
- 部署ceph集群
- cephAuth
- go操作ceph集群
- 扩展:云存储
- go操作oss
- 补充:xorm框架
- 命令小结
- 补充:xorm框架高级部分
- 补充
- MongoDB
- 基础概念
- 简简单单NoSql
- 操作集合(Collection)
- 操作文档(Document)
- 带条件的文档 db.find
- 复杂条件抽文档 db.find
- redis
- redis操作
- go操作redis
- (新增)配置鉴权
- 密码学
- 文件校验算法
- 未来课程的安排
- RPC实践
- 爬虫
- 正则表达式
- 爬取手机号
- 爬取邮箱
- 爬取超链接
- 爬取身份证号
- 并发爬图片
- 扩展:CICD
- GO实现自动化部署系统
- 国际化支持
- 并发带来问题的解决
- GOWEB小知识
- Sync包讲解
- sync.Pool
- 千万级WebSocket消息推送
- 微服务入门:开篇
- 路由通讯
- RabbitMQ
- RabbitMQ工作原理和转发模式
- Dcoker 下 RabbitMQ and Ui
- Go操作RabbitMQ
- 初步微服务
- go-micro
- 补充:consul
- 快速入门
- 补充:consul超时
- 微服务架构
- 微服务架构全景图
- 服务注册和发现
- raft协议基本概念
- raft协议leader选举详解
- raft协议日志复制详解
- raft协议safefy详解
- rpc调用个服务监控
- etcd
- 命令行使用
- Golang操作etcd
- GO操作etcd OP方式 (分布式锁基础)
- etcd 分布式集群乐观锁
- (新增)鉴权
- 服务注册
- 服务发现原理
- 选项设计模式介绍
- 基于插件的注册组建
- 课前知识
- etcd注册开发1
- ffmpeg
- 2.0新的启航
- 高可用Mysql
- mysql逻辑架构
- 常见的MySQL高可用方案
- 索引
- MYSQL调优
- 什么影响了MYSQL的性能
- Mysql 服务器参数配置
- Go深入并发
- 基本同步原语
- 扩展同步原语
- 原子操作
- M P G 模型
- 简单的消息总线
- GoMicro入门
- GO任务池编写
- GO依赖注入
- 一些补充
- golang defer在什么时候执行
- 分布式理论篇(面试吹牛必备)
- CAP理论
- Raft协议
- 保证注册中心的可靠性
- 链路追踪
- 怎么实现强一致性