searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

分布式系统-秒杀

2023-08-22 02:00:20
9
0

链接暴露

打开谷歌的开发者模式,查看网页代码,请求时就会有接口地址

按钮置灰

在开放前按钮一直置灰,URL 不会暴露,定期更换地址

URL 动态化

@RequestMapping(value = "/{goodsId}/getUrl")//获取动态 md5
@RequestMapping(value = "/{seckillGoodsId}/{md5}/execution")//执行操作,验证 md5

 

服务单一职责

单独建立一个数据库,订单服务对应订单库,秒杀是秒杀库。

单一职责的好处就是就算秒杀没抗住,秒杀库崩了,服务挂了,也不会影响到其他的服务。(强行高可用)


资源静态化

秒杀一般都是特定的商品还有页面模板(前后端分离)与 cdn 加速


库存预热

提前把商品的库存加载到 Redis,流程都在 Redis 里面去做,等秒杀结束再异步的去修改库存

注意点

采用主从,正常情况没问题,但是高并发的就会同时读到剩余量

Lua

广泛作为其它语言的嵌入脚本(互联网游戏等),尤其是 C/C++,语法简单、小巧

简单语法

局部变量效率更高,redis 使用 EVAL 命令来直接执行指定的 Lua 脚本

  1. nil 空
  2. boolean 布尔值
  3. number 数字
  4. string 字符串
  5. table 表:lua 专有数据结构,既是数组又类似 HashMap

原子执行

Lua 脚本在 Redis 中是以原子方式执行的,在 Redis 服务器执行EVAL命令时,在命令执行完毕并向调用者返回结果之前,只会执行当前命令指定的 Lua 脚本包含的所有逻辑,其它客户端发送的命令将被阻塞,直到EVAL命令执行完毕为止


分布式锁实现方案(使用 lua 脚本)

Redission 是 Redis 官方推荐的客户端,提供了一个 RLock 的锁,RLock 继承自 juc 的 Lock 接口,提供了中断,超时,尝试获取锁等操作,支持可重入,互斥等特性

加锁

为了实现加锁的原子性,Redisson 使用 Lua 脚本的形式进行加锁

if (redis.call('exists', KEYS[1]) == 0) then 
    redis.call('hset', KEYS[1], ARGV[2], 1); 
    redis.call('pexpire', KEYS[1], ARGV[1]); 
    return nil; 
    end; 
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
    redis.call('hincrby', KEYS[1], ARGV[2], 1); 
    redis.call('pexpire', KEYS[1], ARGV[1]);
    return nil;
    end;
return redis.call('pttl', KEYS[1]);

Lease 续约

如果锁设置了持有锁的超时时间,在超时后会进行锁的释放,如果获取锁的时候不指定持有锁的时间,那么默认获取锁 30s 后超时。为了防止任务没有执行完就释放锁,Redisson 使用一个守护线程(看门狗任务)定时刷新这个锁超时时间进行续约,也就是只要这个锁被获取了,则力保这个锁一直不超时,除非获取锁的线程主动释放。由于获取到锁和这个续命任务的守护线程是在同一个线程的,当获取锁的线程挂掉了,意味着刷新任务的线程也会停止执行,就不会再刷新锁的超时时间

解锁

解锁同样使用 Lua 脚本执行

if (redis.call('exists', KEYS[1]) == 0) then 
    redis.call('publish', KEYS[2], ARGV[1]); 
    return 1; 
    end;
 if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then 
    return nil;
    end; 
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); 
if (counter > 0) then 
    redis.call('pexpire', KEYS[1], ARGV[2]); 
    return 0; 
else 
    redis.call('del', KEYS[1]); 
    redis.call('publish', KEYS[2], ARGV[1]); 
    return 1; 
    end; 
return nil;

 

首先判断锁是否是存在的,如果不存在直接返回 nil。 如果该线程持有锁,则对当前的重入值 -1,如果计算完后大于 0,重新设置超时持有实践返回 0; 如果算完不大于0,删除这个 Hash,并且进行广播,通知 watch dog 停止进行刷新,并且返回 1

0条评论
0 / 1000
胡****冲
3文章数
0粉丝数
胡****冲
3 文章 | 0 粉丝
胡****冲
3文章数
0粉丝数
胡****冲
3 文章 | 0 粉丝
原创

