Storm编程模型


在学这个之后,自我感觉的一个基调就是舒服,学习Storm是一个非常简单流畅的过程。很顺畅的将复杂的流式计算拆分成可见的编程模型。

Storm简介

Storm是Apache的顶级项目,最牛逼的流式计算框架之一,底层是用clojure语言实现。目前最新的稳定版本是1.2.2。也有最新的2.0版本还在SNAPSHOT中。
流式计算主要有两种方式,最直观的处理方式是原生流处理。就是像读文件一样,一行一行的读取数据。然后将数据一个一个的处理。不用说,这是最好理解的处理方式。在大数据背景下,要实现这种细粒度的流式计算,基本就Storm可选了。 还有个Flink框架也能这样细粒度处理,但是没玩过。
另一种是微批处理。将流式数据切分成连续的小批量,然后对小批量数据进行计算。最典型的就是SparkStreaming了。而Storm的Trident也有这样类似的处理方式。后面会介绍。
另外,阿里开源了一个JStorm,是用JAVA语言重新实现了Storm,并赋予了一些新的改进。例如减少Zookeeper访问,NimbusHA,RPC采用Netty等。但是这两框架的编程模型是一样的,也就是说我们自己编写的应用代码基本可以通用。

Storm安装

在大数据背景下的诸多组件中,Storm的安装比较简单。也是三大步骤,下载解压–修改配置–启动集群。 只需要注意下,Storm依赖Zookeeper进行调度。

Storm安装模型
其中Nimbus类似于主节点,负责任务调度。发布任务到Zookeeper。Supervisor节点则通过Zookeeper读取任务信息,发布给worker进行实际任务处理。
理解下这个后,就可以下载Storm改配置文件了。配置文件在Storm安装包的conf目录下,一般就只需要改一个配置文件,Storm.yaml。其中比较重要的配置属性如下:

属性含义
storm.zookeeper.portZookeeper集群地址
storm.local.dir本地缓存地址
nimbus.seedsnimbus节点
supervisor.slots.portsworker插槽,配置每个supervisor节点上运行多少个woker以及每个worker对应哪个端口
storm.health.check.dir安全检查目录
storm.health.check.timeout.ms安全检查超时时间
ui.portui端口

我觉得最重要的就是这个ui.port端口。默认是8080,最繁忙的接口。另外还有8000,3772 ,3774几个端口,如果有端口冲突再调整。
全量的配置项及默认值可以在这里查到。https://github.com/apache/storm/blob/v1.2.2/conf/defaults.yaml

配置完成后就可以启动storm了。 注意需要先启动ZK,并且指令会占用当前命令窗口,使用&后台运行。
在Nimbus节点启动Nimbus服务: bin/storm nimbus >/dev/null 2>&1 &
启动UI:storm ui &
启动supervisor: storm supervisor &
这样就启动完成了
访问UI界面,http://host:[ui.port] 。界面中提供了很详细的监控。Cluster summary部分展现了集群的整体情况,可以看看集群的几个关键数据跟配置文件是如何关联的。
StormUI
Storm安装比较简单,安装完成后,就可以开始开发自己的业务逻辑了。

Storm编程模型

Storm有两个不同的编程模型,原生的编程模型就是典型的原生流式处理,对数据进行细粒度的处理。而Storm还有另外一种升级版的编程模型,Trident,支持更强大的数据处理。
先来看看原生模型是什么样子。
Storm编程
几个重要的概念如下:

  • Topology可以理解为一个完整的Storm程序。
  • Tuple是数据元组,是Storm传送数据的最小单位。
  • Spout是水龙头,负责生产Tuple。tuple可以从其他的数据源中产生。
  • Bolt是水滴,负责接收tuple进行处理。
  • 而Spout与Bolt之间,通过group的方式描述拓扑关系。

Storm的概念怎么理解都无所谓,一看代码就豁然开朗。Storm安装包下有官方大量的示例,是最好的学习资料。在Storm安装目录下有个examples文件夹,storm-starter 目录下的org.apache.storm.starterWordCountTopology这个示例就是最好的示例。查看Main方法就能很容易的理解原生的编程模型:

