博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
redis和redisson实现分布式锁
阅读量:4165 次
发布时间:2019-05-26

本文共 11359 字,大约阅读时间需要 37 分钟。

文章目录

redis 实现分布式锁

介绍三种方式实现分布式锁:

  1. Redis原生方式实现分布式锁

  2. Redisson实现分布式锁

  3. 注解aop的方式加Redisson实现分布式锁(推荐)

一、redis原生方式实现分布式锁

Redis 单线程

SETNX (set if not exists)

setnx key value 若key存在则添加失败,若key不存在才会添加存在

redisTempalte.setIfAbsetn(key,value,time) 设置 key 和 value 和 超时时间

try {    // todo 业务        } finally {        // get 值,若 value 与 设置的value相等 则 删除key            }

简单的实现分布式锁:

// 这里使用到了 springboot集成Redis  用到了springboot的 StringRedisTemplate@GetMapping("/transaction")    public String transaction() {
// 锁的键值 String lockKey = "orderLock"; // 设置客户端id String clientId = UUID.randomUUID().toString(); try {
// 设置锁 如果redis中已经存在 lockKey 则会添加失败 Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 30, TimeUnit.SECONDS); if (result == null || !result) {
return "error"; } String stock = stringRedisTemplate.opsForValue().get("stock"); if (StrUtil.isEmpty(stock)) {
return ">>>>>>>> 数据异常请稍后重试"; } int number = Integer.parseInt(stock); if (number > 0) {
Long lastNumber = stringRedisTemplate.opsForValue().decrement("stock"); System.out.println(">>>>>>>>>>> 扣除库存成功,剩余:" + lastNumber); } else {
System.out.println(">>>>>>>>>>> 扣除库存失败,库存不足"); } } finally {
// 如果客户端id相等 if (clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))) {
stringRedisTemplate.delete(lockKey); } } return "success"; }

二、Redisson实现分布式锁

1.导入jar包

org.redisson
redisson
3.12.0

2.设置连接配置

@Getter@Setter@ConfigurationProperties(prefix="redisson")public class RedissonProperties {
private int timeout = 3000; private String address; private String password; private int connectionPoolSize = 64; private int connectionMinimumIdleSize=10; private int slaveConnectionPoolSize = 250; private int masterConnectionPoolSize = 250; private String[] sentinelAddresses; private String masterName;}

// 自动装配

@Configuration@ConditionalOnClass(Config.class)@EnableConfigurationProperties(RedissonProperties.class)public class RedissonAutoConfiguration {
@Autowired private RedissonProperties redssionProperties; // import org.springframework.boot.autoconfigure.data.redis.RedisProperties;// @Autowired// private RedisProperties redisProperties; /** * 哨兵模式自动装配 * # 哨兵模式 * #redisson: * # master-name: mymaster * # password: * # sentinel-addresses: 10.47.91.83:26379,10.47.91.83:26380,10.47.91.83:26381 * @return */// @Bean// @ConditionalOnProperty(name="redisson.master-name")// RedissonClient redissonSentinel() {
// Config config = new Config();// SentinelServersConfig serverConfig = config.useSentinelServers().addSentinelAddress(redssionProperties.getSentinelAddresses())// .setMasterName(redssionProperties.getMasterName())// .setTimeout(redssionProperties.getTimeout())// .setMasterConnectionPoolSize(redssionProperties.getMasterConnectionPoolSize())// .setSlaveConnectionPoolSize(redssionProperties.getSlaveConnectionPoolSize());//// if(StrUtil.isNotBlank(redssionProperties.getPassword())) {
// serverConfig.setPassword(redssionProperties.getPassword());// }// return Redisson.create(config);// } /** * 单机模式自动装配 * @return */ @Bean @ConditionalOnProperty(name="redisson.address") RedissonClient redissonClient() {
Config config = new Config(); SingleServerConfig serverConfig = config.useSingleServer() .setAddress(redssionProperties.getAddress()) .setTimeout(redssionProperties.getTimeout()) .setDatabase(0) .setConnectionPoolSize(redssionProperties.getConnectionPoolSize()) .setConnectionMinimumIdleSize(redssionProperties.getConnectionMinimumIdleSize()); if(StrUtil.isNotBlank(redssionProperties.getPassword())) {
serverConfig.setPassword(redssionProperties.getPassword()); } return Redisson.create(config); }}/** * redis集群下的配置 cluster * @return */// @Bean// public RedissonClient redisson() {
// //redisson版本是3.5,集群的ip前面要加上“redis://”,不然会报错,3.2版本可不加// List
clusterNodes = new ArrayList<>();// for (int i = 0; i < redisProperties.getCluster().getNodes().size(); i++) {
// clusterNodes.add("redis://" + redisProperties.getCluster().getNodes().get(i));// }// Config config = new Config();// // 采用集群模式// ClusterServersConfig clusterServersConfig = config.useClusterServers()// .addNodeAddress(clusterNodes.toArray(new String[clusterNodes.size()]));// //设置密码 注意这里集群的 redis 密码都必须一致// clusterServersConfig.setPassword(redisProperties.getPassword());// return Redisson.create(config);// }

