一、离线计算与实时计算

离线计算: 批量获取数据、批量传输数据;周期性计算数据,展示数据;

代表技术: sqoop批量导入,HDFS批量存储,mapreduce批量计算,Hive批量计算数据…

就业方向: hivesql, Hadoop集群运维

实时计算: 数据实时产生,数据实时传输,数据实时计算,实时展示;

代表技术: Flume实时获取数据,kafka/metaq 实时数据存储,storm/Jstrom实时计算数据,redis实时结果缓存,mysql持久化存储…

流式计算一般架构图

大数据之storm_storm

 flume实时采集数据。

 Kafka消息队列,用来临时保存数据。

 Strom实时计算数据。

 Redis实时存储数据。

流式计算就是将源源不断产生的数据实时收集并实时计算,尽可能快的得到计算结果。

二、Strom介绍

Storm用来实时计算数据框架,特点:低延迟、高可用、分布式、可扩展、数据不丢失。提供简单容易理解的接口,便于开发。

Storm与Hadoop区别

 Storm用于实时计算,Storm处理的数据保存在内存中,数据源源不断,通过网络传输进来;

 Hadoop用于离线计算,处理的一批一批数据保存在文件系统中,数据保存在磁盘中。

三、Strom编程模型

大数据之storm_storm_02

Nimbus : 任务分配,对任务监督;

Zookeeper : 保存任务分配的信息、心跳信息、元数据信息。

Supervisor : 接受任务,并启动worker。worker的数量根据端口号来的;

Worker : 执行任务的具体组件(其实就是一个JVM),可以执行Spout或者bolt两种类型的任务; 一个worker就是一个端口号;

Task : Task=线程=executor, 一个Task属于一个Spout或者Bolt并发任务;

并发度:

用户指定的一个任务,可以被多个线程执行,对应到storm中就是一个task,并发度的数量等于线程的数量。一个任务的多个线程,会被运行在多个Worker(JVM)上,有一种类似于平均算法的负载均衡策略。尽可能减少网络IO;

大数据之storm_kafka_03

一个Strom程序可以获取多个数据源,每个topology的数据都是自己独有的,和其他的topology没有关系。

spout : 获取外部数据源;

Bolt : 业务逻辑处理节点,可以有多个;

Tuple : 消息发送的最小单元,是一个Tuple对象。

四、Strom集群部署

搭建前,需要安装好zookeeper集群

1、准备工作

配置hosts

vi  /etc/hosts

192.168.239.128 storm01 zk01 hadoop01
192.168.239.129 storm02 zk02 hadoop02
192.168.239.130 storm03 zk03 hadoop03

关闭防火墙

chkconfig iptables off  && setenforce 0

创建用户

groupadd realtime && useradd realtime && usermod -a -G realtime realtime

创建工作目录并赋权

mkdir /export
mkdir /export/servers
chmod 755 -R /export

切换到realtime用户下

su realtime

2、解压压缩包

3、修改配置文件

4、分发压缩包

5、启动集群

6、查看集群

访问storm01:8080,即可看到storm的ui界面。

四、Storm操作命令

1、提交任务

storm jar 【jar路径】 【拓扑包名.拓扑类名】 【拓扑名称】

storm jar examples/storm-starter/storm-starter-topologies-0.9.6.jar storm.starter.WordCountTopology wordcount

注意:因为从外部获取数据、结果保存到redis,所以不需要指定输入、输出

路径,这是和hadoop区别

2、杀死任务

storm kill 【拓扑名称】 -w 10

storm kill topology-name -w 10

3、停运任务

storm deactivte 【拓扑名称】

storm deactivte topology-name

五、问题解答

1、worker与topology

一个worker只属于一个topology,一个topology包含多个worker,其实就是这个topology运行在多个worker上。

一个topology要求的worder数量如何不被满足,集群在分配任务时,根据现有的worker先运行的topology。如果当前集群中worder数量为0,那么最新提交的topology将只会标示为active, 不会运行。只有集群有了空闲资源才会被运行。

2、StreamGrouping:(分组策略)

shuffleGrouping(随机分组)、FieldGrouping(按字段分组)、不分组等

3、运行模式

1、集群模式:在集群上运行

