• 企业400电话
  • 网络优化推广
  • AI电话机器人
  • 呼叫中心
  • 全 部 栏 目

    网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    go如何利用orm简单实现接口分布式锁
    POST TIME:2021-10-18 17:36

    在开发中有些敏感接口,例如用户余额提现接口,需要考虑在并发情况下接口是否会发生问题。如果用户将自己的多条提现请求同时发送到服务器,代码能否扛得住呢?一旦没做锁,那么就真的会给用户多次提现,给公司带来损失。我来简单介绍一下在这种接口开发过程中,我的做法。

    第一阶段:

    我们使用的orm为xorm,提现表对应的结构体如下

    type Participating struct {
     ID   uint     `xorm:"autoincr id" json:"id,omitempty"`
     Openid string    `xorm:"openid" json:"openid"`
     Hit   uint     `xorm:"hit" json:"hit"`
     Orderid string    `xorm:"order_id" json:"order_id"`
     Redpack uint     `xorm:"redpack" json:"redpack"`
     Status uint     `xorm:"status" json:"status"`
     Ctime  tool.JsonTime `xorm:"ctime" json:"ctime,omitempty"`
     Utime  tool.JsonTime `xorm:"utime" json:"utime,omitempty"`
     PayTime tool.JsonTime `xorm:"pay_time" json:"pay_time,omitempty"`
    }
    

    在Participating表中,是以Openid去重的,当一个Openid对应的Hit为1时,可以按照Redpack的数额提现,成功后将Status改为1,简单来说这就是提现接口的业务逻辑。

    起初我并没有太在意并发的问题,我在MySQL的提现表中设置一个字段status来记录提现状态,我只是在提现时将状态修改为2(体现中),提现完成后将status修改为1(已提现)。然后事实证明,我太天真了,用ab做了测试1s发送了1000个请求到服务器,结果。。。成功提现了6次。部分代码如下

    p_info := Participating{}
    // 查找具体提现数额
    has, _ := db.Dalmore.Where("openid = ? and hit = 1 and status = 0", openid).Get(p_info)
    if !has {
     resp.Error(errcode.NO_REDPACK_FOUND, nil, nil)
     return
    }
    
    // 改status为提现中
    p_info.Status = 2
    db.Dalmore.Cols("status").Where("openid = ? and hit = 1 and status = 0", openid).Update(p_info)
    
    // 提现p_info.Redpack
    
    

    第二阶段:

    既然出现了并发问题,那第一反应肯定的加锁啊,代码如下:

    type Set struct {
     m map[string]bool
     sync.RWMutex
    }
    
    func New() *Set {
     return Set{
     m: map[string]bool{},
     }
    }
    
    var nodelock = set.New()
    
    // 加锁
    nodelock.Lock()
    
    p_info := Participating{}
    // 查找具体提现数额
    has, _ := db.Dalmore.Where("openid = ? and hit = 1 and status = 0", openid).Get(p_info)
    if !has {
     resp.Error(errcode.NO_REDPACK_FOUND, nil, nil)
     return
    }
    
    // 改status为提现中
    p_info.Status = 2
    db.Dalmore.Cols("status").Where("openid = ? and hit = 1 and status = 0", openid).Update(p_info)
    
    // 释放锁
    nodelock.Unlock()
    
    // 提现p_info.Redpack
    
    

    加了锁以后。。。emem,允许多次提现的问题解决了,但是这个锁限制的范围太多了,直接让这段加锁代码变成串行,这大大降低了接口性能。而且,一旦部署多个服务端,这个锁又会出现多次提现的问题,因为他只能拦住这一个服务的并发。看来得搞一个不影响性能的分布式才是王道啊。

    第三阶段:

    利用redis,设置一个key为openid的分布式锁,并设置一个过期时间可以解决当前的这个问题。但是难道就没别的办法了吗?当然是有的,golang的xorm中Update函数其实是有返回值的:num,err,我就是利用num做了个分布式锁。

    //记录update修改条数
    num, err := db.Dalmore.Cols("status").Where("openid = ? and status = 0 and hit = 1", openid).Update(p_update)
    if err != nil {
     logger.Runtime().Debug(map[string]interface{}{"error": err.Error()}, "error while updating")
     resp.Error(errcode.INTERNAL_ERROR, nil, nil)
     return
    }
    
    // 查看update操作到底修改了多少条数据,起到了分布式锁的作用
    if num != 1 {
     resp.Error(errcode.NO_REDPACK_FOUND, nil, nil)
     return
    }
    
    p_info := Participating{}
    _, err := db.Dalmore.Where("openid = ? and status = 2", openid).Get(p_info)
    if err != nil {
     logger.Runtime().Debug(map[string]interface{}{"error": err.Error()}, "error while selecting")
     resp.Error(errcode.INTERNAL_ERROR, nil, nil)
     return
    }
    
    // 提现p_info.Redpack
    
    

    其实有点投机取巧的意思,利用xorm的Update函数,我们将核对并发处理请求下数据准确性的问题抛给了MySQL,毕竟MySQL是经过千锤百炼的。再用ab测试,嗯,锁成功了只有,只提现了一次,大功告成~

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

    您可能感兴趣的文章:
    • redis分布式锁的go-redis实现方法详解
    • 详解一种用django_cache实现分布式锁的方式
    • mongo分布式锁Java实现方法(推荐)
    • 浅谈Redis分布式锁的正确实现方式
    • Java使用Redisson分布式锁实现原理
    • Redis Template实现分布式锁的实例代码
    • Go 语言下基于Redis分布式锁的实现方式
    上一篇:golang基础之Interface接口的使用
    下一篇:go实现redigo的简单操作
  • 相关文章
  • 

    关于我们 | 付款方式 | 荣誉资质 | 业务提交 | 代理合作


    © 2016-2020 巨人网络通讯

    时间:9:00-21:00 (节假日不休)

    地址:江苏信息产业基地11号楼四层

    《增值电信业务经营许可证》 苏B2-20120278

    X

    截屏,微信识别二维码

    微信号:veteran88

    (点击微信号复制,添加好友)

     打开微信