go-zero之分布式锁

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 协议》,转载必须注明作者和本文链接
讨论数量: 4

当初我们用了下etcd的分布式锁。

2年前 评论

l.svcCtx.Config.Redis.NewRedis() 如何配置的? 要配置cacheRedis和Redis 两个?

1年前 评论
charliecen (楼主) 1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
39
粉丝
9
喜欢
71
收藏
102
排名:461
访问:1.9 万
私信
所有博文
社区赞助商