Top N
top排名在实际工作中很常见,比如业务系统中最常见的前10异常信息或者是疫情期间统计进入西安人员户籍所在地,按人数排名前5的城市等。
Top模型
常见模型是以KV形式存在,K即是统计的维度,按V排名。
设计实现
信息存储在内存中,同时提供清理策略,对于Top N ,排名N后全部清理。出于性能考虑,存储信息时,不对信息做特殊处理 、保证信息可以快速存储,同时采用异步处理支持高并发;消息清理机制中实现排序功能,对于排序在N后全部清理。
信息存储实现
/** 基础信息 */
private static final int MAX_RECODER = 100;
private static ConcurrentHashMap<String, Message> topMessage = new ConcurrentHashMap<>();
private static Lock lock = new ReentrantLock();
/** Demo以此方式创建线程池,业务中最好通过另外方式创建*/
private static ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("Top100-thread-%d").build());
private static ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setNameFormat("Top100-Recycle-thread-%d").build());
如果统计信息并发高,可增大线程池。
public static void addMessage(String message) {
executorService.execute(() -> {
add(message);
});
}
private static void add(String message) {
Message obj = topMessage.get(message);
if (null == obj) {
if (lock.tryLock()) {
try {
obj = new Message();
obj.setMessage(message);
} finally {
lock.unlock();
}
} else {
while (null == obj) {
obj = topMessage.get(message);
}
}
}
obj.incount();
topMessage.put(message, obj);
}
清理策略实现
采用定时任务定时清理,避免并发高时每次都清理,故定时批量清理,提高性能
/** 间隔5秒清理*/
public static void recyleSchedule() {
scheduledExecutorService.scheduleAtFixedRate(() -> {
recycle();
}, 0, 5, TimeUnit.SECONDS);
}
/** 批量清理*/
private static void recycle() {
if (topMessage.size() > MAX_RECODER) {
ConcurrentHashMap<String, Message> copyMap = new ConcurrentHashMap<>();
SortedSet<Message> sortedSet = new TreeSet<>(topMessage.values());
sortedSet.forEach(x -> {
copyMap.put(x.getMessage(), x);
if (copyMap.size() >= MAX_RECODER) {
return;
}
});
topMessage = copyMap;
}
}
信息获取
//获取前top条信息
public static Collection<Message> getTop(int top) {
return getAny(top);
}
//获取所有信息
public static Collection<Message> getAll() {
return getAny(MAX_RECODER);
}
private static Collection<Message> getAny(int top) {
List<Message> result = new ArrayList<>();
SortedSet<Message> sortedSet = new TreeSet<>(topMessage.values());
sortedSet.forEach(x -> {
result.add(x);
if (result.size() >= top) {
return;
}
});
return result;
}
Messge是对message信息的封装
static class Message implements Comparable<Message> {
private String message;
private volatile int count = 0;
public void setMessage(String message) {
this.message = message;
}
private void incount() {
count++;
}
public String getMessage() {
return message;
}
public int getCount() {
return count;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Message) {
return this.compareTo((Message) obj) == 0;
}
return super.equals(obj);
}
@Override
public int compareTo(Message o) {
if (o == null || o.getMessage() == null) {
return -1;
}
if (o.getCount() - this.getCount() == 0) {
return 0;
}
return (o.getCount() - this.getCount());
}
}
结尾
以上是个人关于top类需求的解决方案,如果你有更好的方案可以一块讨论。