分布式锁——Redisson
1.安装redis
a.由于官方是没有Windows版的,所以我们需要下载微软开发的redis,网址:https://github.com/MicrosoftArchive/redis/releases
b.解压后,在redis根目录打开cmd界面,输入:redis-server.exe redis.windows.conf,启动redis(关闭cmd窗口即停止)
2.导入pom依赖
<properties> ...... <!-- spring --> <spring.version>5.1.1.RELEASE</spring.version> <!-- log4j --> <slf4j.version>1.7.18</slf4j.version> <log4j.version>1.2.17</log4j.version> </properties> <dependencies> <!-- spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- AOP --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.6</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.6</version> </dependency> <!-- 日志相关 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <!-- redis --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.8.2</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>2.1.1.RELEASE</version> </dependency> </dependencies>
3.新建配置文件
a.新建spring.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 自动扫描的包名 --> <context:component-scan base-package="com.wode" /> <!-- 开启AOP代理 --> <aop:aspectj-autoproxy proxy-target-class="true" /> <!--开启注解处理器 --> <context:annotation-config> </context:annotation-config> <context:property-placeholder location="classpath:redis.properties"/> <!-- Spring中引入其他配置文件 --> <import resource="classpath*:/spring-redis.xml" /> </beans>
b.新建spring-redis.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:redisson="http://redisson.org/schema/redisson" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://redisson.org/schema/redisson http://redisson.org/schema/redisson/redisson.xsd"> <!-- redis连接池 --> <bean id="jedisConfig" class="redis.clients.jedis.JedisPoolConfig"> <!-- 最大空闲数 --> <property name="maxIdle" value="${redis.maxIdle}" /> <!-- 连接时最大的等待时间(毫秒) --> <property name="maxWaitMillis" value="${redis.maxWait}" /> <!-- 最大连接数 --> <property name="maxTotal" value="${redis.maxTotal}" /> <!-- 在提取一个jedis实例时,是否提前进行验证操作;如果为true,则得到的jedis实例均是可用的 --> <property name="testOnBorrow" value="${redis.testOnBorrow}" /> </bean> <!-- redis连接工厂 --> <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:database="${redis.database}" p:pool-config-ref="jedisConfig" /> <!-- redis操作模板,这里采用尽量面向对象的模板 --> <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"> <property name="connectionFactory" ref="connectionFactory" /> <!-- 如果不配置Serializer,那么存储的时候只能使用String,如果用对象类型存储,那么会提示错误 can't cast to String!!!--> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> </property> <property name="hashKeySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> </property> <property name="valueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> </property> <property name="hashValueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> </property> </bean> <!-- ========================================= Redisson ========================================= --> <!--redisson配置的实例 单台redis机器配置 --> <redisson:client id="redissonClient"> <redisson:single-server address="redis://127.0.0.1:6379" idle-connection-timeout="10000" ping-timeout="1000" connect-timeout="10000" timeout="3000" retry-attempts="3" retry-interval="1500" connection-minimum-idle-size="10" connection-pool-size="64" database="0" /> </redisson:client> </beans>
c.新建redis.properties
# 主机 redis.host=127.0.0.1 # 端口号 redis.port=6379 # 密码 一般不需要 redis.pass= redis.database=0 redis.maxIdle=5 redis.maxWait=60000 redis.maxTotal=500 redis.testOnBorrow=true
4.使用Redisson + Spring-Date-Redis
a.新建Redisson工具类
@Component public class LockManager { @Autowired private RedissonClient redissonClient; public void lock(String key){ RLock fairLock = redissonClient.getLock(key); fairLock.lock(); } public void unlock(String key){ RLock fairLock = redissonClient.getLock(key); fairLock.unlock(); } }
b.新建Spring-Data-Redis工具类
@Component public class RedisManager { @Autowired private RedisTemplate redisTemplate; /** * 普通缓存获取 * * @param key 键 * @return 值 */ public Object getCache(String key) { return key == null ? null : redisTemplate.opsForValue().get(key); } /** * 普通缓存放入 * * @param key 键 * @param value 值 * @return true成功 false失败 */ public boolean setCache(String key, Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } }
c.新建BeanFactory工具类
public class BeanFactoryUtil { private static ApplicationContext context; public static ApplicationContext getBeanFactory(String path){ if (context == null) { synchronized (BeanFactoryUtil.class) { if (context == null) { if(! StringUtils.hasText(path)){ context = new ClassPathXmlApplicationContext("spring.xml"); }else{ context = new ClassPathXmlApplicationContext(path); } } } } return context; } public static ApplicationContext getBeanFactory(){ if (context == null) { synchronized (BeanFactoryUtil.class) { if (context == null) { context = new ClassPathXmlApplicationContext("spring.xml"); } } } return context; } }
d.测试
public class App { public static final String COMMON_LOCK_KEY = "lockKey"; public static final String COMMON_COUNT_KEY = "countKey"; public static LockManager lock; public static RedisManager redisManager; public static void main( String[] args ) { ApplicationContext applicationContext = BeanFactoryUtil.getBeanFactory(); lock = (LockManager) applicationContext.getBean("lockManager"); redisManager = (RedisManager) applicationContext.getBean("redisManager"); redisManager.setCache(COMMON_COUNT_KEY, 0); Thread thread1 = new MyThread(); thread1.setName("线程1"); Thread thread2 = new MyThread(); thread2.setName("线程2"); thread1.start(); thread2.start(); } public static class MyThread extends Thread{ @Override public void run() { while(true){ lock.lock(COMMON_LOCK_KEY); System.out.println("==============[" + this.getName() + "]开始=============="); try { Thread.sleep(2000); int count = (int) redisManager.getCache(COMMON_COUNT_KEY); System.out.println("==============[" + this.getName() + "]计数: " + count + " =============="); redisManager.setCache(COMMON_COUNT_KEY, count + 1); }catch (Exception e){ e.printStackTrace(); } lock.unlock(COMMON_LOCK_KEY); System.out.println("==============[" + this.getName() + "]结束=============="); } } } }
5.Rediss与AOP整合
a.创建自定义注解
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) //方法注解 public @interface RedisLock { @AliasFor("lockName") String value() default "default.lock"; @AliasFor("value") String lockName() default "default.lock"; }
b.创建AOP切面
@Component @Aspect @Order(1) //执行顺序,越小越先执行 public class RedisLockAspect { @Resource private RedissonClient redissonClient; @Pointcut("@annotation(com.wode.redisson.RedisLock)") public void pointcut() {} @Around("pointcut() && @annotation(lockAnno)") public Object around(ProceedingJoinPoint joinPoint, RedisLock lockAnno) { String lockName = lockAnno.lockName(); RLock lock = redissonClient.getLock(lockName); Object obj = null; lock.lock(); try { obj = joinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } finally{ lock.unlock(); } return obj; } }
c.创建Service使用自定义注解
@Service public class CommonService { public static final String COMMON_LOCK_KEY = "lockKey"; public static final String COMMON_COUNT_KEY = "countKey"; @Resource private RedisManager redisManager; @RedisLock(COMMON_LOCK_KEY) public void test(){ System.out.println("==============[" + Thread.currentThread().getName() + "]开始=============="); try { Thread.sleep(2000); Object count = redisManager.getCache(COMMON_COUNT_KEY); if(count == null){ count = 0; } System.out.println("==============[" + Thread.currentThread().getName() + "]计数: " + count + " =============="); redisManager.setCache(COMMON_COUNT_KEY, (int) count + 1); }catch (Exception e){ e.printStackTrace(); } System.out.println("==============[" + Thread.currentThread().getName() + "]结束=============="); } }
d.测试
public static void main( String[] args ) { ApplicationContext applicationContext = BeanFactoryUtil.getBeanFactory(); CommonService service = (CommonService) applicationContext.getBean("commonService"); Thread thread1 = new Thread(() -> { service.test(); }); thread1.setName("线程1"); Thread thread2 = new Thread(() -> { service.test(); }); thread2.setName("线程2"); thread1.start(); thread2.start(); }