├── src ├── main │ └── java │ │ └── com │ │ └── distributed │ │ ├── sequence │ │ ├── DistributedSequence.java │ │ └── zk │ │ │ └── ZkDistributedSequence.java │ │ ├── lock │ │ ├── Callback.java │ │ ├── DistributedReentrantLock.java │ │ ├── DistributedLockTemplate.java │ │ ├── redis │ │ │ ├── RedisDistributedLockTemplate.java │ │ │ ├── RedisReentrantLock.java │ │ │ └── RedisLockInternals.java │ │ └── zk │ │ │ ├── ZkDistributedLockTemplate.java │ │ │ ├── ZkReentrantLockCleanerTask.java │ │ │ └── ZkReentrantLock.java │ │ └── limit │ │ └── redis │ │ ├── LimitRule.java │ │ └── AccessSpeedLimit.java └── test │ ├── java │ └── com │ │ └── distributed │ │ ├── lock │ │ ├── redis │ │ │ ├── SimpleTest.java │ │ │ └── RedisReentrantLockTemplateTest.java │ │ └── zk │ │ │ └── ZkReentrantLockTemplateTest.java │ │ └── limit │ │ └── redis │ │ └── AccessSpeedLimitTest.java │ └── resources │ └── logback.xml ├── pom.xml ├── README.md └── LICENSE /src/main/java/com/distributed/sequence/DistributedSequence.java: -------------------------------------------------------------------------------- 1 | package com.distributed.sequence; 2 | 3 | /** 4 | * Created by sunyujia@aliyun.com on 2016/2/25. 5 | */ 6 | public interface DistributedSequence { 7 | 8 | public Long sequence(String sequenceName); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/distributed/lock/Callback.java: -------------------------------------------------------------------------------- 1 | package com.distributed.lock; 2 | 3 | /** 4 | * Created by sunyujia@aliyun.com on 2016/2/23. 5 | */ 6 | public interface Callback { 7 | 8 | public Object onGetLock() throws InterruptedException; 9 | 10 | public Object onTimeout() throws InterruptedException; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/distributed/lock/DistributedReentrantLock.java: -------------------------------------------------------------------------------- 1 | package com.distributed.lock; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | /** 6 | * Created by sunyujia@aliyun.com on 2016/2/26. 7 | */ 8 | public interface DistributedReentrantLock { 9 | public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException; 10 | 11 | public void unlock(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/distributed/lock/DistributedLockTemplate.java: -------------------------------------------------------------------------------- 1 | package com.distributed.lock; 2 | 3 | /** 4 | * 分布式锁模板类 5 | * Created by sunyujia@aliyun.com on 2016/2/23. 6 | */ 7 | public interface DistributedLockTemplate { 8 | 9 | /** 10 | * 11 | * @param lockId 锁id(对应业务唯一ID) 12 | * @param timeout 单位毫秒 13 | * @param callback 回调函数 14 | * @return 15 | */ 16 | public Object execute(String lockId,int timeout,Callback callback); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/distributed/limit/redis/LimitRule.java: -------------------------------------------------------------------------------- 1 | package com.distributed.limit.redis; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | /** 6 | * 限制规则 7 | * Created by sunyujia@aliyun.com on 2015/9/30. 8 | */ 9 | public class LimitRule { 10 | 11 | /** 12 | * 单位时间 13 | */ 14 | private int seconds; 15 | 16 | /** 17 | * 单位时间内限制的访问次数 18 | */ 19 | private int limitCount; 20 | 21 | private int lockCount; 22 | 23 | private int lockTime; 24 | 25 | 26 | public int getSeconds() { 27 | return seconds; 28 | } 29 | 30 | public void setSeconds(int seconds) { 31 | this.seconds = seconds; 32 | } 33 | 34 | public int getLimitCount() { 35 | return limitCount; 36 | } 37 | 38 | public void setLimitCount(int limitCount) { 39 | this.limitCount = limitCount; 40 | } 41 | 42 | public int getLockCount() { 43 | return lockCount; 44 | } 45 | 46 | public void setLockCount(int lockCount) { 47 | this.lockCount = lockCount; 48 | } 49 | 50 | public int getLockTime() { 51 | return lockTime; 52 | } 53 | 54 | public void setLockTime(int lockTime) { 55 | this.lockTime = lockTime; 56 | } 57 | 58 | public boolean enableLimitLock(){ 59 | return getLockTime()>0&&getLockCount()>0; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/com/distributed/lock/redis/SimpleTest.java: -------------------------------------------------------------------------------- 1 | package com.distributed.lock.redis; 2 | 3 | import com.distributed.lock.Callback; 4 | import org.junit.Test; 5 | import redis.clients.jedis.JedisPool; 6 | 7 | import java.util.concurrent.CountDownLatch; 8 | import java.util.concurrent.ThreadLocalRandom; 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.concurrent.locks.ReentrantLock; 11 | 12 | /** 13 | * Created by sunyujia@aliyun.com on 2016/2/24. 14 | */ 15 | public class SimpleTest { 16 | 17 | public static void main(String[] args) throws Exception { 18 | JedisPool jedisPool=new JedisPool("127.0.0.1",6379);//实际应用时可通过spring注入 19 | RedisReentrantLock lock=new RedisReentrantLock(jedisPool,"订单流水号"); 20 | try { 21 | if (lock.tryLock(5000L, TimeUnit.MILLISECONDS)) { 22 | //TODO 获得锁后要做的事 23 | }else{ 24 | //TODO 获得锁超时后要做的事 25 | } 26 | }finally { 27 | lock.unlock(); 28 | } 29 | } 30 | 31 | public static void test1(){ 32 | JedisPool jedisPool=new JedisPool("127.0.0.1",6379);//实际应用时可通过spring注入 33 | final RedisDistributedLockTemplate template=new RedisDistributedLockTemplate(jedisPool);//本类多线程安全,可通过spring注入 34 | template.execute("订单流水号", 5000, new Callback() { 35 | @Override 36 | public Object onGetLock() throws InterruptedException { 37 | //TODO 获得锁后要做的事 38 | return null; 39 | } 40 | 41 | @Override 42 | public Object onTimeout() throws InterruptedException { 43 | //TODO 获得锁超时后要做的事 44 | return null; 45 | } 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/distributed/lock/redis/RedisDistributedLockTemplate.java: -------------------------------------------------------------------------------- 1 | package com.distributed.lock.redis; 2 | 3 | import com.distributed.lock.Callback; 4 | import com.distributed.lock.DistributedLockTemplate; 5 | import com.distributed.lock.redis.RedisReentrantLock; 6 | import org.apache.curator.framework.CuratorFramework; 7 | import org.slf4j.LoggerFactory; 8 | import redis.clients.jedis.JedisPool; 9 | 10 | import java.util.concurrent.TimeUnit; 11 | 12 | /** 13 | * Created by sunyujia@aliyun.com on 2016/2/26. 14 | */ 15 | public class RedisDistributedLockTemplate implements DistributedLockTemplate { 16 | private static final org.slf4j.Logger log = LoggerFactory.getLogger(RedisDistributedLockTemplate.class); 17 | 18 | private JedisPool jedisPool; 19 | 20 | 21 | public RedisDistributedLockTemplate(JedisPool jedisPool) { 22 | this.jedisPool = jedisPool; 23 | } 24 | 25 | 26 | 27 | public Object execute(String lockId, int timeout, Callback callback) { 28 | RedisReentrantLock distributedReentrantLock = null; 29 | boolean getLock=false; 30 | try { 31 | distributedReentrantLock = new RedisReentrantLock(jedisPool,lockId); 32 | if(distributedReentrantLock.tryLock(new Long(timeout), TimeUnit.MILLISECONDS)){ 33 | getLock=true; 34 | return callback.onGetLock(); 35 | }else{ 36 | return callback.onTimeout(); 37 | } 38 | }catch(InterruptedException ex){ 39 | log.error(ex.getMessage(), ex); 40 | Thread.currentThread().interrupt(); 41 | }catch (Exception e) { 42 | log.error(e.getMessage(), e); 43 | }finally { 44 | if(getLock) { 45 | distributedReentrantLock.unlock(); 46 | } 47 | } 48 | return null; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/distributed/lock/zk/ZkDistributedLockTemplate.java: -------------------------------------------------------------------------------- 1 | package com.distributed.lock.zk; 2 | 3 | import com.distributed.lock.Callback; 4 | import com.distributed.lock.DistributedLockTemplate; 5 | import org.apache.curator.framework.CuratorFramework; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * Created by sunyujia@aliyun.com on 2016/2/26. 12 | */ 13 | public class ZkDistributedLockTemplate implements DistributedLockTemplate { 14 | private static final org.slf4j.Logger log = LoggerFactory.getLogger(ZkDistributedLockTemplate.class); 15 | 16 | private CuratorFramework client; 17 | 18 | 19 | public ZkDistributedLockTemplate(CuratorFramework client) { 20 | this.client = client; 21 | } 22 | 23 | 24 | 25 | private boolean tryLock(ZkReentrantLock distributedReentrantLock,Long timeout) throws Exception { 26 | return distributedReentrantLock.tryLock(timeout, TimeUnit.MILLISECONDS); 27 | } 28 | 29 | public Object execute(String lockId, int timeout, Callback callback) { 30 | ZkReentrantLock distributedReentrantLock = null; 31 | boolean getLock=false; 32 | try { 33 | distributedReentrantLock = new ZkReentrantLock(client,lockId); 34 | if(tryLock(distributedReentrantLock,new Long(timeout))){ 35 | getLock=true; 36 | return callback.onGetLock(); 37 | }else{ 38 | return callback.onTimeout(); 39 | } 40 | }catch(InterruptedException ex){ 41 | log.error(ex.getMessage(), ex); 42 | Thread.currentThread().interrupt(); 43 | }catch (Exception e) { 44 | log.error(e.getMessage(), e); 45 | }finally { 46 | if(getLock){ 47 | distributedReentrantLock.unlock(); 48 | } 49 | } 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/com/distributed/limit/redis/AccessSpeedLimitTest.java: -------------------------------------------------------------------------------- 1 | package com.distributed.limit.redis; 2 | 3 | import com.distributed.lock.redis.RedisDistributedLockTemplate; 4 | import org.junit.Test; 5 | import redis.clients.jedis.JedisPool; 6 | 7 | import java.text.SimpleDateFormat; 8 | import java.util.Date; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | /** 12 | * Created by sunyujia@aliyun.com on 2016/2/27. 13 | */ 14 | public class AccessSpeedLimitTest { 15 | @Test 16 | public void test1() throws InterruptedException { 17 | JedisPool jp=new JedisPool("127.0.0.1",6379); 18 | AccessSpeedLimit accessSpeedLimit=new AccessSpeedLimit(jp); 19 | SimpleDateFormat sdf=new SimpleDateFormat(" mm:ss"); 20 | while(true){ 21 | //10.0.0.1这个ip每1秒钟最多访问5次if块内代码. 22 | if(accessSpeedLimit.tryAccess("10.0.0.1", 1,5)){ 23 | System.out.println("yes"+sdf.format(new Date())); 24 | }else{ 25 | System.out.println("no"+sdf.format(new Date())); 26 | } 27 | Thread.sleep(100); 28 | } 29 | } 30 | 31 | @Test 32 | public void test2() throws InterruptedException { 33 | JedisPool jp=new JedisPool("127.0.0.1",6379); 34 | final RedisDistributedLockTemplate template=new RedisDistributedLockTemplate(jp); 35 | LimitRule limitRule=new LimitRule(); 36 | limitRule.setSeconds(1); 37 | limitRule.setLimitCount(5); 38 | limitRule.setLockCount(7); 39 | limitRule.setLockTime(2); 40 | AccessSpeedLimit accessSpeedLimit=new AccessSpeedLimit(jp); 41 | SimpleDateFormat sdf=new SimpleDateFormat(" mm:ss"); 42 | while(true){ 43 | //10.0.0.1这个ip每1秒钟最多访问5次if块内代码.1秒超过10次后,锁定2秒,2秒内无法访问. 44 | if(accessSpeedLimit.tryAccess("10.0.0.1",limitRule)){ 45 | System.out.println("yes"+sdf.format(new Date())); 46 | }else{ 47 | System.out.println("no"+sdf.format(new Date())); 48 | } 49 | Thread.sleep(100); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/distributed/sequence/zk/ZkDistributedSequence.java: -------------------------------------------------------------------------------- 1 | package com.distributed.sequence.zk; 2 | 3 | import com.distributed.lock.zk.ZkReentrantLockCleanerTask; 4 | import com.distributed.sequence.DistributedSequence; 5 | import org.apache.curator.RetryPolicy; 6 | import org.apache.curator.framework.CuratorFramework; 7 | import org.apache.curator.framework.CuratorFrameworkFactory; 8 | import org.apache.curator.retry.ExponentialBackoffRetry; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * Created by sunyujia@aliyun.com on 2016/2/25. 13 | */ 14 | public class ZkDistributedSequence implements DistributedSequence { 15 | private static final org.slf4j.Logger log = LoggerFactory.getLogger(ZkReentrantLockCleanerTask.class); 16 | 17 | private CuratorFramework client; 18 | /** 19 | * Curator RetryPolicy maxRetries 20 | */ 21 | private int maxRetries=3; 22 | /** 23 | * Curator RetryPolicy baseSleepTimeMs 24 | */ 25 | private final int baseSleepTimeMs=1000; 26 | 27 | public ZkDistributedSequence(String zookeeperAddress){ 28 | try{ 29 | RetryPolicy retryPolicy = new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries); 30 | client = CuratorFrameworkFactory.newClient(zookeeperAddress, retryPolicy); 31 | client.start(); 32 | }catch (Exception e){ 33 | log.error(e.getMessage(),e); 34 | }catch (Throwable ex){ 35 | ex.printStackTrace(); 36 | log.error(ex.getMessage(),ex); 37 | } 38 | } 39 | 40 | public int getMaxRetries() { 41 | return maxRetries; 42 | } 43 | 44 | public void setMaxRetries(int maxRetries) { 45 | this.maxRetries = maxRetries; 46 | } 47 | 48 | public int getBaseSleepTimeMs() { 49 | return baseSleepTimeMs; 50 | } 51 | 52 | public Long sequence(String sequenceName) { 53 | try { 54 | int value=client.setData().withVersion(-1).forPath("/"+sequenceName,"".getBytes()).getVersion(); 55 | return new Long(value); 56 | } catch (Exception e) { 57 | e.printStackTrace(); 58 | } 59 | return null; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/distributed/lock/zk/ZkReentrantLockCleanerTask.java: -------------------------------------------------------------------------------- 1 | package com.distributed.lock.zk; 2 | 3 | import org.apache.curator.RetryPolicy; 4 | import org.apache.curator.framework.CuratorFramework; 5 | import org.apache.curator.framework.CuratorFrameworkFactory; 6 | import org.apache.curator.retry.ExponentialBackoffRetry; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.util.List; 10 | import java.util.Timer; 11 | import java.util.TimerTask; 12 | 13 | /** 14 | * Created by sunyujia@aliyun.com on 2016/2/25. 15 | */ 16 | public class ZkReentrantLockCleanerTask extends TimerTask { 17 | private static final org.slf4j.Logger log = LoggerFactory.getLogger(ZkReentrantLockCleanerTask.class); 18 | 19 | private CuratorFramework client; 20 | 21 | private Timer timer; 22 | 23 | /** 24 | * 检查周期 25 | */ 26 | private long period=5000; 27 | /** 28 | * Curator RetryPolicy maxRetries 29 | */ 30 | private int maxRetries=3; 31 | /** 32 | * Curator RetryPolicy baseSleepTimeMs 33 | */ 34 | private final int baseSleepTimeMs=1000; 35 | 36 | public ZkReentrantLockCleanerTask(String zookeeperAddress) { 37 | try{ 38 | RetryPolicy retryPolicy = new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries); 39 | client = CuratorFrameworkFactory.newClient(zookeeperAddress, retryPolicy); 40 | client.start(); 41 | }catch (Exception e){ 42 | log.error(e.getMessage(),e); 43 | }catch (Throwable ex){ 44 | ex.printStackTrace(); 45 | log.error(ex.getMessage(),ex); 46 | } 47 | } 48 | 49 | public void start(){ 50 | timer.schedule(this,0,period); 51 | } 52 | 53 | private boolean isEmpty(List list){ 54 | return list==null||list.isEmpty(); 55 | } 56 | 57 | 58 | @Override 59 | public void run() { 60 | try { 61 | List childrenPaths=this.client.getChildren().forPath(ZkReentrantLock.ROOT_PATH); 62 | for(String path:childrenPaths){ 63 | cleanNode(path); 64 | } 65 | } catch (Exception e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | 70 | private void cleanNode(String path){ 71 | try { 72 | if(isEmpty(this.client.getChildren().forPath(path))){ 73 | this.client.delete().forPath(path);//利用存在子节点无法删除和zk的原子性这两个特性. 74 | } 75 | } catch (Exception e) { 76 | e.printStackTrace(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/com/distributed/lock/redis/RedisReentrantLockTemplateTest.java: -------------------------------------------------------------------------------- 1 | package com.distributed.lock.redis; 2 | 3 | import com.distributed.lock.Callback; 4 | import com.distributed.lock.zk.ZkDistributedLockTemplate; 5 | import org.apache.curator.RetryPolicy; 6 | import org.apache.curator.framework.CuratorFramework; 7 | import org.apache.curator.framework.CuratorFrameworkFactory; 8 | import org.apache.curator.retry.ExponentialBackoffRetry; 9 | import org.junit.Test; 10 | import redis.clients.jedis.JedisPool; 11 | 12 | import java.util.concurrent.CountDownLatch; 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.Executors; 15 | import java.util.concurrent.ThreadLocalRandom; 16 | 17 | /** 18 | * Created by sunyujia@aliyun.com on 2016/2/24. 19 | */ 20 | public class RedisReentrantLockTemplateTest { 21 | 22 | @Test 23 | public void testTry() throws InterruptedException { 24 | JedisPool jp=new JedisPool("127.0.0.1",6379); 25 | final RedisDistributedLockTemplate template=new RedisDistributedLockTemplate(jp); 26 | 27 | int size=100; 28 | final CountDownLatch startCountDownLatch = new CountDownLatch(1); 29 | final CountDownLatch endDownLatch=new CountDownLatch(size); 30 | for (int i =0;i 2 | 3 | 4 | 5 | 6 | 7 | 8 | ${LOG_MSG} 9 | 10 | 11 | 12 | 13 | DEBUG 14 | 15 | ${USER_HOME}/debug.log 16 | 17 | ${LOG_DIR}/debug%i.log 18 | 19 | 20MB 20 | 21 | 22 | 23 | ${LOG_MSG} 24 | 25 | 26 | 27 | ${USER_HOME}/info.log 28 | 29 | INFO 30 | 31 | 32 | ${LOG_DIR}/info%i.log 33 | 34 | 20MB 35 | 36 | 37 | 38 | ${LOG_MSG} 39 | 40 | 41 | 42 | ${USER_HOME}/error.log 43 | 44 | ERROR 45 | 46 | 47 | ${LOG_DIR}/error%i.log 48 | 49 | 20MB 50 | 51 | 52 | 53 | ${LOG_MSG} 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | dance 8 | Distributed-Kit 9 | 0.0.1 10 | 11 | UTF-8 12 | 13 | 14 | 15 | org.slf4j 16 | slf4j-api 17 | 1.7.5 18 | 19 | 20 | ch.qos.logback 21 | logback-core 22 | 1.0.13 23 | 24 | 25 | ch.qos.logback 26 | logback-access 27 | 1.0.13 28 | 29 | 30 | ch.qos.logback 31 | logback-classic 32 | 1.0.13 33 | 34 | 35 | org.apache.curator 36 | curator-recipes 37 | 2.9.1 38 | 39 | 40 | junit 41 | junit 42 | 4.12 43 | 44 | 45 | redis.clients 46 | jedis 47 | 2.7.2 48 | 49 | 50 | 51 | 52 | 53 | maven-compiler-plugin 54 | 2.5.1 55 | 56 | 1.7 57 | 1.7 58 | UTF-8 59 | 60 | 61 | 62 | org.apache.maven.plugins 63 | maven-surefire-plugin 64 | 65 | true 66 | 67 | 68 | 69 | 70 | 71 | 72 | Sun yujia 73 | yujiasun 74 | yujiasun@aliyun.com 75 | 76 | Developer 77 | 78 | +8 79 | 80 | 81 | -------------------------------------------------------------------------------- /src/main/java/com/distributed/lock/redis/RedisReentrantLock.java: -------------------------------------------------------------------------------- 1 | package com.distributed.lock.redis; 2 | 3 | import com.distributed.lock.DistributedReentrantLock; 4 | import com.google.common.collect.Maps; 5 | import redis.clients.jedis.Jedis; 6 | import redis.clients.jedis.JedisPool; 7 | import redis.clients.jedis.exceptions.JedisException; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Random; 12 | import java.util.concurrent.ConcurrentMap; 13 | import java.util.concurrent.ThreadLocalRandom; 14 | import java.util.concurrent.TimeUnit; 15 | import java.util.concurrent.atomic.AtomicInteger; 16 | 17 | /** 18 | * Created by sunyujia@aliyun.com on 2016/2/26. 19 | */ 20 | public class RedisReentrantLock implements DistributedReentrantLock { 21 | 22 | private final ConcurrentMap threadData = Maps.newConcurrentMap(); 23 | 24 | private JedisPool jedisPool; 25 | 26 | private RedisLockInternals internals; 27 | 28 | private String lockId; 29 | 30 | 31 | public RedisReentrantLock(JedisPool jedisPool,String lockId) { 32 | this.jedisPool = jedisPool; 33 | this.lockId=lockId; 34 | this.internals=new RedisLockInternals(jedisPool); 35 | } 36 | 37 | private static class LockData { 38 | final Thread owningThread; 39 | final String lockVal; 40 | final AtomicInteger lockCount = new AtomicInteger(1); 41 | 42 | private LockData(Thread owningThread, String lockVal) { 43 | this.owningThread = owningThread; 44 | this.lockVal = lockVal; 45 | } 46 | } 47 | 48 | @Override 49 | public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException{ 50 | Thread currentThread = Thread.currentThread(); 51 | LockData lockData = threadData.get(currentThread); 52 | if ( lockData != null ) { 53 | lockData.lockCount.incrementAndGet(); 54 | return true; 55 | } 56 | String lockVal = internals.tryRedisLock(lockId,timeout,unit); 57 | if ( lockVal != null ) { 58 | LockData newLockData = new LockData(currentThread, lockVal); 59 | threadData.put(currentThread, newLockData); 60 | return true; 61 | } 62 | return false; 63 | } 64 | 65 | @Override 66 | public void unlock() { 67 | Thread currentThread = Thread.currentThread(); 68 | LockData lockData = threadData.get(currentThread); 69 | if ( lockData == null ) { 70 | throw new IllegalMonitorStateException("You do not own the lock: " + lockId); 71 | } 72 | int newLockCount = lockData.lockCount.decrementAndGet(); 73 | if ( newLockCount > 0 ) { 74 | return; 75 | } 76 | if ( newLockCount < 0 ) { 77 | throw new IllegalMonitorStateException("Lock count has gone negative for lock: " + lockId); 78 | } 79 | try { 80 | internals.unlockRedisLock(lockId,lockData.lockVal); 81 | } finally { 82 | threadData.remove(currentThread); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/com/distributed/lock/zk/ZkReentrantLockTemplateTest.java: -------------------------------------------------------------------------------- 1 | package com.distributed.lock.zk; 2 | 3 | import com.distributed.lock.Callback; 4 | import com.distributed.lock.redis.RedisDistributedLockTemplate; 5 | import org.apache.curator.RetryPolicy; 6 | import org.apache.curator.framework.CuratorFramework; 7 | import org.apache.curator.framework.CuratorFrameworkFactory; 8 | import org.apache.curator.retry.ExponentialBackoffRetry; 9 | import org.junit.Test; 10 | import redis.clients.jedis.JedisPool; 11 | 12 | import java.util.concurrent.*; 13 | 14 | /** 15 | * Created by sunyujia@aliyun.com on 2016/2/24. 16 | */ 17 | 18 | 19 | public class ZkReentrantLockTemplateTest { 20 | 21 | @Test 22 | public void testTry() throws InterruptedException { 23 | RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); 24 | CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", retryPolicy); 25 | client.start(); 26 | 27 | final ZkDistributedLockTemplate template=new ZkDistributedLockTemplate(client); 28 | int size=100; 29 | final CountDownLatch startCountDownLatch = new CountDownLatch(1); 30 | final CountDownLatch endDownLatch=new CountDownLatch(size); 31 | for (int i =0;i keys = new ArrayList(); 69 | keys.add(newKey); 70 | List args = new ArrayList(); 71 | args.add(Math.max(limitRule.getLimitCount(), limitRule.getLockCount())+""); 72 | args.add(limitRule.getSeconds()+""); 73 | args.add(limitRule.getLockCount()+""); 74 | args.add(limitRule.getLockTime()+""); 75 | count=Long.parseLong(jedis.eval(buildLuaScript(limitRule),keys,args)+""); 76 | return count<=limitRule.getLimitCount(); 77 | } finally { 78 | if(jedis!=null)jedis.close(); 79 | } 80 | } 81 | 82 | 83 | private String buildLuaScript(LimitRule limitRule){ 84 | StringBuilder lua=new StringBuilder(); 85 | lua.append("\nlocal c"); 86 | lua.append("\nc = redis.call('get',KEYS[1])"); 87 | lua.append("\nif c and tonumber(c) > tonumber(ARGV[1]) then"); 88 | lua.append("\nreturn c;"); 89 | lua.append("\nend"); 90 | lua.append("\nc = redis.call('incr',KEYS[1])"); 91 | lua.append("\nif tonumber(c) == 1 then"); 92 | lua.append("\nredis.call('expire',KEYS[1],ARGV[2])"); 93 | lua.append("\nend"); 94 | if(limitRule.enableLimitLock()){ 95 | lua.append("\nif tonumber(c) > tonumber(ARGV[3]) then"); 96 | lua.append("\nredis.call('expire',KEYS[1],ARGV[4])"); 97 | lua.append("\nend"); 98 | } 99 | lua.append("\nreturn c;"); 100 | return lua.toString(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/distributed/lock/zk/ZkReentrantLock.java: -------------------------------------------------------------------------------- 1 | package com.distributed.lock.zk; 2 | 3 | import com.distributed.lock.DistributedReentrantLock; 4 | import com.google.common.collect.Maps; 5 | import org.apache.curator.framework.CuratorFramework; 6 | import org.apache.curator.framework.recipes.locks.InterProcessMutex; 7 | import org.apache.curator.framework.recipes.locks.LockInternalsDriver; 8 | import org.apache.curator.framework.recipes.locks.StandardLockInternalsDriver; 9 | import org.apache.zookeeper.KeeperException; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.util.List; 13 | import java.util.concurrent.*; 14 | import java.util.concurrent.locks.ReentrantLock; 15 | 16 | /** 17 | * 基于Zookeeper的可重入互斥锁(关于重入:仅限于持有zk锁的jvm内重入) 18 | * Created by sunyujia@aliyun.com on 2016/2/24. 19 | */ 20 | public class ZkReentrantLock implements DistributedReentrantLock { 21 | private static final org.slf4j.Logger log = LoggerFactory.getLogger(ZkReentrantLock.class); 22 | 23 | /** 24 | * 线程池 25 | */ 26 | private static final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10); 27 | 28 | /** 29 | * 所有PERSISTENT锁节点的根位置 30 | */ 31 | public static final String ROOT_PATH = "/ROOT_LOCK/"; 32 | 33 | /** 34 | * 每次延迟清理PERSISTENT节点的时间 Unit:MILLISECONDS 35 | */ 36 | private long delayTimeForClean = 1000; 37 | 38 | /** 39 | * zk 共享锁实现 40 | */ 41 | private InterProcessMutex interProcessMutex = null; 42 | 43 | 44 | /** 45 | * 锁的ID,对应zk一个PERSISTENT节点,下挂EPHEMERAL节点. 46 | */ 47 | private String path; 48 | 49 | 50 | /** 51 | * zk的客户端 52 | */ 53 | private CuratorFramework client; 54 | 55 | 56 | 57 | public ZkReentrantLock(CuratorFramework client, String lockId) { 58 | init(client, lockId); 59 | } 60 | 61 | public void init(CuratorFramework client, String lockId) { 62 | this.client = client; 63 | this.path = ROOT_PATH + lockId; 64 | interProcessMutex = new InterProcessMutex(client, this.path); 65 | } 66 | 67 | @Override 68 | public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { 69 | try { 70 | return interProcessMutex.acquire(timeout, unit); 71 | } catch (InterruptedException e) { 72 | throw e; 73 | } catch (Exception e) { 74 | log.error(e.getMessage(),e); 75 | throw new RuntimeException(e.getMessage(),e); 76 | } 77 | } 78 | 79 | @Override 80 | public void unlock() { 81 | try { 82 | interProcessMutex.release(); 83 | } catch (Throwable e) { 84 | log.error(e.getMessage(), e); 85 | } finally { 86 | executorService.schedule(new Cleaner(client, path), delayTimeForClean, TimeUnit.MILLISECONDS); 87 | } 88 | } 89 | 90 | static class Cleaner implements Runnable { 91 | CuratorFramework client; 92 | String path; 93 | 94 | public Cleaner(CuratorFramework client, String path) { 95 | this.client = client; 96 | this.path = path; 97 | } 98 | 99 | public void run() { 100 | try { 101 | List list = client.getChildren().forPath(path); 102 | if (list == null || list.isEmpty()) { 103 | client.delete().forPath(path); 104 | } 105 | } catch (KeeperException.NoNodeException e1) { 106 | //nothing 107 | } catch (KeeperException.NotEmptyException e2) { 108 | //nothing 109 | } catch (Exception e) { 110 | log.error(e.getMessage(), e);//准备删除时,正好有线程创建锁 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Distributed-Kit 2 | 基于redis和zookeeper分布式工具集-包括:分布式锁实现,分布式速率限制器,分布式ID生成器等. 3 | 4 | ## 使用 5 | ### maven: 需先编译安装到本地仓库或者本地私服 。 6 |

  7 |     <dependency>
  8 |       <groupId>dance</groupId>
  9 |       <artifactId>Distributed-Kit</artifactId>
 10 |       <version>0.0.1</version>
 11 |     </dependency>
 12 | 
13 | 14 | ##基于Redis实现的分布式锁(可重入) 15 | ~~~ java 16 | public static void main(String[] args){ 17 | JedisPool jedisPool=new JedisPool("127.0.0.1",6379);//实际应用时可通过spring注入 18 | final RedisDistributedLockTemplate template=new RedisDistributedLockTemplate(jedisPool);//本类线程安全,可通过spring注入 19 | template.execute("订单流水号", 5000, new Callback() {//获取锁超时时间为5秒 20 | @Override 21 | public Object onGetLock() throws InterruptedException { 22 | //TODO 获得锁后要做的事 23 | return null; 24 | } 25 | 26 | @Override 27 | public Object onTimeout() throws InterruptedException { 28 | //TODO 获得锁超时后要做的事 29 | return null; 30 | } 31 | }); 32 | } 33 | ~~~ 34 | ~~~ java 35 | public static void main(String[] args) throws Exception { 36 | JedisPool jedisPool=new JedisPool("127.0.0.1",6379);//实际应用时可通过spring注入 37 | RedisReentrantLock lock=new RedisReentrantLock(jedisPool,"订单流水号"); 38 | try { 39 | if (lock.tryLock(5000L, TimeUnit.MILLISECONDS)) {//获取锁超时时间为5秒 40 | //TODO 获得锁后要做的事 41 | }else{ 42 | //TODO 获得锁超时后要做的事 43 | } 44 | }finally { 45 | lock.unlock(); 46 | } 47 | } 48 | ~~~ 49 | [测试本实现的可靠性见测试用例](https://github.com/yujiasun/Distributed-Kit/blob/master/src/test/java/com/distributed/lock/redis/RedisReentrantLockTemplateTest.java) 50 | 51 | ##基于Zookeeper实现的分布式锁( 可重入 ) 52 | ~~~ java 53 | public static void main(String[] args){ 54 | RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); 55 | CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", retryPolicy); 56 | client.start(); 57 | 58 | final ZkDistributedLockTemplate template=new ZkDistributedLockTemplate(client);//本类多线程安全,可通过spring注入 59 | template.execute("订单流水号", 5000, new Callback() {//获取锁超时时间为5秒 60 | @Override 61 | public Object onGetLock() throws InterruptedException { 62 | //TODO 获得锁后要做的事 63 | return null; 64 | } 65 | 66 | @Override 67 | public Object onTimeout() throws InterruptedException { 68 | //TODO 获得锁超时后要做的事 69 | return null; 70 | } 71 | }); 72 | } 73 | ~~~ 74 | [测试本实现的可靠性见测试用例](https://github.com/yujiasun/Distributed-Kit/blob/master/src/test/java/com/distributed/lock/zk/ZkReentrantLockTemplateTest.java) 75 | 76 | ##基于Redis实现的分布式速率限制器 77 | 78 | 限制的资源,可以是ip,用户id,订单id,手机号,等等. 79 | * 例如限制一个手机号每分钟只能发1条短信. 80 | * 例如限制一个手机号每10秒钟只能发起1次表单提交请求. 81 | * 例如限制一个ip地址每秒钟只能访问10次特定的资源. 82 | 83 | ~~~ java 84 | public class AccessSpeedLimitTest { 85 | @Test 86 | public void test1() throws InterruptedException { 87 | JedisPool jp=new JedisPool("127.0.0.1",6379); 88 | AccessSpeedLimit accessSpeedLimit=new AccessSpeedLimit(jp); 89 | SimpleDateFormat sdf=new SimpleDateFormat(" mm:ss"); 90 | while(true){ 91 | //10.0.0.1这个ip每1秒钟最多访问5次if块内代码 92 | if(accessSpeedLimit.tryAccess("10.0.0.1", 1,5)){ 93 | System.out.println("yes"+sdf.format(new Date())); 94 | }else{ 95 | System.out.println("no"+sdf.format(new Date())); 96 | } 97 | Thread.sleep(100); 98 | } 99 | } 100 | 101 | @Test 102 | public void test2() throws InterruptedException { 103 | JedisPool jp=new JedisPool("127.0.0.1",6379); 104 | final RedisDistributedLockTemplate template=new RedisDistributedLockTemplate(jp); 105 | LimitRule limitRule=new LimitRule(); 106 | limitRule.setSeconds(1); 107 | limitRule.setLimitCount(5); 108 | limitRule.setLockCount(7); 109 | limitRule.setLockTime(2); 110 | AccessSpeedLimit accessSpeedLimit=new AccessSpeedLimit(jp); 111 | SimpleDateFormat sdf=new SimpleDateFormat(" mm:ss"); 112 | while(true){ 113 | //10.0.0.1这个ip每1秒钟最多访问5次if块内代码.1秒超过10次后,锁定2秒,2秒内无法访问. 114 | if(accessSpeedLimit.tryAccess("10.0.0.1",limitRule)){ 115 | System.out.println("yes"+sdf.format(new Date())); 116 | }else{ 117 | System.out.println("no"+sdf.format(new Date())); 118 | } 119 | Thread.sleep(100); 120 | } 121 | } 122 | } 123 | ~~~ 124 | 125 | #技术交流: 126 | QQ: 4115291 127 | Mail: sunyujia.d@gmail.com -------------------------------------------------------------------------------- /src/main/java/com/distributed/lock/redis/RedisLockInternals.java: -------------------------------------------------------------------------------- 1 | package com.distributed.lock.redis; 2 | 3 | import org.jboss.netty.util.internal.NonReentrantLock; 4 | import org.slf4j.LoggerFactory; 5 | import redis.clients.jedis.Jedis; 6 | import redis.clients.jedis.JedisPool; 7 | import redis.clients.jedis.exceptions.JedisException; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.concurrent.ThreadLocalRandom; 12 | import java.util.concurrent.TimeUnit; 13 | import java.util.concurrent.locks.Condition; 14 | import java.util.concurrent.locks.Lock; 15 | import java.util.concurrent.locks.LockSupport; 16 | import java.util.concurrent.locks.ReentrantLock; 17 | 18 | /** 19 | * Created by sunyujia@aliyun.com on 2016/2/26. 20 | */ 21 | class RedisLockInternals { 22 | private static final org.slf4j.Logger log = LoggerFactory.getLogger(RedisLockInternals.class); 23 | 24 | private JedisPool jedisPool; 25 | 26 | /** 27 | * 重试等待时间 28 | */ 29 | private int retryAwait=300; 30 | 31 | private int lockTimeout=2000; 32 | 33 | 34 | RedisLockInternals(JedisPool jedisPool) { 35 | this.jedisPool = jedisPool; 36 | } 37 | 38 | String tryRedisLock(String lockId,long time, TimeUnit unit) { 39 | final long startMillis = System.currentTimeMillis(); 40 | final Long millisToWait = (unit != null) ? unit.toMillis(time) : null; 41 | String lockValue=null; 42 | while (lockValue==null){ 43 | lockValue=createRedisKey(lockId); 44 | if(lockValue!=null){ 45 | break; 46 | } 47 | if(System.currentTimeMillis()-startMillis-retryAwait>millisToWait){ 48 | break; 49 | } 50 | LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(retryAwait)); 51 | } 52 | return lockValue; 53 | } 54 | 55 | private String createRedisKey(String lockId) { 56 | Jedis jedis = null; 57 | boolean broken = false; 58 | try { 59 | String value=lockId+randomId(1); 60 | jedis = jedisPool.getResource(); 61 | String luaScript = "" 62 | + "\nlocal r = tonumber(redis.call('SETNX', KEYS[1],ARGV[1]));" 63 | + "\nredis.call('PEXPIRE',KEYS[1],ARGV[2]);" 64 | + "\nreturn r"; 65 | List keys = new ArrayList(); 66 | keys.add(lockId); 67 | List args = new ArrayList(); 68 | args.add(value); 69 | args.add(lockTimeout+""); 70 | Long ret = (Long) jedis.eval(luaScript, keys, args); 71 | if( new Long(1).equals(ret)){ 72 | return value; 73 | } 74 | }finally { 75 | if(jedis!=null) jedis.close(); 76 | } 77 | return null; 78 | } 79 | 80 | void unlockRedisLock(String key,String value) { 81 | Jedis jedis = null; 82 | boolean broken = false; 83 | try { 84 | jedis = jedisPool.getResource(); 85 | String luaScript="" 86 | +"\nlocal v = redis.call('GET', KEYS[1]);" 87 | +"\nlocal r= 0;" 88 | +"\nif v == ARGV[1] then" 89 | +"\nr =redis.call('DEL',KEYS[1]);" 90 | +"\nend" 91 | +"\nreturn r"; 92 | List keys = new ArrayList(); 93 | keys.add(key); 94 | List args = new ArrayList(); 95 | args.add(value); 96 | Object r=jedis.eval(luaScript, keys, args); 97 | } finally { 98 | if(jedis!=null) jedis.close(); 99 | } 100 | } 101 | 102 | private final static char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', 103 | '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 104 | 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 105 | 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 106 | 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 107 | 'Z'}; 108 | 109 | private String randomId(int size) { 110 | char[] cs = new char[size]; 111 | for (int i = 0; i < cs.length; i++) { 112 | cs[i] = digits[ThreadLocalRandom.current().nextInt(digits.length)]; 113 | } 114 | return new String(cs); 115 | } 116 | 117 | public static void main(String[] args){ 118 | System.out.println(System.currentTimeMillis()); 119 | LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(300)); 120 | System.out.println(System.currentTimeMillis()); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------