redislock使用回顾与思考

4 篇文章 0 订阅

最近工作上做一个需求,要求一个用户一天最多两次参与机会,参与一次要获得用户的一个虚拟值。参与接口涉及到关键数据的变化,考虑在这个接口做重复提交限制,以免用户提交时多次点击出现问题,另一个方面其实也是为了幂等,不管用户怎么提交,得到的结果应该都一样。于是用到redislock做重复点击控制。

说到redislock的常见用法,一种是限流,防止多个用户同事操作,流量激增,导致接口压力过大出现非预期问题,比如(有空的话也可以自己试下):

 通过Annotation定义切点方法,定义@Aspect进行切面,切到方法调用前-@Before("pointCut()"),在里面做限流。

一种是防止重复点击,也就是我这里要做的回顾与思考。

大概设计:我们的redis lock工具,加锁是往redis中设置一个key,设置默认超时时间,超时后key失效,设置默认客户端token,用于标识每个线程,当一个客户端多次提交请求,或者多个客户端请求,每个处理请求的线程的token都不一样,保证一个线程加的锁只能被自己解锁,不能被其它线程解锁。解锁通过lua脚本。为什么通过lua脚本因为要确保上述操作是原子性。这里有篇原理性文章大家文章可供参考:https://wudashan.com/2017/10/23/Redis-Distributed-Lock-Implement/

代码定义:

加锁解锁。DefaultRedisScript是RedisScript的默认实现,单例模式

 这边设置脚本的读取从master节点来读取,说从slaver节点读取会有问题。StringRedisTemplate实例是向master连接的实例

使用:

设置redislock超时时间为2秒,也就是线程持有锁持有2秒

如果没有机会提示机会不足

如果频繁点击提示不要频繁点击

 测试结果:

通过jmeter测试重复提交,发起4个请求,期望第一次成功,后面全部失败。第一次请求后等待1秒

正常提交

没有机会

提示不要频繁点击

提示不要频繁点击

 

 总结:涉及到与用户相关的关键数据变化时,要考虑相关条件的多重校验,健壮性、原子性。从业5年以来,之前做的业务或许也有此类场景,但以前没有做过类似考量,一方面是认知不够,另一方面也是能力有限,希望通过一些总结也慢慢让自己能提升这块能力。通过本次redislock的使用,让自己认识到redis中间件的其它用处,对后端编程有一些新的认识。希望我们一起进步。

其它:

其实说到锁,或许大家都比较熟悉,除了上面说的redislock,还有代码锁,数据库锁-行锁或表锁,行锁保证数据库中的XX特性,MySQLInnodb引擎是行锁,但有些情况会使用表锁,这里不做展开。可能平时用得比较多的是代码锁,可以锁方法,也可以锁代码。锁方法时保证方法调用是串行的(可以通过日志查看线程id观察是否如此),锁代码的话要求有一个唯一不变资源。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RedisLock 是一个基于 Redis 实现的分布式锁,可以用于控制多个节点对某个资源的访问。下面是使用 RedisLock 实现分布式锁的 Java 代码示例: ```java // 引入 RedisLock 相关包 import com.github.fppt.jedismock.RedisServer; import org.redisson.Redisson; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.redisson.config.SingleServerConfig; import org.redisson.misc.RedisRunner; import java.io.IOException; import java.net.ServerSocket; public class OrderService { private RedissonClient redissonClient; private RedisServer redisServer; public OrderService() throws IOException { // 启动 Redis 服务器 redisServer = RedisRunner.getDefault().run(); // 初始化 Redisson 客户端 Config config = new Config(); SingleServerConfig serverConfig = config.useSingleServer(); serverConfig.setAddress(redisServer.getHost() + ":" + redisServer.getBindPort()); redissonClient = Redisson.create(config); } public void placeOrder(String orderId) throws InterruptedException { // 获取 Redisson 锁 RLock lock = redissonClient.getLock("order:" + orderId); boolean locked = lock.tryLock(10, 100, TimeUnit.SECONDS); // 尝试获取锁,等待 10s,锁定 100s if (locked) { try { // 执行订单操作 } finally { lock.unlock(); // 释放锁 } } else { // 获取锁失败,抛出异常或者等待一段时间后重试 throw new IllegalStateException("Failed to acquire order lock"); } } public void stop() throws IOException { // 停止 Redis 服务器和 Redisson 客户端 redissonClient.shutdown(); redisServer.stop(); } public static void main(String[] args) throws IOException { OrderService orderService = new OrderService(); orderService.placeOrder("123"); orderService.stop(); } } ``` 在上面的代码中,我们首先启动了一个 Redis 服务器,然后通过 Redisson 创建了一个 Redisson 客户端。在 placeOrder 方法中,我们获取了一个名为 "order:orderId" 的 Redisson 锁,通过 tryLock 方法尝试获取锁,等待时间为 10s,锁定时间为 100s。如果获取锁成功,则执行订单操作,并在最后释放锁;否则,抛出异常或者等待一段时间后重试。最后,在 main 方法中,我们启动了 OrderService 并执行了一次 placeOrder 方法,并在最后停止了 Redis 服务器和 Redisson 客户端。 需要注意的是,RedisLock 的实现依赖于 Redisson,因此需要在项目中引入 Redisson 的相关依赖。另外,需要注意 RedisLock 的锁定时间,避免因为锁定时间过长导致资源被长时间占用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值