• Storm-6 Storm的并行度、Grouping策略以及消息可靠处理机制简介


    概念:

    Workers (JVMs): 在一个节点上可以运行一个或多个独立的JVM 进程。一个Topology可以包含一个或多个worker(并行的跑在不同的machine上), 所以worker process就是执行一个topology的子集, 并且worker只能对应于一个topology 

    Executors (threads): 在一个worker JVM进程中运行着多个Java线程。一个executor线程可以执行一个或多个tasks。但一般默认每个executor只执行一个task。一个worker可以包含一个或多个executor, 每个component (spout或bolt)至少对应于一个executor, 所以可以说executor执行一个compenent的子集, 同时一个executor只能对应于一个component。 

    Tasks(bolt/spout instances):Task就是具体的处理逻辑对象,每一个Spout和Bolt会被当作很多task在整个集群里面执行。每一个task对应到一个线程,而stream grouping则是定义怎么从一堆task发射tuple到另外一堆task。你可以调用TopologyBuilder.setSpout和TopBuilder.setBolt来设置并行度 — 也就是有多少个task。 

    配置并行度

    对于并发度的配置, 在storm里面可以在多个地方进行配置, 优先级为:defaults.yaml < storm.yaml < topology-specific configuration < internal component-specific configuration < external component-specific configuration 

    worker processes的数目, 可以通过配置文件和代码中配置, worker就是执行进程, 所以考虑并发的效果, 数目至少应该大亍machines的数目 

    executor的数目, component的并发线程数,只能在代码中配置(通过setBolt和setSpout的参数), 例如, setBolt("green-bolt", new GreenBolt(), 2) 

    tasks的数目, 可以不配置, 默认和executor1:1, 也可以通过setNumTasks()配置 

    Topology的worker数通过config设置,即执行该topology的worker(java)进程数。它可以通过 storm rebalance 命令任意调整。 
    1
    2
    3
    4
    5
    6
    Config conf = new Config();
    conf.setNumWorkers(2); // use two worker processes
    topologyBuilder.setSpout("blue-spout", new BlueSpout(), 2); // set parallelism hint to 2
    topologyBuilder.setBolt("green-bolt", new GreenBolt(), 2).setNumTasks(4).shuffleGrouping("blue-spout"); //set tasks number to 4
    topologyBuilder.setBolt("yellow-bolt", new YellowBolt(), 6).shuffleGrouping("green-bolt");
    StormSubmitter.submitTopology("mytopology", conf, topologyBuilder.createTopology());

    动态的改变并行度

    Storm支持在不 restart topology 的情况下, 动态的改变(增减) worker processes 的数目和 executors 的数目, 称为rebalancing. 通过Storm web UI,或者通过storm rebalance命令实现: 
    1
    storm rebalance mytopology -n 5 -e blue-spout=3 -e yellow-bolt=10

    流分组策略----Stream Grouping

    Stream Grouping,告诉topology如何在两个组件之间发送tuple 
    定义一个topology的其中一步是定义每个bolt接收什么样的流作为输入。stream grouping就是用来定义一个stream应该如果分配数据给bolts上面的多个tasks 

    Storm里面有7种类型的stream grouping,你也可以通过实现CustomStreamGrouping接口来实现自定义流分组 
    1. Shuffle Grouping 
    随机分组,随机派发stream里面的tuple,保证每个bolt task接收到的tuple数目大致相同。 

    2. Fields Grouping 
    按字段分组,比如,按"user-id"这个字段来分组,那么具有同样"user-id"的 tuple 会被分到相同的Bolt里的一个task, 而不同的"user-id"则可能会被分配到不同的task。 

    3. All Grouping 
    广播发送,对亍每一个tuple,所有的bolts都会收到 

    4. Global Grouping 
    全局分组,整个stream被分配到storm中的一个bolt的其中一个task。再具体一点就是分配给id值最低的那个task。 

    5. None Grouping 
    不分组,这个分组的意思是说stream不关心到底怎样分组。目前这种分组和Shuffle grouping是一样的效果, 有一点不同的是storm会把使用none grouping的这个bolt放到这个bolt的订阅者同一个线程里面去执行(如果可能的话)。 

    6. Direct Grouping 
    指向型分组, 这是一种比较特别的分组方法,用这种分组意味着消息(tuple)的发送者指定由消息接收者的哪个task处理这个消息。只有被声明为 Direct Stream 的消息流可以声明这种分组方法。而且这种消息tuple必须使用 emitDirect 方法来发射。消息处理者可以通过 TopologyContext 来获取处理它的消息的task的id (OutputCollector.emit方法也会返回task的id)  

    7. Local or shuffle grouping 
    本地或随机分组。如果目标bolt有一个或者多个task与源bolt的task在同一个工作进程中,tuple将会被随机发送给这些同进程中的tasks。否则,和普通的Shuffle Grouping行为一致。 
    ----------------------------------------------------------------------------------------

     在这里,将会提到storm的七种grouping策略,并且编码逐一实现。

          首先,需要一个集群(希望尽量模仿真实环境,故就不用本地模式了)。详细的安装方法大家可以查看本人的另外一篇博文:storm集群和zookeeper集群的部署过程。

          OK。现在有三个节点。一个作为nimbus,两个作为supervisor。到这里先介绍一下storm逻辑上有两个component,一个是Spout,另一个是Bolt。stream由Spout发出,在不同的Bolt之间进行处理,在其中传递的是storm的基本处理单位:Tuple。由Spout发出一个一个Tuple,然后Bolt接收Tuple进行各种各样的处理。这一整个过程构成一个DAG。在storm里面叫做Topology。当使用远程模式向集群提交一个Topology之后,如果不kill掉的话,将会一直运行到。。。我也不知道尽头。。貌似没有尽头。

          好了,来看一个简单的Topology。将使用这个Topology来实现那几种Grouping策略。

          

          上图中spout的处理逻辑是将一句话发出给下一个Bolt,然后下一个Bolt做句子的单词分割,下一个做计数,最后的Bolt做汇总显示。这里可以有多个Bolt或者Spout进行并行处理。这是关于并行度的设置。

          好了,所谓的grouping策略就是在Spout与Bolt、Bolt与Bolt之间传递Tuple的方式。总共有七种方式:

          1)shuffleGrouping(随机分组)

          2)fieldsGrouping(按照字段分组,在这里即是同一个单词只能发送给一个Bolt)

          3)allGrouping(广播发送,即每一个Tuple,每一个Bolt都会收到)

          4)globalGrouping(全局分组,将Tuple分配到task id值最低的task里面)

          5)noneGrouping(随机分派)

          6)directGrouping(直接分组,指定Tuple与Bolt的对应发送关系)

          7)Local or shuffle Grouping

          8)customGrouping (自定义的Grouping)

          OK,下面逐个来试试!

          首先是使用shuffleGrouping策略。

          

          启动后所得的结果。

          

         可以看到如storm这个单词被随机分配到了两个counter里面,分别是h4和h3两个节点里面。可再做一次提交,又会看到不一样的结果。将与下面的fieldGrouping形成对比。

         

         

         然后换成fieldsGrouping。

         

        启动后的结果如图。图中wordcount2和wordcount3就是我两次提交的topology的名字。

         

         

        下面是两次提交的结果。可以看出,使用fieldsGrouping策略,被分配到每个wordcounterbolt的单词没有变化。

         

         

        下面是第二次提交的结果。

         

         

         再换成noneGrouping策略。

         

        提交集群运行。

         

        运行结果如图。noneGrouping和shuffleGrouping是基本一样的。都是随机的。

        

        

        替换成allGrouping策略。

       

       提交集群运行。

       

       运行结果如图。可以看到。两个bolt所接收到的单词是一样的,都是全部的单词。

       

       

       最后,替换成globalGrouping策略。

       

       提交集群运行。

       

       从上图可以看到,主要分配到了h3这个节点。从下面的结果得以验证。

       

       

       好了,就先做到这里吧!

    ----------------------------------------------------------------------------------------

    消息的可靠处理机制

        在storm中,可靠的信息处理机制是从spout开始的。一个提供了可靠的处理机制的spout需要记录他发射出去的tuple,当下游bolt处理tuple或者子tuple失败时spout能够重新发射。 

        Storm通过调用Spout的nextTuple()发送一个tuple。为实现可靠的消息处理,首先要给每个发出的tuple带上唯一的ID,并且将ID作为参数传递给SoputOutputCollector的emit()方法:collector.emit(new Values("value1","value2"), msgId); 给tuple指定ID告诉Storm系统,无论处理成功还是失败,spout都要接收tuple树上所有节点返回的通知。如果处理成功,spout的ack()方法将会对编号是msgId的消息应答确认;如果处理失败或者超时,会调用fail()方法。 

        bolt要实现可靠的信息处理机制包含两个步骤:1.当发射衍生的tuple时,需要锚定读入的tuple;2.当处理消息成功或失败时分别确认应答或者报错。 

        锚定一个tuple的意思是,建立读入tuple和衍生出的tuple之间的对应关系,这样下游的bolt就可以通过应答确认、报错或超时来加入到tuple树结构中。可以通过调用OutputCollector的emit()的一个重载函数锚定一个或一组tuple:collector.emit(tuple, new Values(word)) 

        非锚定(collector.emit(new Values(word));)的tuple不会对数据流的可靠性起作用。如果一个非锚定的tuple在下游处理失败,原始的根tuple不会重新发送。 
      
        超时时间可以通过任务级参数Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS进行配置,默认超时值为30秒。 

         Storm 系统中有一组叫做"acker"的特殊的任务,它们负责跟踪DAG(有向无环图)中的每个消息。acker任务保存了spout消息id到一对值的映射。第一个值就是spout的任务id,通过这个id,acker就知道消息处理完成时该通知哪个spout任务。第二个值是一个64bit的数字,我们称之为"ack val", 它是树中所有消息的随机id的异或计算结果。ack val表示了整棵树的的状态,无论这棵树多大,只需要这个固定大小的数字就可以跟踪整棵树。当消息被创建和被应答的时候都会有相同的消息id发送过来做异或。 

        每当acker发现一棵树的ack val值为0的时候,它就知道这棵树已经被完全处理了。因为消息的随机ID是一个64bit的值,因此ack val在树处理完之前被置为0的概率非常小。假设你每秒钟发送一万个消息,从概率上说,至少需要50,000,000年才会有机会发生一次错误。即使如此,也只有在这个消息确实处理失败的情况下才会有数据的丢失! 

     有三种方法可以去掉消息的可靠性: 
    1、将参数Config.TOPOLOGY_ACKERS设置为0,通过此方法,当Spout发送一个消息的时候,它的ack方法将立刻被调用; 
    2、Spout发送一个消息时,不指定此消息的messageID。当需要关闭特定消息可靠性的时候,可以使用此方法; 
    3、最后,如果你不在意某个消息派生出来的子孙消息的可靠性,则此消息派生出来的子消息在发送时不要做锚定,即在emit方法中不指定输入消息。因为这些子孙消息没有被锚定在任何tuple tree中,因此他们的失败不会引起任何spout重新发送消息。 
  • 相关阅读:
    JDBC 删除数据两种方式,PreparedStatement表示预编译的 SQL 语句的对象,防止sql注入
    MySQL 主键外键
    JDBC 增删改查
    MySQL数据库语句
    反射
    如何安装、配置、登陆MySQL
    如何干净卸除MySQL数据库
    IO流总结
    字符流五种读写 字符流 BufferedWriter BufferedReader 带缓冲区的字符流
    单例模式
  • 原文地址:https://www.cnblogs.com/duanxz/p/4701752.html
Copyright © 2020-2023  润新知