2、本地模式:在当前idea上运行,测试代码功能时,可以选择该模式

4、如何指定驱动类中每个组件的并发度数量

1、根据上游的数据量来设置spout并发度

2、根据业务复杂度和execute方法执行时间设置bolt并发度

3、根据集群可用资源配置,一般情况下70%资源利用率

5、如何设置worker的数量

worker的数量根据程序并发度task数量来均分,实际业务场景中,并反复调整

六、worldcount代码编写

1、WordCountTopologMain

package cn.itcast.storm;

import backtype.storm.Config;
import backtype.storm.LocalCluster;
import backtype.storm.StormSubmitter;
import backtype.storm.generated.AlreadyAliveException;
import backtype.storm.generated.InvalidTopologyException;
import backtype.storm.topology.TopologyBuilder;
import backtype.storm.tuple.Fields;

/**
* Created by maoxiangyi on 2016/4/27.
*/
public class WordCountTopologMain {
public static void main(String[] args) throws AlreadyAliveException, InvalidTopologyException {

//1、准备一个TopologyBuilder
TopologyBuilder topologyBuilder = new TopologyBuilder();
topologyBuilder.setSpout("mySpout",new MySpout(),2);
topologyBuilder.setBolt("mybolt1",new MySplitBolt(),2).shuffleGrouping("mySpout");
topologyBuilder.setBolt("mybolt2",new MyCountBolt(),4).fieldsGrouping("mybolt1", new Fields("word"));
//topologyBuilder.setBolt("mybolt2",new MyCountBolt(),4).shuffleGrouping("mybolt1");
// config.setNumWorkers(2);

/**
* i
* am
* lilei
* love
* hanmeimei
*/

//2、创建一个configuration,用来指定当前topology 需要的worker的数量
Config config = new Config();
config.setNumWorkers(2);

//3、提交任务 -----两种模式 本地模式和集群模式
//StormSubmitter.submitTopology("mywordcount",config,topologyBuilder.createTopology());
LocalCluster localCluster = new LocalCluster();
localCluster.submitTopology("mywordcount",config,topologyBuilder.createTopology());
}
}

2、MySpout

package cn.itcast.storm;

import backtype.storm.spout.SpoutOutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.IRichSpout;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseRichSpout;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Values;

import java.util.Map;

/**
* Created by maoxiangyi on 2016/4/27.
*/
public class MySpout extends BaseRichSpout {
SpoutOutputCollector collector;

//初始化方法
public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
this.collector = collector;
}

//storm 框架在 while(true) 调用nextTuple方法
public void nextTuple() {
collector.emit(new Values("i am lilei love hanmeimei"));
}

public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("love"));
}
}

3、MySplitBolt

package cn.itcast.storm;
import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.IBasicBolt;
import backtype.storm.topology.IRichBolt;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseRichBolt;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;

import java.util.Map;

/**
* Created by maoxiangyi on 2016/4/27.
*/
public class MySplitBolt extends BaseRichBolt {
OutputCollector collector;
//初始化方法
public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
this.collector = collector;
}

// 被storm框架 while(true) 循环调用 传入参数tuple
public void execute(Tuple input) {
String line = input.getString(0);
String[] arrWords = line.split(" ");
for (String word:arrWords){
collector.emit(new Values(word,1));
}
}

public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("word","num"));
}
}

4、MyCountBolt

package cn.itcast.storm;

import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.IBasicBolt;
import backtype.storm.topology.IRichBolt;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseRichBolt;
import backtype.storm.tuple.Tuple;

import java.util.HashMap;
import java.util.Map;

/**
* Created by maoxiangyi on 2016/4/27.
*/
public class MyCountBolt extends BaseRichBolt {
OutputCollector collector;
Map<String, Integer> map = new HashMap<String, Integer>();

public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
this.collector = collector;
}
public void execute(Tuple input) {
String word = input.getString(0);
Integer num = input.getInteger(1);
System.out.println(Thread.currentThread().getId() + " word:"+word);
if (map.containsKey(word)){
Integer count = map.get(word);
map.put(word,count + num);
}else {
map.put(word,num);
}
// System.out.println("count:"+map);
}

public void declareOutputFields(OutputFieldsDeclarer declarer) {
//不輸出
}
}