mirror of
https://github.com/go-kratos/kratos.git
synced 2025-03-17 21:07:54 +02:00
avoid data race while using Rand in sre_breaker.go
This commit is contained in:
parent
382bbf3c4f
commit
57a614077f
@ -3,6 +3,7 @@ package breaker
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@ -11,6 +12,12 @@ import (
|
||||
"github.com/bilibili/kratos/pkg/stat/metric"
|
||||
)
|
||||
|
||||
var (
|
||||
// rand.New(...) returns a non thread safe object
|
||||
random = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
lock sync.Mutex
|
||||
)
|
||||
|
||||
// sreBreaker is a sre CircuitBreaker pattern.
|
||||
type sreBreaker struct {
|
||||
stat metric.RollingCounter
|
||||
@ -19,7 +26,6 @@ type sreBreaker struct {
|
||||
request int64
|
||||
|
||||
state int32
|
||||
r *rand.Rand
|
||||
}
|
||||
|
||||
func newSRE(c *Config) Breaker {
|
||||
@ -30,7 +36,6 @@ func newSRE(c *Config) Breaker {
|
||||
stat := metric.NewRollingCounter(counterOpts)
|
||||
return &sreBreaker{
|
||||
stat: stat,
|
||||
r: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
|
||||
request: c.Request,
|
||||
k: c.K,
|
||||
@ -69,14 +74,14 @@ func (b *sreBreaker) Allow() error {
|
||||
atomic.CompareAndSwapInt32(&b.state, StateClosed, StateOpen)
|
||||
}
|
||||
dr := math.Max(0, (float64(total)-k)/float64(total+1))
|
||||
rr := b.r.Float64()
|
||||
drop := trueOnProba(dr)
|
||||
if log.V(5) {
|
||||
log.Info("breaker: drop ratio: %f, real rand: %f, drop: %v", dr, rr, dr > rr)
|
||||
log.Info("breaker: drop ratio: %f, drop: %t", dr, drop)
|
||||
}
|
||||
if dr <= rr {
|
||||
return nil
|
||||
if drop {
|
||||
return ecode.ServiceUnavailable
|
||||
}
|
||||
return ecode.ServiceUnavailable
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *sreBreaker) MarkSuccess() {
|
||||
@ -88,3 +93,10 @@ func (b *sreBreaker) MarkFailed() {
|
||||
// drop ratio higher.
|
||||
b.stat.Add(0)
|
||||
}
|
||||
|
||||
func trueOnProba(proba float64) (truth bool) {
|
||||
lock.Lock()
|
||||
truth = random.Float64() < proba
|
||||
lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
||||
"github.com/bilibili/kratos/pkg/stat/metric"
|
||||
xtime "github.com/bilibili/kratos/pkg/time"
|
||||
|
||||
@ -29,7 +29,6 @@ func getSREBreaker() *sreBreaker {
|
||||
stat := metric.NewRollingCounter(counterOpts)
|
||||
return &sreBreaker{
|
||||
stat: stat,
|
||||
r: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
|
||||
request: 100,
|
||||
k: 2,
|
||||
@ -147,6 +146,21 @@ func TestSRESummary(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestTrueOnProba(t *testing.T) {
|
||||
const proba = math.Pi / 10
|
||||
const total = 100000
|
||||
const epsilon = 0.05
|
||||
var count int
|
||||
for i := 0; i < total; i++ {
|
||||
if trueOnProba(proba) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
ratio := float64(count) / float64(total)
|
||||
assert.InEpsilon(t, proba, ratio, epsilon)
|
||||
}
|
||||
|
||||
func BenchmarkSreBreakerAllow(b *testing.B) {
|
||||
breaker := getSRE()
|
||||
b.ResetTimer()
|
||||
|
Loading…
x
Reference in New Issue
Block a user