抓数据的网站限定1秒只能有10次请求,因此设计了一个令牌管理机制来控制请求数量。
设计思路如下:
发请求前需要先获取令牌
限定某时间段内的发放的令牌数量
任务执行完成后不能归还令牌,只能使用定时器不断重置令牌
如果当前goroutine数量过多时也不重置令牌
package mainimport ( "errors"
"fmt"
"sync"
"sync/atomic"
"time")//节流器type throttle struct {
D time.Duration //周期是D,
C int64 //限制一个周期最多操作C次
Mu sync.Mutex
Token chan bool //令牌池
num int64 //当前的goroutine数量
maxNum int64 //允许工作goroutine最大数量}//如果两个周期后还没有申请到令牌,就报错超时//目前用不到,如果限制routine最大数量需要靠这来监控var ErrApplyTimeout = errors.New("apply token time out")func NewThrottle(D time.Duration, C, maxNum int64) *throttle {
instance := &throttle{
D: D,
C: C,
Token: make(chan bool, C),
maxNum: maxNum,
} go instance.reset() return instance
}//每周期重新填充一次令牌池func (t *throttle) reset() {
ticker := time.NewTicker(t.D) for _ = range ticker.C { //goroutine数量不超过最大数量时再填充令牌池
if t.num >= t.maxNum { continue
}
t.Mu.Lock()
supply := t.C - int64(len(t.Token))
fmt.Printf("reset token:%d\n", supply) for supply > 0 {
t.Token <- true
supply--
}
t.Mu.Unlock()
}
}//申请令牌,如果过两个周期还没申请到就报超时退出func (t *throttle) ApplyToken() (bool, error) { select { case <-t.Token: return true, nil
case <-time.After(t.D * 2): return false, ErrApplyTimeout
}
}func (t *throttle) Work(job func()) { if ok, err := t.ApplyToken(); !ok {
fmt.Println(err) return
} go func() {
atomic.AddInt64(&t.num, 1) defer atomic.AddInt64(&t.num, -1)
job()
}()
}func main() {
t := NewThrottle(time.Second, 10, 20) //每秒10次,同时最多20个routine存在
for {
t.Work(doWork)
}
}//真正的工作函数 假设每个需要执行5秒func doWork() {
fmt.Println(time.Now())
<-time.After(5 * time.Second)
}以下是最初设计的,后来实际生产的时候并没有用上面的代码。而是用了另一种简陋实现
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