基于Golang實(shí)現(xiàn)Redis分布式鎖解決秒殺問題
先寫一個(gè)腳本sql,插入2000個(gè)用戶
INSERT INTO sys_users (mobile, password) SELECT numbers.n AS mobile, '$2a$10$zKQfSn/GCcR6MX4nHk3MsOMhJnI0qxN4MFdiufDMH2wzuTaR9G1sq' AS password FROM ( SELECT ones.n + tens.n*10 + hundreds.n*100 + thousands.n*1000 + 1 AS n FROM (SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) ones CROSS JOIN (SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) tens CROSS JOIN (SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) hundreds CROSS JOIN (SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) thousands ORDER BY n ) numbers LIMIT 2000;
登錄是通過2個(gè)字段,一個(gè)是mobile,一個(gè)是password,生成了mobile從1到2000,密碼默認(rèn)是123456
然后寫一個(gè)單元測試,實(shí)現(xiàn)新注冊的2000個(gè)用戶登錄,然后獲取token
package system import ( "encoding/json" "fmt" "io/ioutil" "net/http" "os" "reflect" "runtime" "strings" "sync" "testing" "time" ) var Global_client *http.Client func GetGlobalClient() { client := &http.Client{ Transport: &http.Transport{ MaxIdleConns: 20, // 設(shè)置連接池大小為 200 }, } Global_client = client } func TestBaseApi_TokenNext(t *testing.T) { var wg sync.WaitGroup loginNum := 2000 GetGlobalClient() s := make(chan string, loginNum) limit := make(chan int, 20000) //go prilimit(limit) go Show() for i := 1; i <= 2000000; i++ { mobile := fmt.Sprintf("%d", i) wg.Add(1) password := "123456" //向通道中發(fā)送值,如果滿了500個(gè),則會(huì)阻塞 limit <- 1111 go obtainToken(mobile, password, &wg, limit, s) } wg.Wait() //當(dāng)數(shù)據(jù)都到了通道里面之后,我們可以關(guān)閉通道 close(s) fmt.Println("通道的長度為:",len(s)) file, err := os.OpenFile("E:\\Go\\goproject\\LearnExam\\sever\\token.txt", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) defer file.Close() for token := range s { if token == "" { continue } _, err = file.WriteString(token + "\n") if err != nil { return } } } func Show() { for { num := runtime.NumGoroutine() fmt.Printf("當(dāng)前程序中的協(xié)程數(shù)量:%d\n", num) time.Sleep(1 * time.Second) } } func AppendStringToFile(filePath string, content string) error { file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) if err != nil { return err } defer file.Close() _, err = file.WriteString(content + "\n") if err != nil { return err } return nil } func obtainToken(mobile, password string, wg *sync.WaitGroup, limit chan int, s chan string) { defer wg.Done() type Body struct { Mobile string `json:"mobile"` Password string `json:"password"` } b := Body{ mobile, password, } bodymarshal, err := json.Marshal(&b) if err != nil { return } //再處理一下 reqBody := strings.NewReader(string(bodymarshal)) req, err := http.NewRequest("POST", "." + "", reqBody) if err != nil { fmt.Printf("Error creating request for user %s: %v\n", mobile, err) return } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") resp, err := Global_client.Do(req) if err != nil { //fmt.Printf("Error sending request for user %s: %v\n", mobile, err) fmt.Printf("Error sending request for user %s: %+v\n", mobile, err) fmt.Println("反射:", reflect.TypeOf(err)) fmt.Println("err是EOF,那resp是:",resp) return } //defer func(Body io.ReadCloser) { // err = Body.Close() // if err != nil { // // } //}(resp.Body) body, err := ioutil.ReadAll(resp.Body) //把請求到的body轉(zhuǎn)化成byte[] if err != nil { return } type Result struct { Code int `json:"code"` Data struct { Token string `json:"token"` } `json:"data"` } r := Result{} err = json.Unmarshal(body, &r) if err != nil { return } if r.Code == 0 { s <- r.Data.Token temp := <-limit fmt.Println("通道取值:", temp) fmt.Printf("Token obtained for user %s\n", mobile) } else { fmt.Printf("Failed to obtain token for user %s\n", mobile) } }
我們使用有緩沖的通道和sync.WaitGroup信號(hào)量,來控制協(xié)程的數(shù)量,經(jīng)過測試,發(fā)現(xiàn)limit,loginNum,影響到最后成功的結(jié)果,這其中的的原理我還暫時(shí)沒有想清楚。limit為50,loginNum為2000,會(huì)存在服務(wù)端正常返回,但是客戶端報(bào)EOF,limit為50,loginNum為500的時(shí)候,不會(huì)出現(xiàn)EOF問題,那說明limit為50是沒問題的,按照道理說,及時(shí)loginNum增大到100000也不會(huì)有問題,但是卻出現(xiàn)了問題,后面再解決吧。
現(xiàn)在已經(jīng)拿到了2000個(gè)用戶的token了,我們使用jemter工具來進(jìn)行壓測
到此這篇關(guān)于Golang實(shí)現(xiàn)Redis分布式鎖解決秒殺問題的文章就介紹到這了,更多相關(guān)Golang Redis解決秒殺問題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang基礎(chǔ)之字符串與int、int64類型互相轉(zhuǎn)換
這篇文章主要給大家介紹了關(guān)于golang基礎(chǔ)之字符串與int、int64類型互相轉(zhuǎn)換的相關(guān)資料,在Go語言中string轉(zhuǎn)int是一項(xiàng)常見的操作,需要的朋友可以參考下2023-07-07Go多線程中數(shù)據(jù)不一致問題的解決方案(sync鎖機(jī)制)
在Go語言的并發(fā)編程中,如何確保多個(gè)goroutine安全地訪問共享資源是一個(gè)關(guān)鍵問題,Go語言提供了sync包,其中包含了多種同步原語,用于解決并發(fā)編程中的同步問題,本文將詳細(xì)介紹sync包中的鎖機(jī)制,需要的朋友可以參考下2024-10-10golang基礎(chǔ)之Gocurrency并發(fā)
這篇文章主要介紹了golang基礎(chǔ)之Gocurrency并發(fā),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-07-07