基于Golang實現(xiàn)Redis分布式鎖解決秒殺問題
先寫一個腳本sql,插入2000個用戶
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個字段,一個是mobile,一個是password,生成了mobile從1到2000,密碼默認是123456
然后寫一個單元測試,實現(xiàn)新注冊的2000個用戶登錄,然后獲取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, // 設置連接池大小為 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個,則會阻塞 limit <- 1111 go obtainToken(mobile, password, &wg, limit, s) } wg.Wait() //當數(shù)據(jù)都到了通道里面之后,我們可以關閉通道 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("當前程序中的協(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轉化成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信號量,來控制協(xié)程的數(shù)量,經(jīng)過測試,發(fā)現(xiàn)limit,loginNum,影響到最后成功的結果,這其中的的原理我還暫時沒有想清楚。limit為50,loginNum為2000,會存在服務端正常返回,但是客戶端報EOF,limit為50,loginNum為500的時候,不會出現(xiàn)EOF問題,那說明limit為50是沒問題的,按照道理說,及時loginNum增大到100000也不會有問題,但是卻出現(xiàn)了問題,后面再解決吧。
現(xiàn)在已經(jīng)拿到了2000個用戶的token了,我們使用jemter工具來進行壓測
到此這篇關于Golang實現(xiàn)Redis分布式鎖解決秒殺問題的文章就介紹到這了,更多相關Golang Redis解決秒殺問題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Go多線程中數(shù)據(jù)不一致問題的解決方案(sync鎖機制)
在Go語言的并發(fā)編程中,如何確保多個goroutine安全地訪問共享資源是一個關鍵問題,Go語言提供了sync包,其中包含了多種同步原語,用于解決并發(fā)編程中的同步問題,本文將詳細介紹sync包中的鎖機制,需要的朋友可以參考下2024-10-10