Storm常见模式2——TOP N介绍

转载 2015年11月18日 19:49:35
导读问题:
1.TOP N计算的应用场景有哪些?
2.TOP N的实现方法和原理是什么?
Storm的另一种常见模式是对流式数据进行所谓“streaming top N”的计算,它的特点是持续的在内存中按照某个统计指标(如出现次数)计算TOP N,然后每隔一定时间间隔输出实时计算后的TOP N结果。
流式数据的TOP N计算的应用场景很多,例如计算twitter上最近一段时间内的热门话题、热门点击图片等等。
下面结合Storm-Starter中的例子,介绍一种可以很容易进行扩展的实现方法:首先,在多台机器上并行的运行多个Bolt,每个Bolt负责一部分数据的TOP N计算,然后再有一个全局的Bolt来合并这些机器上计算出来的TOP N结果,合并后得到最终全局的TOP N结果。
该部分示例代码的入口是RollingTopWords类,用于计算文档中出现次数最多的N个单词。首先看一下这个Topology结构:
 
Topology构建的代码如下:
  1. TopologyBuilder builder = new TopologyBuilder();
  2.         builder.setSpout("word", new TestWordSpout(), 5);
  3.         builder.setBolt("count", new RollingCountObjects(60, 10), 4)
  4.                  .fieldsGrouping("word", new Fields("word"));
  5.         builder.setBolt("rank", new RankObjects(TOP_N), 4)
  6.                  .fieldsGrouping("count", new Fields("obj"));
  7.         builder.setBolt("merge", new MergeObjects(TOP_N))
  8.                  .globalGrouping("rank");
复制代码

(1)首先,TestWordSpout()是Topology的数据源Spout,持续随机生成单词发出去,产生数据流“word”,输出Fields是“word”,核心代码如下:
  1. public void nextTuple() {
  2.         Utils.sleep(100);
  3.         final String[] words = new String[] {"nathan", "mike", "jackson", "golda", "bertels"};
  4.         final Random rand = new Random();
  5.         final String word = words[rand.nextInt(words.length)];
  6.         _collector.emit(new Values(word));
  7.   }
  8.     public void declareOutputFields(OutputFieldsDeclarer declarer) {
  9.         declarer.declare(new Fields("word"));
  10.   }
复制代码

(2)接下来,“word”流入RollingCountObjects这个Bolt中进行word count计算,为了保证同一个word的数据被发送到同一个Bolt中进行处理,按照“word”字段进行field grouping;在RollingCountObjects中会计算各个word的出现次数,然后产生“count”流,输出“obj”和“count”两个Field,核心代码如下:
  1. public void execute(Tuple tuple) {

  2.         Object obj = tuple.getValue(0);
  3.         int bucket = currentBucket(_numBuckets);
  4.         synchronized(_objectCounts) {
  5.             long[] curr = _objectCounts.get(obj);
  6.             if(curr==null) {
  7.                 curr = new long[_numBuckets];
  8.                 _objectCounts.put(obj, curr);
  9.             }
  10.             curr[bucket]++;
  11.             _collector.emit(new Values(obj, totalObjects(obj)));
  12.             _collector.ack(tuple);
  13.         }
  14.     }
  15.     public void declareOutputFields(OutputFieldsDeclarer declarer) {
  16.         declarer.declare(new Fields("obj", "count"));
  17.     }
复制代码

(3)然后,RankObjects这个Bolt按照“count”流的“obj”字段进行field grouping;在Bolt内维护TOP N个有序的单词,如果超过TOP N个单词,则将排在最后的单词踢掉,同时每个一定时间(2秒)产生“rank”流,输出“list”字段,输出TOP N计算结果到下一级数据流“merge”流,核心代码如下:
  1. public void execute(Tuple tuple, BasicOutputCollector collector) {
  2.         Object tag = tuple.getValue(0);
  3.         Integer existingIndex = _find(tag);
  4.         if (null != existingIndex) {
  5.             _rankings.set(existingIndex, tuple.getValues());
  6.         } else {
  7.             _rankings.add(tuple.getValues());
  8.         }
  9.         Collections.sort(_rankings, new Comparator<List>() {
  10.             public int compare(List o1, List o2) {
  11.                 return _compare(o1, o2);
  12.             }
  13.         });
  14.         if (_rankings.size() > _count) {
  15.             _rankings.remove(_count);
  16.         }
  17.         long currentTime = System.currentTimeMillis();
  18.         if(_lastTime==null || currentTime >= _lastTime + 2000) {
  19.             collector.emit(new Values(new ArrayList(_rankings)));
  20.             _lastTime = currentTime;
  21.         }
  22.     }

  23.     public void declareOutputFields(OutputFieldsDeclarer declarer) {
  24.         declarer.declare(new Fields("list"));
  25.     }
复制代码

