分布式锁

一世执手 提交于 2019-12-12 12:44:24
  • 什么是分布式锁
    • 线程锁
      • 主要用来给方法、代码块加锁。当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁只在同一JVM中有效果,因为线程锁的实现在根本上是依靠线程之间共享内存实现的,比如synchronized是共享对象头,显示锁Lock是共享某个变量(state)。
    • 进程锁
      • 为了控制同一操作系统中多个进程访问某个共享资源,因为进程具有独立性,各个进程无法访问其他进程的资源,因此无法通过synchronized等线程锁实现进程锁。
    • 分布式锁
      • 当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问。
  • 使用场景
    • 线程间并发问题和进程间并发问题都是可以通过分布式锁解决的,但是强烈不建议这样做!因为采用分布式锁解决这些小问题是非常消耗资源的!分布式锁应该用来解决分布式情况下的多进程并发问题才是最合适的。
    • 有这样一个情境,线程A和线程B都共享某个变量X。
      • 如果是单机情况下(单JVM),线程之间共享内存,只要使用线程锁就可以解决并发问题。
      • 如果是分布式情况下(多JVM),线程A和线程B很可能不是在同一JVM中,这样线程锁就无法起到作用了,这时候就要用到分布式锁来解决。
  • 分布式锁的实现(Redis)
    • 分布式锁实现的关键是在分布式的应用服务器外,搭建一个存储服务器,存储锁信息,这时候我们很容易就想到了Redis。首先我们要搭建一个Redis服务器,用Redis服务器来存储锁信息。
    • 在实现的时候要注意的几个关键点:
      • 1、锁信息必须是会过期超时的,不能让一个线程长期占有一个锁而导致死锁;
      • 2、同一时刻只能有一个线程获取到锁。
    • 除了Redis,一般还可以使用Zookeeper(算法对数据的完整性要求很高。在分布式的zookeeper中,数据是很难丢失的。所以数据/锁的完整性更高)或者Memcached。
    • 使用Redis
      • 使用redisson(无法保证服务端的宕机数据丢失)
        • 在redisson中不需要set指令

          RBucket<Object> bucket = redisson.getBucket("a");
          bucket.set("bb");
        • 所有的值都是结构体
          • 如前面的RBucket、后面会用的Lock,redisson提供了十几种结构体供我们使用,当取值时,redisson也会自动将值转换成对应的结构体。所以如果使用redisson取redisTemplate放入的值,就要小心报错。
        • 初始化
          • 借助JedisConnectionFactory的连接中的参数来初始化redisson连接。

            import org.redisson.Redisson;
            import org.redisson.api.RedissonClient;
            import org.redisson.config.Config;
            import org.springframework.beans.factory.annotation.Autowired;
            import org.springframework.beans.factory.annotation.Qualifier;
            import org.springframework.context.annotation.Bean;
            import org.springframework.context.annotation.Configuration;
            import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
            import org.springframework.data.redis.serializer.GenericToStringSerializer;
            import org.springframework.data.redis.serializer.StringRedisSerializer;
            import org.springframework.data.redis.core.RedisTemplate;
            
            @Configuration
            public class RedisLockConfig
            {
            
                @Autowired
                private JedisConnectionFactory jcf;
            
                @Qualifier("RedissonLockClient")
                @Bean
                public RedissonClient redisson()
                {
                    Config config = new Config();
            
                    config.useSingleServer().setAddress("redis://" + jcf.getHostName() + ":" + jcf.getPort())
                            .setPassword(jcf.getPassword());
                    return Redisson.create(config);
                }
            
                @Qualifier("RedisLockTemplate")
                @Bean
                public RedisTemplate<String, Object> redisTemplate()
                {
                    final RedisTemplate<String, Object> template = new RedisTemplate<>();
                    template.setConnectionFactory(jcf);
                    template.setKeySerializer(new StringRedisSerializer());
                    template.setHashValueSerializer(new GenericToStringSerializer<Object>(Object.class));
                    template.setValueSerializer(new GenericToStringSerializer<Object>(Object.class));
                    return template;
                }
            }
        • 使用
          • redisson.getLock("xxx")获得的锁是分布式锁,用于多个JVM中的多线程处理。而这里通过普通的RedisTemplate进行的操作,只是为了记录任务状态,毕竟redisson的行为不可见。

            @Qualifier("RedissonLockClient")
            @Autowired
            private RedissonClient redisson;
            
            @Qualifier("RedisLockTemplate")
            @Autowired
            private RedisTemplate<String, Object> template;
            
            @Scheduled(cron = "${xxx.yyy.zzz}")
            public void bizMethod()
            {
                logger.debug("STEPIN");
                RLock lock = redisson.getLock("bizLock");
                if(!lock.isLocked()) 
                {
                    lock.lock();
                    try
                    {
                        logger.trace("{} : acquired lock", System.identityHashCode(this));
            
                        int year = Calendar.getInstance().get(Calendar.YEAR);
                        int month = Calendar.getInstance().get(Calendar.MONTH);
                        int day = Calendar.getInstance().get(Calendar.DATE);
                        String key = year + "-" + month + "-" + day;
            
                        Object value = template.opsForValue().get(key);
                        if (value == null)
                        {
                            template.opsForValue().set(key, "progressing");
                            logger.trace("{}: {}  is progressing", System.identityHashCode(this), key);
            
                            // logic code
            
                            template.opsForValue().set(key, "done");
                            logger.trace("{}: {}  is done", System.identityHashCode(this), key);
                        }
                        else
                        {
                            logger.trace("{}: {}  is already done!", System.identityHashCode(this), key);
                        }
            
                    }
                    finally
                    {
                        logger.trace("{} : releasing lock", System.identityHashCode(this));
                        lock.unlock();
                    }  
                }
                else 
                {
                    logger.trace("{} : someone already acquired lock", System.identityHashCode(this));
                }
                logger.debug("STEPOUT");
            }
            
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!