Top热门类需求实现思路

598 阅读2分钟

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类需求的解决方案,如果你有更好的方案可以一块讨论。