当前位置: 首页 > news >正文

德洲网站建设wordpress手动安装插件

德洲网站建设,wordpress手动安装插件,wordpress个性标签,没有下载功能的网页视频怎么下载Golang实现Redis分布式锁#xff08;Lua脚本可重入自动续期#xff09; 1 概念 应用场景 Golang自带的Lock锁单机版OK#xff08;存储在程序的内存中#xff09;#xff0c;分布式不行 分布式锁#xff1a; 简单版#xff1a;redis setnx》加锁设置过期时间需要保证原…Golang实现Redis分布式锁Lua脚本可重入自动续期 1 概念 应用场景 Golang自带的Lock锁单机版OK存储在程序的内存中分布式不行 分布式锁 简单版redis setnx》加锁设置过期时间需要保证原子性》lua脚本完整版redis Lua脚本实现可重入自动续期》hset结构 应用场景 防止用户重复下单锁住用户id防止商品超卖问题锁住账户防止并发操作 例如我本地启两个端口跑两个相同服务然后通过Nginx反向代理分别将请求均衡打到两个服务模拟分布式微服务最后通过Jmeter模拟高并发场景。同时我在代码里添加上lock锁。 可以看到还是有消费到相同数据出现超卖现象这是因为lock锁是在go程序的内存只能锁住当前程序。如果是分布式的话就需要涉及分布式锁。 注意本地通过MacJmeterIrisNginx模拟分布式场景详情可见https://blog.csdn.net/weixin_45565886/article/details/136635997 package mainimport (contextgithub.com/go-redis/redis/v8github.com/kataras/iris/v12context2 github.com/kataras/iris/v12/contextmyTest/demo_home/redis_demo/distributed_lock/constantservice2 myTest/demo_home/redis_demo/distributed_lock/other_svc/servicesync )func main() {constant.RedisCli redis.NewClient(redis.Options{Addr: localhost:6379,DB: 0,})_, err : constant.RedisCli.Set(context.TODO(), constant.AppleKey, 500, -1).Result()if err ! nil err ! redis.Nil {panic(err)}app : iris.New()xLock2 : new(sync.Mutex)app.Get(/consume, func(c *context2.Context) {xLock2.Lock()defer xLock2.Unlock()service2.GoodsService2.Consume()c.JSON(ok port:9999)})app.Listen(:9999, nil) }分布式锁必备特性 分布式锁需要具备的特性 独占性(排他性)任何时刻有且仅有一个线程持有高可用redis集群情况下不能因为某个节点挂了而出现获取锁失败和释放锁失败的情况防死锁杜绝死锁必须有超时控制机制或撤销操作 Expire key不乱抢防止乱抢。自己只能unlock自己的锁lua脚本保证原子性且只删除自己的锁重入性同一个节点的同一个线程如果获得锁之后它也可以再次获取这个锁 setnx只能解决有无分布式锁hset 解决可重入问题记录加锁次数 hset zyRedisLock uuid:threadID 3 2 思路分析 宕机与过期 如果加锁成功之后某个Redis节点宕机该锁一直得不到释放就会导致其他Redis节点加锁失败。 加锁时需要设置过期时间 //通过lua脚本保证加锁与设置过期时间的原子性func (r *RedisLock) TryLock() bool {//通过lua脚本加锁[hincrby如果key不存在则会主动创建,如果存在则会给count数加1表示又重入一次]lockCmd : if redis.call(exists, KEYS[1]) 0 or redis.call(hexists, KEYS[1], ARGV[1]) 1 then redis.call(hincrby, KEYS[1], ARGV[1], 1) redis.call(expire, KEYS[1], ARGV[2]) return 1 else return 0 endresult, err : r.redisCli.Eval(context.TODO(), lockCmd, []string{r.key}, r.Id, r.expire).Result()if err ! nil {log.Errorf(tryLock %s %v, r.key, err)return false}i : result.(int64)if i 1 {//获取锁成功自动续期go r.reNewExpire()return true}return false }防止误删key 锁过期时间设置30s业务逻辑假如要跑40s。30s后锁自动过期释放了其他线程加锁了。再过10s后业务逻辑走完了去释放锁就会出现把其他人的锁删除。【张冠李戴】 设置key时可带上线程id和uuid我这里以uuid演示。删除key之前要判断是否是自己的锁。如果是则unlock释放不是就return走。 func (r *RedisLock) Unlock() {//通过lua脚本删除锁//1. 查看锁是否存在如果不存在直接返回//2. 如果存在对锁进行hincrby -1操作,当减到0时表明已经unlock完成可以删除keydelCmd : if redis.call(hexists, KEYS[1], ARGV[1]) 0 then return nil elseif redis.call(hincrby, KEYS[1], ARGV[1], -1) 0 then return redis.call(del, KEYS[1]) else return 0 endresp, err : r.redisCli.Eval(context.TODO(), delCmd, []string{r.key}, r.Id).Result()if err ! nil err ! redis.Nil {log.Errorf(unlock %s %v, r.key, err)}if resp nil {fmt.Println(delKey, resp)return} }Lua保证原子性 加锁与设置过期时间需要保证原子性。否则如果加锁成功后还没来得及设置过期时间Redis节点挂掉了就又会出现其他节点一直获取不到锁的问题。 Lua脚本保证原子性 //lock 加锁设置过期时间 if redis.call(exists, KEYS[1]) 0 or redis.call(hexists, KEYS[1], ARGV[1]) 1 then redis.call(hincrby, KEYS[1], ARGV[1], 1) redis.call(expire, KEYS[1], ARGV[2]) return 1 else return 0 end//unlock解锁delCmd : if redis.call(hexists, KEYS[1], ARGV[1]) 0 then return nil elseif redis.call(hincrby, KEYS[1], ARGV[1], -1) 0 then return redis.call(del, KEYS[1]) else return 0 end//自动续期 renewCmd : if redis.call(hexists, KEYS[1], ARGV[1]) 1 then return redis.call(expire, KEYS[1], ARGV[2]) else return 0 end可重入锁 存在一部分业务方法里还需要继续加锁。需要实现锁的可重入记录加锁的次数。Lock几次就unLock几次。 map[string]map[string]int 可通过Redis hset结构实现 # yiRedisLock redis的key # fas421424safsfa:1 uuid线程号 # 5 加锁次数重入次数 hset yiRedisLock fas421424safsfa:1 5//通过hsethincrby 保证可重入记录加锁次数 lockCmd : if redis.call(exists, KEYS[1]) 0 or redis.call(hexists, KEYS[1], ARGV[1]) 1 then redis.call(hincrby, KEYS[1], ARGV[1], 1) redis.call(expire, KEYS[1], ARGV[2]) return 1 else return 0 enddelCmd : if redis.call(hexists, KEYS[1], ARGV[1]) 0 then return nil elseif redis.call(hincrby, KEYS[1], ARGV[1], -1) 0 then return redis.call(del, KEYS[1]) else return 0 end自动续期 相同业务耗时可能因为网络等问题而有所变化。例如我们设置分布式锁超时时间为20s但是业务因为网络问题某次耗时达到了30s这时锁就会被超时释放其他线程就能获取到锁。存在业务风险。 加锁成功之后设置自动续期启一个timer定时任务比如每10s检测一下锁有没有被释放如果没有就自动续期。 // 判断锁是否存在如果存在表明业务还未完成重新设置过期时间自动续期 renewCmd : if redis.call(hexists, KEYS[1], ARGV[1]) 1 then return redis.call(expire, KEYS[1], ARGV[2]) else return 0 end3 代码 3.1 项目结构解析 constant模块定义分布式锁名称、业务Key用于模拟扣减数据库lock模块核心模块实现分布式锁 LockTryLockUnLockNewRedisLock other_svc在其他端口启另外一个服务用于本地模拟分布式service业务类扣减商品数量其中的扣减操作涉及分布式锁main提供iris web服务 3.2 全部代码 注other_svc这里不提供与分布式锁实现无太大关系。同时为了快速演示效果部分项目结构与代码不规范。 感兴趣的朋友可以上Github查看全部代码。 Githubhttps://github.com/ziyifast/ziyifast-code_instruction/tree/main/redis_demo/distributed_lock现象 constant/const.go package constantimport github.com/go-redis/redis/v8var (BizKey XXOOAppleKey appleRedisCli *redis.Client )lock/redis_lock.go package serviceimport (contextgithub.com/go-redis/redis/v8github.com/ziyifast/logmyTest/demo_home/redis_demo/distributed_lock/constantmyTest/demo_home/redis_demo/distributed_lock/lockstrconv )type goodsService struct { }var GoodsService new(goodsService)func (g *goodsService) Consume() {redisLock : lock.NewRedisLock(constant.RedisCli, constant.BizKey)redisLock.Lock()defer redisLock.Unlock()//consume goodsresult, err : constant.RedisCli.Get(context.TODO(), constant.AppleKey).Result()if err ! nil err ! redis.Nil {panic(err)}i, err : strconv.ParseInt(result, 10, 64)if err ! nil {panic(err)}if i 0 {log.Infof(no more apple...)return}_, err constant.RedisCli.Set(context.TODO(), constant.AppleKey, i-1, -1).Result()if err ! nil err ! redis.Nil {panic(err)}log.Infof(consume success...appleID:%d, i) }service/goods_service.go package serviceimport (contextgithub.com/go-redis/redis/v8github.com/ziyifast/logmyTest/demo_home/redis_demo/distributed_lock/constantmyTest/demo_home/redis_demo/distributed_lock/lockstrconv )type goodsService struct { }var GoodsService new(goodsService)func (g *goodsService) Consume() {redisLock : lock.NewRedisLock(constant.RedisCli, constant.BizKey)redisLock.Lock()defer redisLock.Unlock()//consume goodsresult, err : constant.RedisCli.Get(context.TODO(), constant.AppleKey).Result()if err ! nil err ! redis.Nil {panic(err)}i, err : strconv.ParseInt(result, 10, 64)if err ! nil {panic(err)}if i 0 {log.Infof(no more apple...)return}_, err constant.RedisCli.Set(context.TODO(), constant.AppleKey, i-1, -1).Result()if err ! nil err ! redis.Nil {panic(err)}log.Infof(consume success...appleID:%d, i) }main.go package mainimport (contextgithub.com/go-redis/redis/v8github.com/kataras/iris/v12context2 github.com/kataras/iris/v12/contextmyTest/demo_home/redis_demo/distributed_lock/constantmyTest/demo_home/redis_demo/distributed_lock/service )func main() {constant.RedisCli redis.NewClient(redis.Options{Addr: localhost:6379,DB: 0,})_, err : constant.RedisCli.Set(context.TODO(), constant.AppleKey, 500, -1).Result()if err ! nil err ! redis.Nil {panic(err)}app : iris.New()//xLock : new(sync.Mutex)app.Get(/consume, func(c *context2.Context) {//xLock.Lock()//defer xLock.Unlock()service.GoodsService.Consume()c.JSON(ok port:8888)})app.Listen(:8888, nil) }
http://www.w-s-a.com/news/541659/

