go語言裏,在併發下,設置隨機數種子的方法(Seed())和隨機數其他方法(比如,Intn())是不能共存的。
背景:之前在一個項目裏需要生成隨機數,上線之後總是報有大量的499超時,代碼各種優化到沒得優化也找不出問題,哪知最後逐行排查打時間戳,居然是隨機數的原因,隨機數的產生居然需要10來ms。
當時的代碼產生隨機數是這兩句:
rand.Seed(time.Now().UnixNano())
rnd_int := rand.Intn(11)
後來查源碼文檔有說明:// Seed should not be called concurrently with any other Rand method.
所以以後還是想起他辦法去隨機數吧,比如給時間戳取模之類的、用shuffle()方法洗牌再取之類。
最後,寫了個小程序,可以壓測一下看看效果:
package main
import (
"fmt"
"math/rand"
"net/http"
"strconv"
"sync"
"time"
)
type Demo struct {
rnd *rand.Rand
Lock sync.RWMutex
}
func (d Demo) Get() int {
d.Lock.RLock()
defer d.Lock.RUnlock()
d.rnd.Seed(time.Now().UnixNano())
return d.rnd.Intn(11)
}
func main() {
// 最快
http.HandleFunc("/intn", func(w http.ResponseWriter, r *http.Request) {
t1 := time.Now().UnixNano() / 1e3
rnd_int := rand.Intn(11)
t2 := time.Now().UnixNano() / 1e3
fmt.Println(t2 - t1)
rnd_str := strconv.Itoa(rnd_int)
fmt.Fprintln(w, rnd_str, r.URL.Path)
})
// 不能併發取,會慢
http.HandleFunc("/seed", func(w http.ResponseWriter, r *http.Request) {
t1 := time.Now().UnixNano() / 1e3
rand.Seed(time.Now().UnixNano())
rnd_int := rand.Intn(10-0+1) + 0
t2 := time.Now().UnixNano() / 1e3
fmt.Println(t2 - t1)
rnd_str := strconv.Itoa(rnd_int)
fmt.Fprintln(w, rnd_str, r.URL.Path)
})
// 可以併發取,加了鎖
http.HandleFunc("/seedCon", func(w http.ResponseWriter, r *http.Request) {
t1 := time.Now().UnixNano() / 1e3
rObj := Demo{
rnd : rand.New(rand.NewSource(time.Now().UnixNano())),
}
rnd_int := rObj.Get()
t2 := time.Now().UnixNano() / 1e3
fmt.Println(t2 - t1)
rnd_str := strconv.Itoa(rnd_int)
fmt.Fprintln(w, rnd_str, r.URL.Path)
})
//監聽3000端口
http.ListenAndServe(":3000", nil)
}
wrk -t200 -c200 -d60s "http://127.0.0.1:3000/intn"
wrk -t200 -c200 -d60s "http://127.0.0.1:3000/seed"
wrk -t200 -c200 -d60s "http://127.0.0.1:3000/seedCon"