分布式锁——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();
    }

 

posted @ 2019-07-29 09:55  晨M风  阅读(707)  评论(0编辑  收藏  举报