主要内容:
1. 使用redis实现分布式延迟队列(redis2.9 版本),用redis锁
2. 使用zookeeper分布式锁优化延迟队列读取
3. 使用延迟队列强制释放过期的zookeeper锁
用到的依赖:
spring框架
redis:
redis.clients:jedis:2.9.0
zookeeper&curator:
org.apache.zookeeper:zookeeper:3.4.6
org.apache.curator:curator-framework:2.12.0
org.apache.curator:curator-recipes:2.12.0
一. 使用redis实现分布式延迟队列
使用redis的zset功能作为延迟队列,使用时间戳unix timestamp作为score,取的时候取超过当前时间的score。
redis为2.9版本,我翻破了参考文档也没有找到一个可以原子性删除+取回zset某范围内一定数量的值的方法(头很痛),所以只能用锁来控制这个zset的访问,如果有什么好办法请告诉我。
因为可能有很多值在同一时间被取出,如果一次取出zset某时间段内所有值会造成单台机器压力过大,需要限制每次取出的最大量。
下面是一个延迟队列的例子,时间单位全部是毫秒。
import com.study.javaweb.test1.utils.JsonUtils;
import com.study.javaweb.test1.utils.StringUtils;
import com.study.javaweb.test1.utils.TimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Tuple;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @Author: longjuanfeng
* @Date: 8/13/18
* @Description:
*/
@Component
@Slf4j
public class RedisDelayQueue {
@Autowired
private Jedis jedis;
private final String delayQueuePrefix = "d_q_p:";
private final String delayQueueLock = "d_q_l:";
public void push(String name, Map<?, Long> dataList) {
if (CollectionUtils.isEmpty(dataList)) {
return;
}
String key = delayQueuePrefix + name;
Pipeline pipeline = jedis.pipelined();
dataList.forEach((data, delay) -> {
if (data == null || delay == null) {
return;
}
//JsonUtils把类转化为json string存储在redis
pipeline.zadd(key, System.currentTimeMillis() + delay, JsonUtils.toJson(data));
});
pipeline.sync();
}
public <T> Map<T, Long> poll(String name, Long timeout, Integer count, Class<T> clazz) {
String queueLock = delayQueueLock + name;
Long waitTime = 0L;
while (waitTime < timeout) {
String result = jedis.set(queueLock, "1", "NX", "EX", 2000);
if (StringUtils.isEmpty(result)) {
TimeUtils.sleep(50L);
waitTime += 50;
} else {
break;
}
}
if (waitTime >= timeout) {
return null;
}
try {
String key = de