基于Golang實(shí)現(xiàn)Redis分布式鎖解決秒殺問(wèn)題
先寫(xiě)一個(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;登錄是通過(guò)2個(gè)字段,一個(gè)是mobile,一個(gè)是password,生成了mobile從1到2000,密碼默認(rèn)是123456
然后寫(xiě)一個(gè)單元測(cè)試,實(shí)現(xiàn)新注冊(cè)的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("通道的長(zhǎng)度為:",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) //把請(qǐng)求到的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)量,來(lái)控制協(xié)程的數(shù)量,經(jīng)過(guò)測(cè)試,發(fā)現(xiàn)limit,loginNum,影響到最后成功的結(jié)果,這其中的的原理我還暫時(shí)沒(méi)有想清楚。limit為50,loginNum為2000,會(huì)存在服務(wù)端正常返回,但是客戶端報(bào)EOF,limit為50,loginNum為500的時(shí)候,不會(huì)出現(xiàn)EOF問(wèn)題,那說(shuō)明limit為50是沒(méi)問(wèn)題的,按照道理說(shuō),及時(shí)loginNum增大到100000也不會(huì)有問(wèn)題,但是卻出現(xiàn)了問(wèn)題,后面再解決吧。
現(xiàn)在已經(jīng)拿到了2000個(gè)用戶的token了,我們使用jemter工具來(lái)進(jìn)行壓測(cè)
到此這篇關(guān)于Golang實(shí)現(xiàn)Redis分布式鎖解決秒殺問(wèn)題的文章就介紹到這了,更多相關(guān)Golang Redis解決秒殺問(wèn)題內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang基礎(chǔ)之字符串與int、int64類(lèi)型互相轉(zhuǎn)換
這篇文章主要給大家介紹了關(guān)于golang基礎(chǔ)之字符串與int、int64類(lèi)型互相轉(zhuǎn)換的相關(guān)資料,在Go語(yǔ)言中string轉(zhuǎn)int是一項(xiàng)常見(jiàn)的操作,需要的朋友可以參考下2023-07-07
Go多線程中數(shù)據(jù)不一致問(wèn)題的解決方案(sync鎖機(jī)制)
在Go語(yǔ)言的并發(fā)編程中,如何確保多個(gè)goroutine安全地訪問(wèn)共享資源是一個(gè)關(guān)鍵問(wèn)題,Go語(yǔ)言提供了sync包,其中包含了多種同步原語(yǔ),用于解決并發(fā)編程中的同步問(wèn)題,本文將詳細(xì)介紹sync包中的鎖機(jī)制,需要的朋友可以參考下2024-10-10
golang基礎(chǔ)之Gocurrency并發(fā)
這篇文章主要介紹了golang基礎(chǔ)之Gocurrency并發(fā),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07

