Quellcode durchsuchen

新增redis 分布式锁支持

周浩 vor 9 Jahren
Ursprung
Commit
8ff57b8469

+ 7 - 0
hsweb-web-concurrent/hsweb-web-concurrent-lock/pom.xml

@@ -21,6 +21,13 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-aop</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>org.springframework.session</groupId>
+            <artifactId>spring-session-data-redis</artifactId>
+            <optional>true</optional>
+        </dependency>
+
         <dependency>
             <groupId>org.webbuilder</groupId>
             <artifactId>wb-sql-util</artifactId>

+ 9 - 5
hsweb-web-concurrent/hsweb-web-concurrent-lock/src/main/java/org/hsweb/concurrent/lock/ReadWriteLockFactoryAutoConfig.java

@@ -1,8 +1,10 @@
 package org.hsweb.concurrent.lock;
 
 import org.hsweb.concurrent.lock.support.AnnotationLockAopAdvice;
-import org.hsweb.concurrent.lock.support.DefaultReadWriteLockFactory;
+import org.hsweb.concurrent.lock.support.DefaultLockFactory;
+import org.hsweb.concurrent.lock.support.redis.RedisLockFactoryAutoConfig;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.test.ImportAutoConfiguration;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
@@ -10,16 +12,18 @@ import org.springframework.context.annotation.Configuration;
  * Created by zhouhao on 16-4-27.
  */
 @Configuration