相关文章:

  • 可以做游戏的网站有哪些客户关系管理系统的主要功能
  • 整人关不掉的网站怎么做广东省网站免备案表
  • 网站设计素材edu域名网站
  • 中山学校的网站建设wordpress文章图片显示不出
  • 兰溪城市建设规划网站网站联盟的基本流程
  • 免费推广网站注册入口小说阅读网站怎么建设
  • 新网站怎么做网络推广怎么做企业网站排名
  • jsp商业网站开发网站链接如何做二维码
  • 江苏高校品牌专业建设网站怎么制作网站搜索窗口
  • 北京app建设 网站开发公司织梦网站seo
  • 大学网站 作风建设专题汽车配件外贸出口公司
  • 东莞做网站系统购物网站建设精英
  • 建设vip网站相关视频网站营销建设公司
  • 微站直播平台杭州seo按天计费
  • seo 新旧网站 两个域名福州设计网站建设
  • 如何做网站客户端如何做网络营销网站
  • 苏州网站建设制度打鱼网站建设
  • 瓜子二手车直卖网上海小红书seo
  • 天津中小企业网站制作珠海做网站的
  • 网站排名影响因素最牛的科技网站建设
  • 长春网站建设公司怎么样电商网站建设与开发期末考试
  • 品牌网站建设搭建国内外网站建设
  • 辽宁人社app一直更新整站seo定制
  • 兰州网站建设论坛装修品牌
  • 云南省城乡住房与建设厅网站用什么网站可以做电子书
  • 自己电脑怎么做网站服务器吗0基础如何做网站
  • 做网站的股哥网络整合营销方案策划
  • 网站你懂我意思正能量晚上唯品会网站开发费用
  • 网站认证金额怎么做分录网页无法访问是怎么回事
  • 樟木头建网站的wordpress自适应吸附菜单