yaml中的配置文件

# 哨兵模式#redisson:#  master-name: mymaster#  password:#  sentinel-addresses: 10.47.91.83:26379,10.47.91.83:26380,10.47.91.83:26381# redis单机模式redisson:  address: redis://81.69.43.66:6379  password: root

3.编写代码实现分布式锁

// 实现分布式锁

@GetMapping("/redisson")    public String redisson() {
String lockKey = "orderLock"; // 获取锁 RLock lock = redissonClient.getLock(lockKey); try {
// 加锁 lock.lock(); String stock = stringRedisTemplate.opsForValue().get("stock"); if (StrUtil.isEmpty(stock)) {
return ">>>>>>>> 数据异常请稍后重试"; } int number = Integer.parseInt(stock); if (number > 0) {
Long lastNumber = stringRedisTemplate.opsForValue().decrement("stock"); System.out.println(">>>>>>>>>>> 扣除库存成功,剩余:" + lastNumber); } else {
System.out.println(">>>>>>>>>>> 扣除库存失败,库存不足"); } } finally {
System.out.println(">>>>>>>>>>>>>>>>> 解除锁定"); // 解锁 lock.unlock(); } return "success"; }

三、注解aop的方式加Redisson实现分布式锁(推荐)

// 定义注解类

@Target({
ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface LockAction {
/** * 锁的名称 */ String value() default ""; /** * 描述 */ String description() default ""; /** * 等待锁超时时间,默认 30 * @return */ long waitTime() default 30; /** * 自动解锁时间,自动解锁时间一定得大于方法执行时间,否则会导致锁提前释放,默认100 * @return */ long leaseTime() default 100; /** * 时间单位,默认为 秒 * @return */ TimeUnit timeUnit() default TimeUnit.SECONDS; /** * 默认:可重入锁 * @return */ LockTypeEnum lockType() default LockTypeEnum.REENTRANT_LOCK;}

// 定义AOP

@Slf4j@Aspect@Component@Order(1)   //order越小越是最先执行,但更重要的是最先执行的最后结束。order默认值是2147483647public class LockAspect implements ApplicationContextAware {
private final String LOCK_CODE = "lockCode"; private final RedissonClient redissonClient; private ApplicationContext applicationContext; public LockAspect(RedissonClient redissonClient) {
this.redissonClient = redissonClient; } /** * 定义公共的切点 */ @Pointcut("@annotation(LockAction)") public void log(){
} /** * 环绕通知 * @param joinPoint * @return * @throws Throwable */ @Around(value = "@annotation(lockAction)") public Object around(ProceedingJoinPoint joinPoint,LockAction lockAction) throws Throwable {
String code = IdUtil.fastSimpleUUID(); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder .getRequestAttributes(); HttpServletRequest request = Objects.requireNonNull(attributes).getRequest(); String remoteUser = request.getRemoteUser(); log.info(">>>>>>>>>>>> remoteUser 远程用户:{}",remoteUser); String requestURI = request.getRequestURI(); log.info(">>>>>>>>>>>> requestURI 请求地址:{}",requestURI); request.setAttribute(LOCK_CODE, code); RLock lock = redissonClient.getLock(lockAction.value()); Object proceed = null; try {
// lock.lock(); // 这种方式最稳妥,框架自动续期 // 采用自定义时间的方式,注意时间长短 boolean b = lock.tryLock(lockAction.waitTime(), lockAction.leaseTime(), lockAction.timeUnit()); if (b) {
// 类似于 method.invoke 方法 System.out.println(">>>>>>>>>>>>>>>>>>> 加锁成功"); proceed = joinPoint.proceed(); } } finally {
lock.unlock(); System.out.println(">>>>>>>>>>>>>>>>>>> 解锁成功"); } // 第一个参数// String seckillId = joinPoint.getArgs()[0].toString(); // 方法名// String name = joinPoint.getSignature().getName();// Class declaringType = joinPoint.getSignature().getDeclaringType();// Method[] methods = declaringType.getMethods();// Object proceed = null;// for (Method method : methods) {
// if(name.equals(method.getName())){
// // 获得方法上面的注解// String value = AnnotationUtil// .getAnnotationValue(method, LockAction.class, "value");// if (StrUtil.isEmpty(value)){
// throw new RuntimeException("运行时发生异常");// }// // 获取锁// RLock lock = redissonClient.getLock(value);// try {
// // 加锁,加锁结果// lock.lock();// System.out.println(">>>>>>>>>>>>>>>>>>> 枷锁成功");// // 类似于 method.invoke 方法// proceed = joinPoint.proceed();// } finally {
// System.out.println(">>>>>>>>>>>>>>>> 解除锁定");// lock.unlock();// }// }// } return proceed; } /** * 异常通知,当目标方法抛出异常时,该方法会被触发 * @param joinPoint */ @AfterThrowing(value = "log()",throwing = "e") public void afterThrowing(JoinPoint joinPoint, Exception e){
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = Objects.requireNonNull(attributes).getRequest(); String code = (String) request.getAttribute(LOCK_CODE); log.info(">>>>>>>>>> 错误码:{}", code); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext; }}

// 分布式锁 注解实战使用

@LockAction(value = "lockey")    @GetMapping("/order/{parameter}")    public String order(@PathVariable("parameter") String parameter) {
String stock = stringRedisTemplate.opsForValue().get("stock"); int number = Integer.parseInt(stock); if (number > 0) {
Long lastNumber = stringRedisTemplate.opsForValue().decrement("stock"); System.out.println(">>>>>>>>>>> 扣除库存成功,剩余:" + lastNumber); } else {
System.out.println(">>>>>>>>>>> 扣除库存失败,库存不足"); } return "success"; }

转载地址:http://nexxi.baihongyu.com/

你可能感兴趣的文章
Java设计模式(5) - 多线程并发设计模式 - 生产者-消费者设计模式多种写法
查看>>
Java多线程(9) - 多线程 - 线程池详解与使用示例
查看>>
Java多线程(10) - 多线程 - CountDownLatch、CyclicBarrier、Semaphore使用示例详解
查看>>
Java多线程(11) - 多线程 - 锁详解:重入锁、公平锁、非公平锁、读写锁、不可重入锁、自旋锁、独享锁、共享锁、互斥锁、悲观锁、乐观锁、分段锁、偏向锁、轻量级锁、重量级锁、CAS算法原理
查看>>
Java网络编程(10) - Netty网络编程常见问题与疑问
查看>>
Spring源码(4) - Spring AOP源码分析
查看>>
Spring源码(5) - Spring事务入门与源码分析
查看>>
大胆的做梦与执行
查看>>
塑造公司管理方式(二)- 招聘、决策
查看>>
设置Django连接到Google Cloud SQL(MYSQL)
查看>>
爬虫: 基于Node.js的强大爬虫,能直接发布抓取的文章哦
查看>>
Django学习笔记 扩展User模型
查看>>
Django资料总结
查看>>
设计模式学习之路-适配器模式
查看>>
github的README.md编写
查看>>
Android手机Fiddler2抓包教程
查看>>
Android进阶系列之事件分发详解
查看>>
Android进阶系列之源码分析Activity的启动流程
查看>>
Android进阶系列-发布项目到Jcenter
查看>>
基于Zxing的二维码扫描解析库——ZxingPlus
查看>>