基于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,密碼默認(rèn)是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, // 設(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個,則會阻塞
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信號量,來控制協(xié)程的數(shù)量,經(jīng)過測試,發(fā)現(xiàn)limit,loginNum,影響到最后成功的結(jié)果,這其中的的原理我還暫時沒有想清楚。limit為50,loginNum為2000,會存在服務(wù)端正常返回,但是客戶端報EOF,limit為50,loginNum為500的時候,不會出現(xiàn)EOF問題,那說明limit為50是沒問題的,按照道理說,及時loginNum增大到100000也不會有問題,但是卻出現(xiàn)了問題,后面再解決吧。
現(xiàn)在已經(jīng)拿到了2000個用戶的token了,我們使用jemter工具來進行壓測
到此這篇關(guān)于Golang實現(xiàn)Redis分布式鎖解決秒殺問題的文章就介紹到這了,更多相關(guān)Golang Redis解決秒殺問題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Golang中NewTimer計時器的底層實現(xiàn)原理
本文將主要介紹一下Go語言中的NewTimer,首先展示基于NewTimer創(chuàng)建的定時器來實現(xiàn)超時控制。接著通過一系列問題的跟進,展示了NewTimer的底層實現(xiàn)原理,需要的可以參考一下2023-05-05
golang框架gin的日志處理和zap lumberjack日志使用方式
這篇文章主要介紹了golang框架gin的日志處理和zap lumberjack日志使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
Go語言數(shù)據(jù)結(jié)構(gòu)之單鏈表的實例詳解
鏈表由一系列結(jié)點(鏈表中每一個元素稱為結(jié)點)組成,結(jié)點可以在運行時動態(tài)生成。本文將通過五個例題帶大家深入了解Go語言中單鏈表的用法,感興趣的可以了解一下2022-08-08
go實現(xiàn)服務(wù)優(yōu)雅關(guān)閉的示例
本文主要介紹了go實現(xiàn)服務(wù)優(yōu)雅關(guān)閉的示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
GoFrame?gredis緩存DoVar及Conn連接對象的自動序列化
這篇文章主要為大家介紹了GoFrame?gredis干貨DoVar?Conn連接對象自動序列化詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06
Go語言設(shè)計實現(xiàn)在任務(wù)欄里提醒你喝水的兔子
這篇文章主要為大家介紹了Go語言設(shè)計實現(xiàn)在任務(wù)欄里提醒你喝水的兔子示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-01-01