public static void main(String[] args) throws Exception {
	//构建拓扑
    TopologyBuilder builder = new TopologyBuilder();
	//指定spout。RandomSentenceSpout为示例中的demo,会不断的发射随机字符串。5为并发度,可以理解为执行的线程数。
    builder.setSpout("spout", new RandomSentenceSpout(), 5);
	//指定名为split的bolt,跟在spout下,分组方式为随机分组。
    builder.setBolt("split", new SplitSentence(), 8).shuffleGrouping("spout");
    //指定名为count的bolt,跟在split的bolt下,分组方式为根据word字段分组。这样就完成了整个拓扑结构的描述
    builder.setBolt("count", new WordCount(), 12).fieldsGrouping("split", new Fields("word"));
	//配置信息。可以覆盖集群中的部分配置。
    Config conf = new Config();
    conf.setDebug(true);
    if (args != null && args.length > 0) {
      conf.setNumWorkers(3); //设置启动3个worker节点处理。每个worker会占用supervisor上的一个slot插槽。
      //集群方式提交。需要打成jar包,用storm指令提交到集群运行。跟spark、mapreduce提交集群一个概念
      StormSubmitter.submitTopologyWithProgressBar(args[0], conf, builder.createTopology());
    }else {
      conf.setMaxTaskParallelism(3);
	 //本地提交。Storm本地提交不需要外部产品,zk,Storm都不需要,就可以直接运行。但是要注意pom中depency的scope不能是provided。提交集群时一般会指定为provided,减少提交的jar包数量。
      LocalCluster cluster = new LocalCluster();
      cluster.submitTopology("word-count", conf, builder.createTopology());
      Thread.sleep(10000);
	  //本地提交要注意手动关闭
      cluster.shutdown();
    }
  }

理解这个大致流程后,其他的代码跟踪一下就很能够理解了。

有兴趣的可以自己搭建一个maven工程把代码移植过来跑一下。全是复制,很简单。只需要注意一点,原始的日志会比较复杂,当你需要调整日志内容时,需要在根路径下增加一个log4j2.xml。配置里面的logback日志内容。这个配置会覆盖storm-core包下的log4j2.xml文件,过滤自己不关注的日志。

这个例子就完成了最基础的wordcount逻辑。可以看到,整体流程是由RandomSentenceSpout不断发射tuple作为元组数据,split bolt将tuple拆分成一个一个的单词,最后由count bolt来进行统计。
可以看到编写一个Storm的业务逻辑相当简单,核心就是围绕一个StormTopology对象,描述清楚拓扑结构。
而这种原生的模型可以看到,拓扑结构还是非常耦合的,而且数据处理方式比较单一。为了更加丰富Storm的数据处理能力,Storm实现了另外一种编程模型:Trident
其核心是将Topology实现类改为TridentTopology,核心同样是围绕如何描述TridentTopology的拓扑结构。
关于Trident,实际上是Storm封装了更多的数据处理方式,例如分组、统计、tuple重组等。这样,更加丰富了Storm的数据处理能力。
具体示例可以参看storm-kafka-client-examples目录下的org.apache.storm.kafka.trident.TridentKafkaClientWordCountNamedTopics示例。该示例实现逻辑是消费Kafka的消息,然后将所有的消息整理成(word,count)结构的中间结果集:TridentState。最后通过DRPC服务查询该结果集,完成指定word的count统计。后续再补充详细的说明。

编写可靠的Storm拓扑

在原生编程模型中,spout会不断发射数据,如果没有发射,则会休眠1ms。
而storm会保证每个tuple都会被处理,但是如何保证每个tuple只会被消费一次呢?例如在对接Kafka时,worker需要提交offset到zookeeper,这样才能保证下一次不会再次消费同样的消息?
其实,在Trident编程模型中,Storm完成的细节更丰富,基本不会需要考虑这样的问题。而在原生的编程模型中,则需要分两步来提高Storm运行的可靠性。

  1. 针对Spout, 要保证每个消息都会被发送一次.spout会调用nextTuple方法不断发送消息,这时,如果消息发送失败,可以实现ISpout接口的fail方法,在失败后实现补偿发射逻辑。 只是补偿发射时,只能跟据MessageId来进行对相应,而Storm的fail方法中,并没有保存MessageId和Tuple的对应关系,需要自己维护下这个对应关系。
  2. 针对Bolt, 有两种方式:1、继承BaseBasicBolt。Storm内部会自动完成ack和fail。但是这个类功能比较简单,应用场景也会比较单一。2、继承BaseRichBolt。这个父类的方法比较多,应用场景比较丰富。但是,需要手动完成ack通知。具体在executor方法中,调用collector,.ack()方法手动通知。这样就能避免消息被重复消费。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

roykingw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值