go-zero之分散式鎖

charliecen發表於2021-11-01

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 協議》,轉載必須註明作者和本文連結

相關文章