1.storm简介:
Storm是Twitter公司开源贡献给Apache的一款实时流式计算框架,作用是用于解决数据的实时计算,以及实时处理等问题。它与hadoop的不同就是能够做到实时处理数据的能力,这里有一个hadoop离线项目的经典架构模式:ftp(获取)----hdfs(存储)-------hive(操作计算)--------mysql(最终结果存储)但是这种离线的架构分析数据模式,在数据处理上时间过程很长,所以出现了实时数据分析架构模式,这里举例:kafka---storm----redis,这是一个经典的实时数据分析架构模式。离线是全部数据拿过来之后再计算,实时就是生产一条数据,处理计算一条数据,达到实时分析的目的。
Storm只负责数据的计算不负责数据的存储,2011年storm横空出世,2013年前后,阿里巴巴基于storm框架,使用java语言开发了类似的流式计算框架佳作,Jstorm。2016年年底阿里巴巴将源码贡献给了Apache storm,两个项目开始合并,新的项目名字叫做storm2.x。阿里巴巴团队专注flink开发(目前市场上离线的项目数量要大于实时的项目,所以storm的应用程度与市场占比并不是很大。但是总体来讲,趋势是向实时发展的,所以storm未来可期)。
2.Storm的架构:
满足经典的主从架构模式,主从之间需要依赖zk(zookeeper),
架构元素:
2.1 nimbus:负责资源分配和任务的调度,新版本storm中可以有多个nimbus节点,用来完成高可用,做主备。
2.2 Supervisor:接受nimbus分配的任务,启动停止自己管理的worker进程。
2.3 Zookeeper:在storm中zk主要完成是nimbus与supervisor之间的信息交流,将nimbus分配给supervisor的任务写入到zk中。
2.4 Worker:是storm用来完成运行处理具体组件的逻辑进程,worker中的每一个spout/bolt的线程成为task。在storm0.8版本以后,task不在与物理线程对应,同一个task可能共享一个物理线程,该线程成为executor。新版本的jstorm已经废除了task的概念。
3. storm的编程模型
那么一个worker里面究竟是怎样的处理模式呢,worker主要由两个部分组成
3.1spout:接受外部数据的组件,将外部数据转换成storm内部的数据(在实际开发中主要是对接kafka),然后传递给下一个bolt。
3.2bolt:接受spout的数据,或者上一个bolt的数据,对数据进行过滤、计算、函数应用、存储等操作,发送给下一个bolt或者存储到某种介质上(mysql,redis,mongodb)。实际中用的比较多的是redis。
4. storm简单案列的编写步骤(搭建java的maven的工程,编写一个storm的简单计算案列模式,storm支持了本地测试模式与远程运行模式,其实我们主要做的就是编写spout、bolt与驱动类这三部分)
4.1导入依赖:
<dependency>
<groupId>org.apache.storm</groupId>
<artifactId>storm-core</artifactId>
<version>1.1.1</version>
</dependency>
4.2编写spout程序:读取外部的数据,将一行一行的数据发送给下游(上下游:storm是实时的计算框架,这个上下游叫法就是形容storm像水流一样,源源不断的实时状态,也形容了数据在storm里面传输的过程)的bolt,编写时要需要继承一个模板BaseRichSpout,继承之后会重写三个方法,这三个方法与作用分别是:
public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
//初始化方法,类似于这个类的构造器,只在加载的时候运行一次, //一般用来打开数据库连接,打开网络连接。
//conf:传入的是storm集群的配置文件和用户自定义配置文件,一般不 //用
//context:传入的是上下文对象,一般不用。
//collector:数据输出的收集器,spout类将数据发送给collector,由collector //发送给storm框架。
}
public void nextTuple() {
//字面意思是下一个tuple,tuple:一条在storm里面传输的数 //据,表示数据在storm里的基本单位。我们在执行storm程序 //的时候会循环执行这一个方法,发送一条一条的tuple,在底 //层有一个while循环来调用该方法,每调用一次,发送一条tuple
//数据出去,从而实现每接收一条数据发送一条数据。
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
//声明发出的数据是什么,
declarer.declare(new Fields("自定义数据的名称"));
}
4.3 编写bolt程序:可以写一个bolt程序,也可以写多个bolt程序,这取决于我们的需求,但是每一个bolt都是对传过来的数据进行处理,一层一层的接近我们想要的结果。编写时要继承一个模板BaseRichBolt,继承之后要重写三个方法,这个三个方法与其对应完成的功能分别是:
Public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
//初始化方法只会运行一次,
//stormConf:配置文件
//context:上下文对象,一般不用
//collector:数据收集器(如果此bolt是最后一个bolt,就不需要自 //定义collector,并赋值)
}
public void execute(Tuple input) {
//执行业务逻辑的方法
//input:获取上游(这里上游的数据可以来自spout也可以来自 //上一个bolt)的数据
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
// 向下游发送数据(如果此bolt是最后一个,那么这里不用实 //现)
}
4.4 编写驱动类,作用是用来驱动程序,一般的情况驱动类写法如下:
public class Topology {
public static void main(String[] args) throws InvalidTopologyException, AuthorizationException, AlreadyAliveException {
// 通过TopologyBuilder来封装任务信息
TopologyBuilder topologyBuilder = new TopologyBuilder();
//设置spout,获取数据
topologyBuilder.setSpout("我们编写的spout类名",new ReadFileSpout(),2);
//设置bolt,对数据进行操作,.shuffleGrouping("")接收上游的数据
topologyBuilder.setBolt("我们编写的boltt类名",newSplitBolt(),4).shuffleGrouping("我们编写的spout类名");
//准备配置项
Config config = new Config();
config.setDebug(false);
//提交job
//提交由两种方式:一种本地运行模式、一种集群运行模式。
if (args != null && args.length > 0) {
//运行集群模式
config.setNumWorkers(1);
StormSubmitter.submitTopology(args[0],config,topologyBuilder.createTopology());
} else {
LocalCluster localCluster = new LocalCluster();
localCluster.submitTopology("运行名称(自定义)", config, topologyBuilder.createTopology());
}
}