go 利用orm简单实现接口分布式锁

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

 

第一阶段:golang

咱们使用的orm为xorm,提现表对应的结构体以下redis

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,简单来讲这就是提现接口的业务逻辑。json

起初我并无太在乎并发的问题,我在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测试,嗯,锁成功了只有,只提现了一次,大功告成~

但愿对你们有所帮助,祝你们天天开心~

相关文章
相关标签/搜索