前提

项目业务开发已完成,开始着手准备多节点拓展方案。

项目使用go语言开发,gin框架。

项目中使用了go原生的进程锁 sync.Mutex ,用于防止高并发下,数据可能遭到重复修改的问题。

但是用了进程锁就无法支持集群部署架构,集群中每个单点都是一个进程,只能锁住自己的一部分,而操作的又是同一个DB,高并发下就会出现数据重复修改的问题。

 

因此需要引入分布式锁,用来保证多节点部署架构下,数据正常。

 

方案选择

有多种分布式锁的方案,ZooKeeper,基于数据库实现分布式锁(for update),redis等。

鉴于项目本身面对的用户量并不多,首期预计2000人,多节点方案也只是plan B,避免上线当天出现炸服的情况,有个方案可以及时拓展节点,提高承载量,

因此最终选用了redis,简单易用,而且项目本身也部署了redis,也不影响现有的架构,毕竟多引入一个中间件,就多了其他的风险。

 

redis分布式锁应用实例

redis分布式锁,用的是其Setnx(SET if Not Exists)函数, 指定的 key 不存在时,为 key 设置指定的值。

因为redis是单线程的,因此Setnx 100%是串行执行命令, 当多个请求进来时,即使同时调用Setnx,最终是按顺序返回值,达到了锁的效果。

项目中使用示例:

func main() {
    key := consts.USER_UPDATE + id
    op := ExistKeyOrSet(key, consts.DB_OP_EXPIRES_TIME)

    if !op {
	// 已存在key,表示上一次操作未结束
	appG.Response(http.StatusBadRequest, e.FREQUENT_OPERATION, nil)
	return
    }

    // 程序执行结束后删除 redis 存储数据
    defer rdb.DelKey(key)
}



func ExistKeyOrSet(key string, expiresTime time.Duration) bool {
	timestamp := time.Now().UnixNano()
	// redis的setnx方法 key存在返回false, 不存在则设置锁并返回true
	return Client.SetNX(ctx, key, timestamp, expiresTime).Val()
}

  

 

posted on 2022-06-14 09:43  Boom__Clap  阅读(76)  评论(0编辑  收藏  举报