go token bukkit

阅读更多
令牌桶  限流器

golang 提供了拓展库(golang.org/x/time/rate)提供了限流器组件

package main

import (
	"fmt"
	"time"
)

func main(){
	var  fillInterval = time.Microsecond * 10
	var  capacity =100
	var tokenBucket = make (chan struct{},capacity)

	//time.NewTicker 定时器
	fillToken := func(){
		//NewTimer 创建一个 Timer,它会在最少过去时间段 d 后到期,向其自身的 C 字段发送当时的时间
		ticker := time.NewTicker(fillInterval)
		for {
			select {
				case <- ticker.C:
					select{
						case tokenBucket <- struct{}{}:
						default :
					}
					fmt.Println(" current token cnt:",len(tokenBucket),time.Now())
			}
		}
	}

	go fillToken()
	time.Sleep(time.Hour)
}


//取令牌
func TakeAvailable(block bool) bool{
	var takenResult bool
	if block {
		select {
		case <-tokenBucket:
			takenResult = true
		}
	} else {
		select {
		case <-tokenBucket:
			takenResult = true
		default:
			takenResult = false
		}
	} 
	return takenResult
}




优化:
我们来思考一下,令牌桶每隔一段固定的时间向桶中放令牌,如果我们记下上一次
放令牌的时间为 t1,和当时的令牌数k1,放令牌的时间间隔为ti,每次向令牌桶中
放x个令牌,令牌桶容量为cap。现在如果有人来调用 TakeAvailable 来取n个令
牌,我们将这个时刻记为t2。在t2时刻,令牌桶中理论上应该有多少令牌呢?伪代
码如下:
cur = k1 + ((t2 - t1)/ti) * x
cur = cur > cap ? cap : cur
我们用两个时间点的时间差,再结合其它的参数,理论上在取令牌之前就完全可以
知道桶里有多少令牌了。那劳心费力地像本小节前面向channel里填充token的操
作,理论上是没有必要的。只要在每次 Take 的时候,再对令牌桶中的token数进
行简单计算,就可以得到正确的令牌数。是不是很像 惰性求值 的感觉?
5.6 服务流量限制
482在得到正确的令牌数之后,再进行实际的 Take 操作就好,这个 Take 操作只需
要对令牌数进行简单的减法即可,记得加锁以保证并发安
全。

github.com/juju/ratelimit 这个库就是这样做的


熔断器:
当我们调用其他服务时,如果失败一般会抛出并记录异常或者重试再调用几次,这个熔断器模式就是用于这个场景的。在一些场景中,如果服务暂时不可用,如果全做重试处理,可能会加重服务端的负担,并且重试多次也会导致自己的服务页面的不可用,慢慢不可用的状态会扩散到更多服务。熔断器模式通过记录最近多次的请求结果,来判断当前要调用的服务是否正常,不过失败次数过多,则下次再请求这个服务时,不再请求直接返回失败。当过一段时间后,再少量尝试调用,如果这几次调用服务都可以正常返回,则解除限制。
这个处理方法,类似电路上的保险丝,如果服务多次不能正常提供服务,则暂停再去请求服务,待过一段时候后(给服务恢复留些时间),再尝试看看服务是不是好了,如果没好,重复等待、尝试,好了的话,就跟正常调用一样。
github.com/sony/gobreaker
这个熔断器


熔断器运行中,有三种状态,分别是闭合状态、断开状态和半断开状态。

闭合状态:也是最开始的状态,这个是时候,请求过来是,是可以正常处理的。如果服务多次失败,则进入到断开状态。
断开状态:当服务不能正常提供服务时,则进入到该状态。并且熔断器每隔 timeout 时间段后,会切换到半断开状态,来再次看看服务是否已经恢复好了
半断开状态:在这个状态时,允许一定(少量)调用服务,如果这次服务调用都正常返回,则切换到闭合状态。如果其中有失败,则再次切换到断开状态。






你可能感兴趣的:(go)