(1). Redisson是什么?

Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid).
它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务.其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) Redisson提供了使用Redis的最简单和最便捷的方法.
Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上.

(2). Redisson 限流案例

package help.lixin.redisson;

import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.TransportMode;

public class RateLimiterTest {

	private static RedissonClient client = null;

	@BeforeClass
	public static void init() {
		Config config = new Config();
		config.setTransportMode(TransportMode.NIO);
		config.useSingleServer().setAddress("redis://127.0.0.1:6379");
		client = Redisson.create(config);
	} // end init
	
	// 测试限流
	@Test
	public void testRateLimiter() {
		RRateLimiter rateLimiter = client.getRateLimiter("myRateLimiter");
		// 初始化
		// 最大流速 = 每1秒钟产生2个令牌
		rateLimiter.trySetRate(RateType.OVERALL, 2, 1, RateIntervalUnit.SECONDS);

		// 创建三个线程来测试,通过日志上的时间来验证.
		Thread t1 = new Thread() {
			public void run() {
				System.out.println("t1 acquire start " + new Date());
				rateLimiter.acquire(1);
				System.out.println("t1 acquire end " + new Date());
			}
		};

		Thread t2 = new Thread() {
			public void run() {
				System.out.println("t2 acquire start " + new Date());
				rateLimiter.acquire(1);
				System.out.println("t2 acquire end " + new Date());
			}
		};

		Thread t3 = new Thread() {
			public void run() {
				System.out.println("t3 acquire start " + new Date());
				rateLimiter.acquire(1);
				System.out.println("t3 acquire end " + new Date());
			}
		};

		t1.start();
		t2.start();
		t3.start();

		CountDownLatch latch = new CountDownLatch(1);
		try {
			latch.await();
		} catch (InterruptedException e) {
		}
	} // end testRateLimiter
}

(3). Redisson限流案例(结果分析)

# 令牌桶逻辑为:每1秒钟产生2个令牌
# t1,t2,t3同时进入,获取令牌方法.
t2 acquire start Thu May 13 17:13:41 CST 2021
t3 acquire start Thu May 13 17:13:41 CST 2021
t1 acquire start Thu May 13 17:13:41 CST 2021

# t1,t3是获取到了令牌(正好是1秒2个)
t1 acquire end Thu May 13 17:13:41 CST 2021
t3 acquire end Thu May 13 17:13:41 CST 2021

# t2 等待下一秒令牌的生成,并获取
t2 acquire end Thu May 13 17:13:42 CST 2021

(4). Redisson分布式锁案例

package help.lixin.redisson;

import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.TransportMode;

public class LockTest {

	private static RedissonClient client = null;

	@BeforeClass
	public static void init() {
		Config config = new Config();
		config.setTransportMode(TransportMode.NIO);
		// 监控狗,每隔10秒续租一次
		config.setLockWatchdogTimeout(10000);
		config.useSingleServer().setAddress("redis://127.0.0.1:6379");
		client = Redisson.create(config);
	} // end init
	
	@Test
	public void testLock() {
		CountDownLatch latch = new CountDownLatch(2);
		
		Runnable r = ()->{
			RLock lock = client.getLock("anyLock");
			try {
				System.out.println(Thread.currentThread().getName() + " lock start before " + new Date());
				// 只有在这种加锁(lock.lock())的模式下,看门狗才会帮你续租(访方法是个阻塞方法)
				lock.lock();

				// 以下这几种模式,都会自动释放锁,不会帮你进行续租
				// lock.lock(10, TimeUnit.SECONDS);
				// boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
				System.out.println(Thread.currentThread().getName() + " lock SUCCESS,before Runntin Business " + new Date());
				// 模拟:业务执行时间为30秒,想知道看门狗是否会自动帮我续租.
				TimeUnit.SECONDS.sleep(30);
				System.out.println(Thread.currentThread().getName() + " lock SUCCESS,after Runntin Business " + new Date());
			} catch (InterruptedException ignore) {
			} finally {
				System.out.println(Thread.currentThread().getName() + " unlock " + new Date());
				lock.unlock();
			}
			latch.countDown();
		};
		
		Thread t1 = new Thread(r, "t1");
		Thread t2 = new Thread(r, "t2");
		t1.start();
		t2.start();
		
		try {
			latch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	} // end testLock
}

(5). Redisson分布式锁案例(结果分析)

结论:lock.lock()方法是阻塞方法,同时,看门狗会帮忙续租,其余lock方法,锁会自动释放(结合Redis TTL验证).

# 在加锁之前:t1和t2同时,进入方法,并打印日志
t1 lock start before Thu May 13 17:04:00 CST 2021
t2 lock start before Thu May 13 17:04:00 CST 2021

# t2先加锁成功,执行业务逻辑(业务逻辑时间花费了30秒),然后,t2释放锁
t2 lock SUCCESS,before Runntin Business Thu May 13 17:04:00 CST 2021
t2 lock SUCCESS,after Runntin Business Thu May 13 17:04:30 CST 2021
t2 unlock Thu May 13 17:04:30 CST 2021

# 只有t2释放锁,t1才会继续执行.
t1 lock SUCCESS,before Runntin Business Thu May 13 17:04:30 CST 2021
t1 lock SUCCESS,after Runntin Business Thu May 13 17:05:00 CST 2021
t1 unlock Thu May 13 17:05:00 CST 2021

(6). 总结

如果要使用令牌桶限流或者分布锁的话,直接使用:Redisson框架即可.
后面,我也会着重对这两者的源码进行剖析.