# 078. 在 storm 拓扑中加入热点缓存实时自动识别和感知的代码逻辑

思路:

  1. 对商品访问次数 LRUMap 中的所有数据进行排序
  2. 计算后 95% 商品平均访值
  3. 对前 5% 的商品进行热点阈值评估,大于 n 倍的视为热点商品,存储在热点商品列表中

下面用代码来实现这个逻辑

/**
 * 热点商品感知
 */
private static class HotProductFindThread extends Thread {
    private Logger logger = LoggerFactory.getLogger(getClass());
    private LRUMap<Long, Long> countMap;

    public HotProductFindThread(LRUMap<Long, Long> countMap) {
        this.countMap = countMap;
    }

    @Override
    public void run() {
        List<Map.Entry<Long, Long>> countList = new ArrayList<>();
        List<Long> hotPidList = new ArrayList<>();

        while (true) {
            Utils.sleep(5000);
            if (countMap.size() < 2) {
              // 至少有 2 个商品
              continue;
            }
            // 1. 全局排序
            countList.clear();
            hotPidList.clear();
            for (Map.Entry<Long, Long> entry : countMap.entrySet()) {
                countList.add(entry);
            }
            Collections.sort(countList, new Comparator<Map.Entry<Long, Long>>() {
                @Override
                public int compare(Map.Entry<Long, Long> o1, Map.Entry<Long, Long> o2) {
                    // 取反结果,是降序排列
                    return ~Long.compare(o1.getValue(), o2.getValue());
                }
            });

            // 2.计算后 95% 商品平均访值
            int avg95Count = (int) (countList.size() * 0.95);
            int avg95Total = 0;
            // 从列表尾部开始循环 avg95Count 次
            for (int i = countList.size() - 1; i >= countList.size() - avg95Count; i--) {
                avg95Total += countList.get(i).getValue();
            }
            // 后百分之 95 商品的平均访问值
            int avg95Avg = avg95Total / avg95Count;
            int threshold = 5; // 阈值

            // 3. 计算热点商品
            for (int i = 0; i < avg95Count; i++) {
                Map.Entry<Long, Long> entry = countList.get(i);
                if (entry.getValue() > avg95Avg * threshold) {
                    logger.info("热点商品:" + entry);
                    hotPidList.add(entry.getKey());
                }
            }
            logger.info("热点商品列表:" + hotPidList);
        }
    }

    public static void main(String[] args) {
        LRUMap<Long, Long> countMap = new LRUMap<>(5);
        countMap.put(1L, 2L);
        countMap.put(2L, 1L);
        countMap.put(3L, 3L);
        countMap.put(4L, 30L);
        // 最后打印 4L 是热点商品,这个结果是对的
        new HotProductFindThread(countMap).run();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

吸取上一章节的教训,这次写完一个小模块,就测试下逻辑是否正常。