【问题标题】:How to use apache storm tuple如何使用apache风暴元组
【发布时间】:2015-11-10 06:59:22
【问题描述】:

我刚开始使用 Apache Storm。我阅读了教程并查看了examples 我的问题是所有示例都使用非常简单的元组(通常使用字符串归档)。元组是内联创建的(使用 new Values(...))。就我而言,我有许多字段的元组(5..100)。所以我的问题是如何为每个字段实现具有名称和类型(所有原始)的这样的元组?

有例子吗? (我认为直接实现“元组”不是一个好主意)

谢谢

【问题讨论】:

    标签: java tuples apache-storm


    【解决方案1】:

    创建将所有字段作为值的元组的另一种方法是创建一个 bean 并将其传递到元组中。

    给定以下类:

    public class DataBean implements Serializable {
        private static final long serialVersionUID = 1L;
    
        // add more properties as necessary
        int id;
        String word;
    
        public DataBean(int id, String word) {
            setId(id);
            setWord(word);
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getWord() {
            return word;
        }
        public void setWord(String word) {
            this.word = word;
        }
    }
    

    一键创建并发出 DataBean:

    collector.emit(new Values(bean));
    

    获取目标bolt中的DataBean:

    @Override
    public void execute(Tuple tuple, BasicOutputCollector collector) {
        try {
            DataBean bean = (DataBean)tuple.getValue(0);
            // do your bolt processing with the bean
        } catch (Exception e) {
            LOG.error("WordCountBolt error", e);
            collector.reportError(e);
        }       
    }
    

    在设置拓扑时不要忘记让 bean 可序列化并注册:

    Config stormConfig = new Config();
    stormConfig.registerSerialization(DataBean.class);
    // more stuff
    StormSubmitter.submitTopology("MyTopologyName", stormConfig, builder.createTopology());
    

    免责声明:Bean 可以很好地用于随机分组。如果你需要做一个fieldsGrouping,你仍然应该使用一个原语。例如,在 Word Count 场景中,您需要逐个单词进行分组,这样您可能会发出:

    collector.emit(new Values(word, bean));
    

    【讨论】:

    • 这似乎是一个不错的选择,但我想知道如何或何时使用元组(它是storm的关键概念之一)?!
    • 您仍在使用元组.. 只是传递一个对象而不是原语。如果您每次都要传递相同的 100 个字段,那么我会使用上面显示的 bean。如果您每次发送的字段变化很大,那么它可能就没有那么有用了。
    • 这对我来说非常清楚,但我很好奇如何使用“真实”元组(具有多个字段的东西)。但似乎没有人使用它...
    • 好像有什么误会。您永远不会直接创建 Tuple 对象。 Value 对象在内部被转换为元组类型。因此,如果输出 Value 有多个字段,则元组也有多个字段。此外,Storm 不支持类型检查。最后但最不重要的是,bean 想法的缺点是您无法使用字段分组连接模式访问各个属性。
    • @MatthiasJ.Sax 关于 fieldsGrouping 的观点非常好,我在底部添加了免责声明。
    【解决方案2】:

    我将按如下方式实现自定义元组/值类型:不使用成员变量来存储数据,而是将每个属性映射到继承Values 类型的对象列表中的固定索引。这种方法避免了常规 Bean 的“字段分组”问题。

    1. 不需要为字段分组添加其他属性(这很不自然)
    2. 避免数据重复(减少传送字节数)
    3. 它保留了 bean 模式的优势

    字数统计示例如下:

    public class WordCountTuple extends Values {
        private final static long serialVersionUID = -4386109322233754497L;
    
        // attribute indexes
        /** The index of the word attribute. */
        public final static int WRD_IDX = 0;
        /** The index of the count attribute. */
        public final static int CNT_IDX = 1;
    
        // attribute names
        /** The name of the word attribute. */
        public final static String WRD_ATT = "word";
        /** The name of the count attribute. */
        public final static String CNT_ATT = "count";
    
        // required for serialization
        public WordCountTuple() {}
    
        public WordCountTuple(String word, int count) {
            super.add(WRD_IDX, word);
            super.add(CNT_IDX, count);
        }
    
        public String getWord() {
            return (String)super.get(WRD_IDX);
        }
    
        public void setWort(String word) {
            super.set(WRD_IDX, word);
        }
    
        public int getCount() {
            return (Integer)super.get(CNT_IDX);
        }
    
        public void setCount(int count) {
            super.set(CNT_IDX, count);
        }
    
        public static Fields getSchema() {
            return new Fields(WRD_ATT, CNT_ATT);
        }
    }
    

    为避免不一致,“word”和“count”属性使用final static 变量。此外,方法getSchema()返回实现的模式,用于在Spout/Bolt方法.declareOutputFields(...)中声明输出流

    对于输出元组,这种类型可以直接使用:

    public MyOutBolt implements IRichBolt {
    
        @Override
        public void execute(Tuple tuple) {
            // some more processing
            String word = ...
            int cnt = ...
            collector.emit(new WordCountTuple(word, cnt));
        }
    
        @Override
        public void declareOutputFields(OutputFieldsDeclarer declarer) {
            declarer.declare(WordCountTuple.getSchema());
        }
    
        // other methods omitted
    }
    

    对于输入元组,我建议使用以下模式:

    public MyInBolt implements IRichBolt {
        // use a single instance for avoid GC trashing
        private final WordCountTuple input = new WordCountTuple();
    
        @Override
        public void execute(Tuple tuple) {
            this.input.clear();
            this.input.addAll(tuple.getValues());
    
            String word = input.getWord();
            int count = input.getCount();
    
            // do further processing
        }
    
        // other methods omitted
    }
    

    MyOutBoltMyInBolt可以如下连接:

    TopologyBuilder b = ...
    b.setBolt("out", new MyOutBolt());
    b.setBolt("in", new MyInBolt()).fieldsGrouping("out", WordCountTuple.WRD_ATT);
    

    使用字段分组很简单,因为WordCountTuple 允许单独访问每个属性。

    【讨论】:

    • 感谢您,目前我使用“已接受”的方法,但我使用 EnumMap 而不是字段。 Enum 指定字段和字段类型。目前我正在努力创建一个抽象元组,但 Enum 不能是抽象的,所以我必须使用普通 Map 或者我必须忍受一些冗余 - 如果我有解决方案,我会在这里发布。
    • +1 我非常喜欢这个。不过有问题......因为OP的问题是关于管理很多字段,所以通过构造函数传递所有字段实际上并不可行。加上将对象添加到 Values 数组的顺序很重要,所以也许你的无参数构造函数应该用 null 初始化所有内容?
    • 通过构造函数添加所有字段我猜是个人喜好问题。我猜null 初始化将是一个有效的改进。但是,对于 setter 而言,这无关紧要,因为它们使用索引访问。当然,必须小心使用常规的 ArrayList 方法(Values 继承自该方法)。否则,你可能会搞砸一切!
    • 有趣且有见地的答案。知道如何为自定义对象列表实现此功能吗?
    • “自定义对象列表”是什么意思?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-07
    • 1970-01-01
    • 1970-01-01
    • 2016-01-08
    相关资源
    最近更新 更多