go-zero之分散式鎖
由於支付回撥可能會有併發操作,所以在修改訂單表的時候最好加鎖機制進行併發控制,避免資料混亂。
這裡用到
redis
鎖來控制
測試
測試程式碼
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"runtime"
"strconv"
"strings"
"sync"
)
var wg sync.WaitGroup
func main() {
fmt.Printf("runtime.NumCPU(): %v\n", runtime.NumCPU())
runtime.GOMAXPROCS(runtime.NumCPU())
apiUrl := "http://$IP/order/find_order"
var bearer = "Bearer 4a0AUsvuHHjCzqc62MZDQ-WZwJTccG3TkPcOOkku4_o....."
data := url.Values{}
data.Set("order_id", "P20211027115409955995")
for i := 0; i < 10; i++ {
wg.Add(4)
go PostApiA(apiUrl, bearer, data)
go PostApiB(apiUrl, bearer, data)
go PostApiC(apiUrl, bearer, data)
go PostApiD(apiUrl, bearer, data)
}
wg.Wait()
}
func PostApiA(apiUrl string, bearer string, data url.Values) {
defer wg.Done()
req, _ := http.NewRequest("POST", apiUrl, strings.NewReader(data.Encode()))
req.Header.Add("Authorization", bearer)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("A failed %s\n", err)
}
b, _ := ioutil.ReadAll(resp.Body)
log.Println("A output ", string(b))
}
func PostApiB(apiUrl string, bearer string, data url.Values) {
defer wg.Done()
req, _ := http.NewRequest("POST", apiUrl, strings.NewReader(data.Encode()))
req.Header.Add("Authorization", bearer)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("B failed %s\n", err)
}
b, _ := ioutil.ReadAll(resp.Body)
log.Println("B output ", string(b))
}
func PostApiC(apiUrl string, bearer string, data url.Values) {
defer wg.Done()
req, _ := http.NewRequest("POST", apiUrl, strings.NewReader(data.Encode()))
req.Header.Add("Authorization", bearer)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("C failed %s\n", err)
}
b, _ := ioutil.ReadAll(resp.Body)
log.Println("C output ", string(b))
}
func PostApiD(apiUrl string, bearer string, data url.Values) {
defer wg.Done()
req, _ := http.NewRequest("POST", apiUrl, strings.NewReader(data.Encode()))
req.Header.Add("Authorization", bearer)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("D failed %s\n", err)
}
b, _ := ioutil.ReadAll(resp.Body)
log.Println("D output ", string(b))
}
修改訂單狀態
結果顯示有多次修改
加鎖
修改訂單之前加鎖
// 分散式鎖
redisLock := redis.NewRedisLock(l.svcCtx.Config.Redis.NewRedis(), "updateOrder")
redisLock.SetExpire(1) // 過期時間
if ok, err := redisLock.Acquire(); !ok || err != nil {
logx.Error("當前有其它使用者正在操作,請稍後重試:",err)
return &order.Response{
Code: 201,
Msg: "當前有其它使用者正在操作,請稍後重試",
Data: data,
}, nil
}
defer func() {
recover()
// 釋放鎖
_, _ = redisLock.Release()
}()
繼續修改
結果只有一個能修改成功
本作品採用《CC 協議》,轉載必須註明作者和本文連結