-@ConditionalOnMissingBean(value = {LockFactory.class})
-public class ReadWriteLockFactoryAutoConfig {
+@ImportAutoConfiguration(RedisLockFactoryAutoConfig.class)
+public class LockFactoryAutoConfig {
 
     @Bean
-    public LockFactory createReadWriteLockFactory() {
-        return new DefaultReadWriteLockFactory();
+    @ConditionalOnMissingBean(LockFactory.class)
+    public LockFactory defaultLockFactory() {
+        return new DefaultLockFactory();
     }
 
     @Bean
     public AnnotationLockAopAdvice annotationLockAopAdvice() {
         return new AnnotationLockAopAdvice();
     }
+
 }

+ 1 - 1
hsweb-web-concurrent/hsweb-web-concurrent-lock/src/main/java/org/hsweb/concurrent/lock/support/DefaultReadWriteLockFactory.java

@@ -13,7 +13,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
  * 默认的锁工程。用于提供JDK自带锁创建
  * Created by zhouhao on 16-4-27.
  */
-public class DefaultReadWriteLockFactory implements LockFactory {
+public class DefaultLockFactory implements LockFactory {
     protected ConcurrentMap<String, ReadWriteLock> READ_WRITE_LOCK_BASE = new ConcurrentHashMap<>();
     protected ConcurrentMap<String, Lock> LOCK_BASE = new ConcurrentHashMap<>();
 

+ 104 - 0
hsweb-web-concurrent/hsweb-web-concurrent-lock/src/main/java/org/hsweb/concurrent/lock/support/redis/RedisLock.java

@@ -0,0 +1,104 @@
+package org.hsweb.concurrent.lock.support.redis;
+
+import org.hsweb.concurrent.lock.exception.LockException;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.util.Assert;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * Created by zhouhao on 16-5-27.
+ */
+public class RedisLock implements Lock {
+
+    static final String PREFIX = "lock:";
+    static final byte[] LOCK_VALUE = new byte[0];
+
+    private RedisTemplate redisTemplate;
+    private String key;
+
+    private byte[] getKey() {
+        return (PREFIX + key + ".lock").getBytes();
+    }
+
+    public RedisLock(String key, RedisTemplate redisTemplate) {
+        Assert.notNull(key);
+        Assert.notNull(redisTemplate);
+        this.key = key;
+        this.redisTemplate = redisTemplate;
+    }
+
+    @Override
+    public void lock() {
+        redisTemplate.execute((RedisCallback<Boolean>) connection -> {
+            boolean locked = false;
+            do {
+                locked = connection.setNX(getKey(), LOCK_VALUE);
+                sleep();
+            } while (!locked);
+            return true;
+        });
+    }
+
+    @Override
+    public void lockInterruptibly() throws InterruptedException {
+        boolean locked = (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection ->
+                        connection.setNX(getKey(), LOCK_VALUE)
+        );
+        if (!locked) throw new InterruptedException(new String(getKey()) + " is locked!");
+    }
+
+    @Override
+    public boolean tryLock() {
+        return (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection ->
+                        connection.setNX(getKey(), LOCK_VALUE)
+        );
+    }
+
+    @Override
+    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
+        byte[] error = new byte[1];
+        boolean success = (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection -> {
+            boolean locked = false;
+            long startWith = System.nanoTime();
+            do {
+                locked = connection.setNX(getKey(), LOCK_VALUE);
+                if (locked) {
+                    connection.expire(getKey(), 30);
+                    return true;
+                }
+                long now = System.nanoTime();
+                if (now - startWith > unit.toNanos(time)) {
+                    error[0] = 1;
+                    return false;
+                }
+                sleep();
+            } while (!locked);
+            return null;
+        });
+        if (error[0] == 1) {
+            throw new InterruptedException("lock time out!");
+        }
+        return success;
+    }
+
+    @Override
+    public void unlock() {
+        redisTemplate.execute((RedisCallback) conn -> conn.del(getKey()));
+    }
+
+    @Override
+    public Condition newCondition() {
+        throw new LockException("method not support");
+    }
+
+    protected void sleep() {
+        try {
+            Thread.sleep(10);
+        } catch (InterruptedException e) {
+        }
+    }
+}

+ 42 - 0
hsweb-web-concurrent/hsweb-web-concurrent-lock/src/main/java/org/hsweb/concurrent/lock/support/redis/RedisLockFactory.java

@@ -0,0 +1,42 @@
+package org.hsweb.concurrent.lock.support.redis;
+
+import org.hsweb.concurrent.lock.support.DefaultLockFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+
+/**
+ * Created by zhouhao on 16-5-27.
+ */
+public class RedisLockFactory extends DefaultLockFactory {
+    private RedisTemplate redisTemplate;
+
+    public void setRedisTemplate(RedisTemplate redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+
+    @Override
+    public ReadWriteLock createReadWriteLock(String key) {
+        synchronized (READ_WRITE_LOCK_BASE) {
+            ReadWriteLock readWriteLock = READ_WRITE_LOCK_BASE.get(key);
+            if (readWriteLock == null) {
+                readWriteLock = new RedisReadWriteLock(key, redisTemplate);
+                READ_WRITE_LOCK_BASE.put(key, readWriteLock);
+            }
+            return readWriteLock;
+        }
+    }
+
+    @Override
+    public Lock createLock(String key) {
+        synchronized (LOCK_BASE) {
+            Lock lock = LOCK_BASE.get(key);
+            if (lock == null) {
+                lock = new RedisLock(key, redisTemplate);
+                LOCK_BASE.put(key, lock);
+            }
+            return lock;
+        }
+    }
+}

+ 32 - 0
hsweb-web-concurrent/hsweb-web-concurrent-lock/src/main/java/org/hsweb/concurrent/lock/support/redis/RedisLockFactoryAutoConfig.java

@@ -0,0 +1,32 @@
+package org.hsweb.concurrent.lock.support.redis;
+
+import org.hsweb.concurrent.lock.LockFactory;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import javax.annotation.Resource;
+
+/**
+ * Created by zhouhao on 16-5-27.
+ */
+@Configuration
+@ConditionalOnBean(RedisTemplate.class)
+@ConditionalOnMissingBean(LockFactory.class)
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class RedisLockFactoryAutoConfig {
+
+    @Resource
+    public RedisTemplate redisTemplate;
+
+    @Bean
+    public RedisLockFactory redisLockFactory() {
+        RedisLockFactory lockFactory = new RedisLockFactory();
+        lockFactory.setRedisTemplate(redisTemplate);
+        return lockFactory;
+    }
+}

+ 215 - 0
hsweb-web-concurrent/hsweb-web-concurrent-lock/src/main/java/org/hsweb/concurrent/lock/support/redis/RedisReadWriteLock.java

@@ -0,0 +1,215 @@
+package org.hsweb.concurrent.lock.support.redis;
+
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.util.Assert;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+
+/**
+ * Created by zhouhao on 16-5-27.
+ */
+public class RedisReadWriteLock implements ReadWriteLock {
+    static final String PREFIX = "lock:";
+    static final byte[] LOCK_VALUE = new byte[0];
+
+    private ReadLock readLock;
+
+    private WriteLock writeLock;
+
+    private String key;
+
+    private RedisTemplate redisTemplate;
+
+    public RedisReadWriteLock(String key, RedisTemplate redisTemplate) {
+        Assert.notNull(key);
+        Assert.notNull(redisTemplate);
+        this.key = key;
+        this.redisTemplate = redisTemplate;
+        readLock = new ReadLock();
+        writeLock = new WriteLock();
+    }
+
+    @Override
+    public Lock readLock() {
+        return readLock;
+    }
+
+    @Override
+    public Lock writeLock() {
+        return writeLock;
+    }
+
+    private byte[] getReadKey() {
+        return (PREFIX + key + ".read.lock").getBytes();
+    }
+
+    private byte[] getWriteKey() {
+        return (PREFIX + key + ".write.lock").getBytes();
+    }
+
+    protected void sleep() {
+        try {
+            Thread.sleep(10);
+        } catch (InterruptedException e) {
+        }
+    }
+
+    class ReadLock implements Lock {
+        @Override
+        public void lock() {
+            redisTemplate.execute((RedisCallback<String>) connection -> {
+                boolean locked = false;
+                do {
+                    locked = connection.exists(getWriteKey());
+                    if (!locked) {
+                        connection.setNX(getReadKey(), LOCK_VALUE);
+                        locked = true;
+                    }
+                    sleep();
+                } while (!locked);
+                return null;
+            });
+        }
+
+        @Override
+        public void lockInterruptibly() throws InterruptedException {
+            boolean locked = (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection ->
+            {
+                boolean writeLocked = connection.exists(getWriteKey());
+                if (!writeLocked) {
+                    connection.setNX(getReadKey(), LOCK_VALUE);
+                    writeLocked = true;
+                }
+                return writeLocked;
+            });
+            if (!locked) throw new InterruptedException("");
+        }
+
+        @Override
+        public boolean tryLock() {
+            return (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection ->
+                            connection.setNX(getReadKey(), LOCK_VALUE)
+            );
+        }
+
+        @Override
+        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
+            byte[] error = new byte[1];
+            boolean success = (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection -> {
+                boolean locked = false;
+                long startWith = System.nanoTime();
+                do {
+                    locked = connection.exists(getWriteKey());
+                    if (!locked) {
+                        connection.setNX(getReadKey(), LOCK_VALUE);
+                        connection.expire(getReadKey(), 30);
+                        return true;
+                    }
+                    long now = System.nanoTime();
+                    if (now - startWith > unit.toNanos(time)) {
+                        error[0] = 1;
+                        return false;
+                    }
+                    sleep();
+                } while (!locked);
+                return null;
+            });
+            if (error[0] == 1) {
+                throw new InterruptedException("lock time out!");
+            }
+            return success;
+        }
+
+        @Override
+        public void unlock() {
+            redisTemplate.execute((RedisCallback) conn -> conn.del(getReadKey()));
+        }
+
+        @Override
+        public Condition newCondition() {
+            return null;
+        }
+    }
+
+    class WriteLock implements Lock {
+        @Override
+        public void lock() {
+            redisTemplate.execute((RedisCallback<String>) connection -> {
+                boolean locked = false, readLocked = false;
+                do {
+                    readLocked = connection.exists(getReadKey());
+                    if (!readLocked) {
+                        locked = connection.setNX(getWriteKey(), LOCK_VALUE);
+                    }
+                    sleep();
+                } while (!locked);
+                return null;
+            });
+        }
+
+        @Override
+        public void lockInterruptibly() throws InterruptedException {
+            boolean locked = (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection ->
+            {
+                boolean readLocked = connection.exists(getReadKey());
+                if (!readLocked) {
+                    return connection.setNX(getWriteKey(), LOCK_VALUE);
+                }
+                return false;
+            });
+            if (!locked) throw new InterruptedException("");
+        }
+
+        @Override
+        public boolean tryLock() {
+            return (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection -> {
+                if (connection.exists(getReadKey())) return false;
+                return connection.setNX(getWriteKey(), LOCK_VALUE);
+            });
+        }
+
+        @Override
+        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
+            byte[] error = new byte[1];
+            boolean success = (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection -> {
+                boolean locked = false;
+                long startWith = System.nanoTime();
+                do {
+                    locked = connection.exists(getReadKey());
+                    if (!locked) {
+                        locked = connection.setNX(getWriteKey(), LOCK_VALUE);
+                        if (locked) {
+                            connection.expire(getWriteKey(), 30);
+                            return true;
+                        }
+                    }
+                    long now = System.nanoTime();
+                    if (now - startWith > unit.toNanos(time)) {
+                        error[0] = 1;
+                        return false;
+                    }
+                    sleep();
+                } while (!locked);
+                return null;
+            });
+            if (error[0] == 1) {
+                throw new InterruptedException("lock time out!");
+            }
+            return success;
+        }
+
+        @Override
+        public void unlock() {
+            redisTemplate.execute((RedisCallback) conn -> conn.del(getWriteKey()));
+        }
+
+        @Override
+        public Condition newCondition() {
+            return null;
+        }
+    }
+}

+ 1 - 1
hsweb-web-concurrent/hsweb-web-concurrent-lock/src/main/resources/META-INF/spring.factories

@@ -1,3 +1,3 @@
 # Auto Configure
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-org.hsweb.concurrent.lock.ReadWriteLockFactoryAutoConfig
+org.hsweb.concurrent.lock.LockFactoryAutoConfig