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 协议》,转载必须注明作者和本文链接
当初我们用了下etcd的分布式锁。
@frans :+1:
l.svcCtx.Config.Redis.NewRedis() 如何配置的? 要配置cacheRedis和Redis 两个?