(4)最后,MergeObjects这个Bolt按照“rank”流的进行全局的grouping,即所有上一级Bolt产生的“rank”流都流到这个“merge”流进行;MergeObjects的计算逻辑和RankObjects类似,只是将各个RankObjects的Bolt合并后计算得到最终全局的TOP N结果,核心代码如下:
  1. public void execute(Tuple tuple, BasicOutputCollector collector) {
  2.         List<List> merging = (List) tuple.getValue(0);
  3.         for(List pair : merging) {
  4.             Integer existingIndex = _find(pair.get(0));
  5.             if (null != existingIndex) {
  6.                 _rankings.set(existingIndex, pair);
  7.             } else {
  8.                 _rankings.add(pair);
  9.             }

  10.             Collections.sort(_rankings, new Comparator<List>() {
  11.                 public int compare(List o1, List o2) {
  12.                     return _compare(o1, o2);
  13.                 }
  14.             });

  15.             if (_rankings.size() > _count) {
  16.                 _rankings.subList(_count, _rankings.size()).clear();
  17.             }
  18.         }

  19.         long currentTime = System.currentTimeMillis();
  20.         if(_lastTime==null || currentTime >= _lastTime + 2000) {
  21.             collector.emit(new Values(new ArrayList(_rankings)));
  22.             LOG.info("Rankings: " + _rankings);
  23.             _lastTime = currentTime;
  24.         }
  25.     }

  26.     public void declareOutputFields(OutputFieldsDeclarer declarer) {
  27.         declarer.declare(new Fields("list"));
  28.     }
复制代码

关于上述例子的几点说明:

(1) 为什么要有RankObjects和MergeObjects两级的Bolt来计算呢?

其实,计算TOP N的一个最简单的思路是直接使用一个Bolt(通过类似于RankObjects的类实现)来做全局的求TOP N操作。

但是,这种方式的明显缺点在于受限于单台机器的处理能力。

(2) 如何保证计算结果的正确性?

首先通过field grouping将同一个word的计算放到同一个Bolt上处理;最后有一个全局的global grouping汇总得到TOP N。

这样可以做到最大可能并行性,同时也能保证计算结果的正确。

(3) 如果当前计算资源无法满足计算TOP N,该怎么办?

这个问题本质上就是系统的可扩展性问题,基本的解决方法就是尽可能做到在多个机器上的并行计算过程,针对上面的Topology结构:

a) 可以通过增加每一级处理单元Bolt的数量,减少每个Bolt处理的数据规模;

b) 可以通过增加一级或多级Bolt处理单元,减少最终汇总处理的数据规模。

大圆那些事

相关文章推荐

Storm常见模式——求TOP N

Storm的另一种常见模式是对流式数据进行所谓“streaming top N”的计算,它的特点是持续的在内存中按照某个统计指标(如出现次数)计算TOP N,然后每隔一定时间间隔输出实时计算后的TOP...
  • z_l_l_m
  • z_l_l_m
  • 2012年12月18日 17:45
  • 757

Storm常见模式——TimeCacheMap

Storm中使用一种叫做TimeCacheMap的数据结构,用于在内存中保存近期活跃的对象,它的实现非常地高效,而且可以自动删除过期不再活跃的对象。 TimeCacheMap使用多个桶buckets...
  • z_l_l_m
  • z_l_l_m
  • 2012年12月18日 17:42
  • 498

Storm常见模式——分布式RPC

本文翻译自:https://github.com/nathanmarz/storm/wiki/Distributed-RPC,作为学习Storm DRPC的资料,转载必须以超链接形式标明文章原始出处及...
  • z_l_l_m
  • z_l_l_m
  • 2012年12月18日 17:40
  • 592

Storm常见模式——批处理

Storm对流数据进行实时处理时,一种常见场景是批量一起处理一定数量的tuple元组,而不是每接收一个tuple就立刻处理一个tuple,这样可能是性能的考虑,或者是具体业务的需要。 例如,批量查询...
  • z_l_l_m
  • z_l_l_m
  • 2012年12月18日 17:44
  • 900

Storm常见模式——流聚合及 timecachemap的使用

转自:http://www.cnblogs.com/panfeng412/archive/2012/06/04/storm-common-patterns-of-stream-join.html ...

Storm常见模式——流聚合

流聚合(stream join)是指将具有共同元组(tuple)字段的数据流(两个或者多个)聚合形成一个新的数据流的过程。 从定义上看,流聚合和SQL中表的聚合(table join)很像,但是二者...

Top-N Recommendation——基于用户的推荐实验

无论是在实体商店还是在网络上,都会有Top-N推荐的情况。基于客户或者基于商品做出推荐。本实验基于Movielens、Ratings的电影数据集,对用户做出Top-N 推荐。主要目的是基于User-B...

Storm的常见模式---求TOPN

转载:http://my.oschina.net/infiniteSpace/blog/309784参考了storm-starter和很多网上的例子。 总共需要三个bolt://bolt1,负责实时...

带空值的排列 (Top/Bottom N、First/Last、NTile) ——分析函数3

一、带空值的排列: 1、测试环境 SQL> desc user_order; 名称 是否为空? 类型 ------...

豆瓣top250电影抓取——存放到数据库(2)

下面我们把数据存放到数据库中。 import json import urllib.request import urllib.parse import pymysql class DoubanM...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Storm常见模式2——TOP N介绍
举报原因:
原因补充:

(最多只允许输入30个字)