项目背景
最近做的项目有个需求:生成一则工单推送给用户后,等待用户处理。15分钟后再查询工单的状态,如果超时未处理,则推送一则超时工单消息通知用户。
实现的方式很多,这里不做讨论。由于项目规模不是很大,这里不想引入MQ中间件,增加系统复杂度。所以用JDK 提供的 DelayQueue 实现了一把延迟队列 ~~
代码实现
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* @author huanghaihua
* @since 2021/11/27
* @description 延迟实体
*/
public class DelayedItem<K> implements Delayed {
/** key **/
private K t;
/** 延迟时间 **/
private long liveTime;
/** 移除的具体时间 **/
private long removeTime;
public DelayedItem(K t, long liveTime) {
this.setK(t);
this.liveTime = liveTime;
this.removeTime = TimeUnit.NANOSECONDS.convert(liveTime, TimeUnit.MILLISECONDS) + System.nanoTime();
}
@Override
public long getDelay(TimeUnit unit) {
long i = unit.convert(removeTime - System.nanoTime(), unit);
return i;
}
@Override
public int compareTo(Delayed o) {
if (o == null)
return 1;
if (o == this)
return 0;
if (o instanceof DelayedItem) {
DelayedItem<K> tmpDelayedItem = (DelayedItem<K>) o;
if (removeTime > tmpDelayedItem.removeTime) {
return 1;
} else if (removeTime == tmpDelayedItem.removeTime) {
return 0;
} else {
return -1;
}
}
long diff = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
return diff > 0 ? 1 : diff == 0 ? 0 : -1;
}
public K getK() {
return t;
}
public void setK(K t) {
this.t = t;
}
public int hashCode() {
return t.hashCode();
}
public boolean equals(Object object) {
if (object instanceof DelayedItem) {
return object.hashCode() == hashCode() ? true : false;
}
return false;
}
}
import java.util.Objects;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author huanghaihua
* @since 2021/11/27
* @description 延迟缓存
*/
@Slf4j
public abstract class DelayCache<K> {
public DelayQueue<DelayedItem<K>> queue = new DelayQueue<>();
// 启动10个线程去查询 防止阻塞
ExecutorService cachedThreadPool = Executors.newFixedThreadPool(10);
/**
* put: 延迟队列
*
* @param k
* key
* @param liveTime
* 延迟时间 单位毫秒 异常: @exception
*/
public void put(K k, long liveTime) {
log.info(">>>[put]>>>put key:" + k + " into delayQueue cache!");
DelayedItem<K> tmpItem = new DelayedItem<>(k, liveTime);
queue.put(tmpItem);
}
public DelayCache() {
Thread t = new Thread(() -> {
try {
dameonCheckOverdueKey();
} catch (InterruptedException e) {
log.error(e.getMessage(),e);
}
});
/**
* setDaemon():
* 程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成
* 。如果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是,只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,
* 这时就可以用setDaemon方法了。
*/
t.setDaemon(true);
t.start();
}
public void dameonCheckOverdueKey() throws InterruptedException {
while (true) {
DelayedItem<K> delayedItem = queue.take();
if (Objects.nonNull(delayedItem)) {
log.info(">>>>[dameonCheckOverdueKey]>>> get key:" + delayedItem.getK() + " from delayQueue cache!");
cachedThreadPool.execute(() -> invoke(delayedItem.getK()));
}
}
}
public abstract void invoke(K k);
}
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.IntStream;
/**
* @author huanghaihua
* @since 2021/11/27
* @description 延迟调用缓存队列
*/
@Slf4j
public class LocalCacheDelay extends DelayCache<String> {
public static final LocalCacheDelay service = new LocalCacheDelay();
/**
* put: 延迟队列
* @param oderKey
* K
* @param liveTime
* 延迟时间 单位毫秒
*/
public void put(String oderKey, long liveTime) {
super.put(oderKey, liveTime);
}
public void invoke(String k) {
try {
log.info(">>>>>[invoke]>>>> receive key: {}",k);
} catch (Exception e) {
log.error(e.getMessage(),e);
}
}
/**
* 模仿测试用,生成可选择去掉
*/
public static void main(String[] args) throws InterruptedException {
//构造100w个用户
List<String> users = new ArrayList<>(1000000);
IntStream.range(0, 1000000).parallel().forEach(b -> users.add("路人乙:" + b));
//并行流执行 ,15分钟
users.parallelStream().forEach(u -> LocalCacheDelay.service.put(u,900000));
}
}
运行结果
15分钟前:
经过漫长的等待,15分钟后:
如有问题,请留言指导修正~ emememmmm