分布式系统-秒杀

2023-08-22 02:00:20
9
0

链接暴露

打开谷歌的开发者模式,查看网页代码,请求时就会有接口地址

按钮置灰

在开放前按钮一直置灰,URL 不会暴露,定期更换地址

URL 动态化

@RequestMapping(value = "/{goodsId}/getUrl")//获取动态 md5
@RequestMapping(value = "/{seckillGoodsId}/{md5}/execution")//执行操作,验证 md5

 

服务单一职责

单独建立一个数据库,订单服务对应订单库,秒杀是秒杀库。

单一职责的好处就是就算秒杀没抗住,秒杀库崩了,服务挂了,也不会影响到其他的服务。(强行高可用)


资源静态化

秒杀一般都是特定的商品还有页面模板(前后端分离)与 cdn 加速


库存预热

提前把商品的库存加载到 Redis,流程都在 Redis 里面去做,等秒杀结束再异步的去修改库存

注意点

采用主从,正常情况没问题,但是高并发的就会同时读到剩余量

Lua

广泛作为其它语言的嵌入脚本(互联网游戏等),尤其是 C/C++,语法简单、小巧

简单语法

局部变量效率更高,redis 使用 EVAL 命令来直接执行指定的 Lua 脚本

  1. nil 空
  2. boolean 布尔值
  3. number 数字
  4. string 字符串
  5. table 表:lua 专有数据结构,既是数组又类似 HashMap

原子执行

Lua 脚本在 Redis 中是以原子方式执行的,在 Redis 服务器执行EVAL命令时,在命令执行完毕并向调用者返回结果之前,只会执行当前命令指定的 Lua 脚本包含的所有逻辑,其它客户端发送的命令将被阻塞,直到EVAL命令执行完毕为止


分布式锁实现方案(使用 lua 脚本)

Redission 是 Redis 官方推荐的客户端,提供了一个 RLock 的锁,RLock 继承自 juc 的 Lock 接口,提供了中断,超时,尝试获取锁等操作,支持可重入,互斥等特性

加锁

为了实现加锁的原子性,Redisson 使用 Lua 脚本的形式进行加锁

if (redis.call('exists', KEYS[1]) == 0) then 
    redis.call('hset', KEYS[1], ARGV[2], 1); 
    redis.call('pexpire', KEYS[1], ARGV[1]); 
    return nil; 
    end; 
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
    redis.call('hincrby', KEYS[1], ARGV[2], 1); 
    redis.call('pexpire', KEYS[1], ARGV[1]);
    return nil;
    end;
return redis.call('pttl', KEYS[1]);

Lease 续约

如果锁设置了持有锁的超时时间,在超时后会进行锁的释放,如果获取锁的时候不指定持有锁的时间,那么默认获取锁 30s 后超时。为了防止任务没有执行完就释放锁,Redisson 使用一个守护线程(看门狗任务)定时刷新这个锁超时时间进行续约,也就是只要这个锁被获取了,则力保这个锁一直不超时,除非获取锁的线程主动释放。由于获取到锁和这个续命任务的守护线程是在同一个线程的,当获取锁的线程挂掉了,意味着刷新任务的线程也会停止执行,就不会再刷新锁的超时时间

解锁

解锁同样使用 Lua 脚本执行

if (redis.call('exists', KEYS[1]) == 0) then 
    redis.call('publish', KEYS[2], ARGV[1]); 
    return 1; 
    end;
 if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then 
    return nil;
    end; 
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); 
if (counter > 0) then 
    redis.call('pexpire', KEYS[1], ARGV[2]); 
    return 0; 
else 
    redis.call('del', KEYS[1]); 
    redis.call('publish', KEYS[2], ARGV[1]); 
    return 1; 
    end; 
return nil;

 

首先判断锁是否是存在的,如果不存在直接返回 nil。 如果该线程持有锁,则对当前的重入值 -1,如果计算完后大于 0,重新设置超时持有实践返回 0; 如果算完不大于0,删除这个 Hash,并且进行广播,通知 watch dog 停止进行刷新,并且返回 1

文章来自个人专栏
分布式架构
3 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0