当前位置: > 数据库 > Redis >

Redis实现分布式锁

时间:2015-10-18 13:04来源:linux.it.net.cn 作者:IT

在服务器端,如果不对用户连击行为做出控制,很容易到出现数据异常,尤其涉及到软妹子的问题时;解决问题的方法有:数据库加锁,代码加锁等.本文将从代码加锁方式实现,适应于单机或者分布式集群等
  1. 普通方式

    1. 直接看代码

      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        package com.whereta.jedis;
         import org.springframework.stereotype.Component; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool;
         import javax.annotation.Resource; import java.util.Calendar; import java.util.Date; import java.util.concurrent.TimeUnit;
         /**
         * Created by vincent on 15-10-14.
         */ @Component("jedisLock") public class JedisLock {
          
            public class CacheName {
                public static final String ADD_BEAN_AFTER_CHECK_LOCK_KEY = "ADDBeanAfterCheckLock";
            }
          
            @Resource
            private JedisPool jedisPool;
          
            public void addBeanAfterCheck(int userId) {
                Jedis jedis = jedisPool.getResource();
          
                Calendar calendar = Calendar.getInstance();
                calendar.set(Calendar.MILLISECOND, 0);
                Date calendarTime = calendar.getTime();
                long time = calendarTime.getTime();
                //以用户id为key,不同用户调用同一个方法不会加锁         String key = CacheName.ADD_BEAN_AFTER_CHECK_LOCK_KEY + ":" + userId;
                try {
                    if (jedis != null) {
                        Long lock = jedis.setnx(key, time + "");
                        while (lock == 0) {
                            TimeUnit.MILLISECONDS.sleep(50);
                            lock = jedis.setnx(key, time + "");
                        }
                        //设置超时时间                 jedis.expire(key, 3);
                    }
          
                    //自己的业务逻辑处理
          
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    if (jedis != null) {
                        jedis.del(key);
                        jedis.close();
                    }
                }
            }
          
          
        }
    2. 示例中以用户id为key加锁:不同用户访问该方法的时候不会排斥等待

    3. 锁机制的实现利用了Redis的setnx方法:如果数据库里key存在了则不存储数据,返回0

    4. redis的该方法是线程安全的,可以放心使用

  2. aop方式

    1. 示例代码

      1.  
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        64
        65
        package com.whereta.jedis;
         import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool;
         import javax.annotation.Resource; import java.util.Calendar; import java.util.Date; import java.util.concurrent.TimeUnit;
         /**
         * Created by vincent on 15-10-14.
         */ @Aspect @Component public class JedisLockAop {
          
          
            @Resource
            private JedisPool jedisPool;
          
          
            @Around("execution(public * com.heli.core.pay.soaservice.impl.*.*(..))")
            public Object serviceAOP(ProceedingJoinPoint point) {
                Object proceed = null;
                Jedis jedis = jedisPool.getResource();
          
                Calendar calendar = Calendar.getInstance();
                calendar.set(Calendar.MILLISECOND, 0);
                Date calendarTime = calendar.getTime();
                long time = calendarTime.getTime();
                //请求参数数组         Object[] args = point.getArgs();
          
                //key可以自定义         String key = JedisLock.CacheName.ADD_BEAN_AFTER_CHECK_LOCK_KEY + ":" + args[0];
                try {
                    if (jedis != null) {
                        Long lock = jedis.setnx(key, time + "");
                        while (lock == 0) {
                            TimeUnit.MILLISECONDS.sleep(50);
                            lock = jedis.setnx(key, time + "");
                        }
                        //设置超时时间                 jedis.expire(key, 3);
                    }
                    //用户业务处理             proceed = point.proceed();
                    return proceed;
                } catch (Throwable throwable) {
                    throw new RuntimeException(throwable);
                } finally {
                    if (jedis != null) {
                        jedis.del(key);
                        jedis.close();
                    }
                }
          
            }
          
        }
    2. 使用Spring Aop的环绕方法

    3. 具体key值可以自定义涉及,譬如:ip,时间等


(责任编辑:IT)
------分隔线----------------------------