利用redis实现分布式锁,网上搜索的大部分是使用java jedis实现的。
redis官方推荐的分布式锁实现为redisson http://ifeve.com/redis-lock/
以下为spring boot实现分布式锁的步骤
项目pom中需要添加官方依赖 我是1.8JDK固为
<!-- redisson --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.4.2</version> </dependency>
定义一个分布式锁的回调类
package com.example.demo.redis2; /** * 分布式锁回调接口 * * @author lk */ public interface DistributedLockCallback<T> { /** * 调用者必须在此方法中实现需要加分布式锁的业务逻辑 * * @return */ public T process(); /** * 得到分布式锁名称 * * @return */ public String getLockName(); }
分布式锁操作模板
package com.example.demo.redis2; import java.util.concurrent.TimeUnit; /** * 分布式锁操作模板 * * @author lk */ public interface DistributedLockTemplate { /** * 使用分布式锁,使用锁默认超时时间。 * * @param callback * @return */ public <T> T lock(DistributedLockCallback<T> callback); /** * 使用分布式锁。自定义锁的超时时间 * * @param callback * @param leaseTime 锁超时时间。超时后自动释放锁。 * @param timeUnit * @return */ public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit); }
使用redisson最简单的Single instance mode实现分布式锁模板接口
package com.example.demo.redis2; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import java.util.concurrent.TimeUnit; /** * Single Instance mode 分布式锁模板 * * @author lk */ public class SingleDistributedLockTemplate implements DistributedLockTemplate { private static final long DEFAULT_TIMEOUT = 5; private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS; private RedissonClient redisson; public SingleDistributedLockTemplate() { } public SingleDistributedLockTemplate(RedissonClient redisson) { this.redisson = redisson; } @Override public <T> T lock(DistributedLockCallback<T> callback) { return lock(callback, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT); } @Override public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit) { RLock lock = null; try { lock = redisson.getLock(callback.getLockName()); lock.lock(leaseTime, timeUnit); return callback.process(); } finally { if (lock != null) { lock.unlock(); } } } public void setRedisson(RedissonClient redisson) { this.redisson = redisson; } }
创建可以被spring管理的 Bean
package com.example.demo.redis2; import java.io.IOException; import java.io.InputStream; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.apache.log4j.Logger; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.beans.factory.FactoryBean; /** * 创建分布式锁模板实例的工厂Bean * * @author lk */ public class DistributedLockFactoryBean implements FactoryBean<DistributedLockTemplate> { private Logger logger = Logger.getLogger(DistributedLockFactoryBean.class); private LockInstanceMode mode; private DistributedLockTemplate distributedLockTemplate; private RedissonClient redisson; @PostConstruct public void init() { String ip = "127.0.0.1"; String port = "6379"; Config config=new Config(); config.useSingleServer().setAddress(ip+":"+port); redisson=Redisson.create(config); System.out.println("成功连接Redis Server"+"\t"+"连接"+ip+":"+port+"服务器"); } @PreDestroy public void destroy() { logger.debug("销毁分布式锁模板"); redisson.shutdown(); } @Override public DistributedLockTemplate getObject() throws Exception { switch (mode) { case SINGLE: distributedLockTemplate = new SingleDistributedLockTemplate(redisson); break; } return distributedLockTemplate; } @Override public Class<?> getObjectType() { return DistributedLockTemplate.class; } @Override public boolean isSingleton() { return true; } public void setMode(String mode) { if (mode==null||mode.length()<=0||mode.equals("")) { throw new IllegalArgumentException("未找到dlm.redisson.mode配置项"); } this.mode = LockInstanceMode.parse(mode); if (this.mode == null) { throw new IllegalArgumentException("不支持的分布式锁模式"); } } private enum LockInstanceMode { SINGLE; public static LockInstanceMode parse(String name) { for (LockInstanceMode modeIns : LockInstanceMode.values()) { if (modeIns.name().equals(name.toUpperCase())) { return modeIns; } } return null; } } }
配置进spring boot中
package com.example.demo.redis2; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Created by LiaoKe on 2017/5/22. */ @Configuration public class BeanConfig { @Bean public DistributedLockFactoryBean distributeLockTemplate(){ DistributedLockFactoryBean d = new DistributedLockFactoryBean(); d.setMode("SINGLE"); return d; } }
目前为止已经可以使用。
为了验证锁是否成功,我做了如下例子。
首先建立了一个数据库实体(使用的JPA),模拟被购买的商品数量,当被购买后,num+1
在高并发环境下,这必定会有问题,因为在查询之后的设值,存在对同一数据库源的操作。
package com.example.demo.redis2.entity; import org.hibernate.annotations.GenericGenerator; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; /** * 测试类实体 * Created by LiaoKe on 2017/5/22. */ @Entity public class TestEntity { @Id @GeneratedValue(generator = "system-uuid") @GenericGenerator(name = "system-uuid", strategy = "uuid") private String id; private Integer num; public String getId() { return id; } public void setId(String id) { this.id = id; } public Integer getNum() { return num; } public void setNum(Integer num) { this.num = num; } }
具体数据库操作,加锁和不加锁的操作,要注意我使用了@Async,异步任务注解,我没有配置线程池信息,使用的默认线程池。
package com.example.demo.redis2.service; import com.example.demo.redis2.DistributedLockCallback; import com.example.demo.redis2.DistributedLockTemplate; import com.example.demo.redis2.dao.TestEntityRepository; import com.example.demo.redis2.entity.TestEntity; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * Created by LiaoKe on 2017/5/22. */ @Service public class AsyncService { @Resource TestEntityRepository ts; @Resource DistributedLockTemplate distributedLockTemplate; /** * 加锁 */ @Async public void addAsync(){ distributedLockTemplate.lock(new DistributedLockCallback<Object>(){ @Override public Object process() { add(); return null; } @Override public String getLockName() { return "MyLock"; } }); } /** * 未加锁 */ @Async public void addNoAsync(){ add(); } /** * 测试异步方法 * 在不加分布式锁的情况下 * num数目会混乱 */ @Async private void add(){ if(ts.findAll().size()==0){ TestEntity t = new TestEntity(); t.setNum(1); ts.saveAndFlush(t); }else{ TestEntity dbt = ts.findAll().get(0); dbt.setNum(dbt.getNum()+1); ts.saveAndFlush(dbt); } } }
最后为了测试简单跑了两个接口
package com.example.demo; import com.example.demo.redis2.DistributedLockTemplate; import com.example.demo.redis2.service.AsyncService; import oracle.jrockit.jfr.StringConstantPool; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @SpringBootApplication @RestController @EnableAsync public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @Resource AsyncService as; @GetMapping("") public void test(){ for(int i = 0 ;i<10000;i++){ as.addNoAsync(); } } @GetMapping("lock") public void test2(){ for(int i = 0 ;i<10000;i++){ as.addAsync(); } } }
访问localhost:8888 及 localhost:8888/lock
在不加锁的情况下
数据库已经爆炸了
最后得到的数据奇奇怪怪
使用加锁后的访问
可以看到库存增加绝对正确。
此处并未使用任何数据库锁,并且基于redis,可在不同的网络节点实现上锁。
这只是简单的实现,在真正的生产环境中,还要注意许多问题,超时和放锁时机需要好好研究,在此不便贴真正项目代码。
参考博客:http://layznet.iteye.com/blog/2307179 感谢作者
相关推荐
NULL 博文链接:https://liaoke0123.iteye.com/blog/2375556
SpringBoot整合Redisson实现RedLock分布式锁同步源码包
Java开发基于SpringBoot+WebSocket+Redis分布式即时通讯群聊系统。一个基于Spring Boot + WebSocket + Redis,可快速开发的分布式即时通讯群聊系统。适用于直播间聊天、游戏内聊天、客服聊天等临时性群聊场景。 ...
SpringBoot基于redis的分布式锁,有word使用文档,根据文档配置即可使用
之前看很多人手写分布式锁,其实 Spring Boot 现在已经做的足够好了,开箱...本篇栈长以 Redis 为例(这也是用得最多的方案),教大家如何利用 Spring Boot 集成 Redis 实现缓存,如何简单、快速实现 Redis 分布式锁。
http://localhost:8080/ass/getUser 测试是否登录 http://localhost:8080/any/user/passLogin 登录测试 http://localhost:8080/lock redis分布式锁测试
项目为spring boot实现,maven生成jar包能直接运行 ...三种方式实现redis分布式锁 1.redis incr计数器实现 2.redis setIfAbsent 3.redisson 博客:https://blog.csdn.net/u011974797/article/details/81238079
Redisson实现分布式锁 有关Redisson实现分布式锁前面写了三篇博客作为该项目落地的铺垫。 1、 2、 3、 该项目可以直接运用于实际开发中,作为分布式锁使用。 一、项目概述 1、技术架构 项目总体技术选型 SpringBoot...
本篇内容主要讲解的是redis分布式锁,这个在各⼤⼚⾯试⼏乎都是必备的,下⾯结合模拟抢单的场景来使⽤她;本篇不涉及到的 redis环境搭建,快速搭建个⼈测试环境,这⾥建议使⽤docker;
redis分布式锁工具包,提供纯Java方式调用,支持传统Spring工程, 为spring boot应用提供了starter,更方便快捷的调用
SpringBoot实现的Redis分布式锁代码示例
基于Spring Boot + WebSocket + Redis,可快速开发的分布式即时通讯群聊系统.zip 1、该资源内项目代码经过严格调试,下载即用确保可以运行! 2、该资源适合计算机相关专业(如计科、人工智能、大数据、数学、电子信息...
redis分布式锁工具包,提供纯Java方式调用,支持传统Spring工程, 为spring boot应用提供了starter,更方便快捷的调用。 项目结构 原生redis分布式锁实现,支持注解,不推荐项目中使用,仅供学习使用 redis-...
springboot整合Redis实现在分布式情况,使用分布式锁解决数据并发的方案,主要使用Redis提供的setIfAbsent方法实现,并且考虑到了setIfAbsent在极端情况下的多实例同时设置一个key成功的情况。 本案例通过实现对库存...
基于Spring Boot+WebSocket+Redis开发的分布式即时通讯群聊系统源码+项目使用说明.zip 该项目是个人毕设项目源码,评分达到95分,都经过严格调试,确保可以运行!放心下载使用。 该项目资源主要针对计算机、自动化等...
一款基于 Spring Boot Starter 机制的分布式锁框架,实现了redis和zookeeper两种模式的分布式锁功能,以注解的方式(@RLock和@ZLock)对方法进行加锁操作,零代码实现业务加锁能力,涵盖各种加锁方式,并支持redis和...
但是服务的多副本运行随之也会引来一些分布式问题,比如某个接口的处理逻辑是这样的:接收到请求后,先查询 DB 看是否有相关的数据,如果没有则插入数据,如果有则更新数据。在这种场景下如果相同的 N 个请求并发发...
基于redis的分布式锁spring-boot starter组件,使得项目拥有分布式锁能力变得异常简单,支持spring boot,和spirng mvc等spring相关项目
redis客户端连接、spring boot整合、分布式锁
使用Redis实现分布式锁 5.1 设置分布式锁 5.2 释放分布式锁 Redis常用操作 6.1 字符串操作 6.2 哈希操作 6.3 列表操作 6.4 集合操作 6.5 有序集合操作 Redis持久化配置 7.1 RDB持久化 7.2 AOF持久化 7.3 持久化配置...