一、离线计算与实时计算
离线计算: 批量获取数据、批量传输数据;周期性计算数据,展示数据;
代表技术: sqoop批量导入,HDFS批量存储,mapreduce批量计算,Hive批量计算数据…
就业方向: hivesql, Hadoop集群运维
实时计算: 数据实时产生,数据实时传输,数据实时计算,实时展示;
代表技术: Flume实时获取数据,kafka/metaq 实时数据存储,storm/Jstrom实时计算数据,redis实时结果缓存,mysql持久化存储…
流式计算一般架构图
flume实时采集数据。
Kafka消息队列,用来临时保存数据。
Strom实时计算数据。
Redis实时存储数据。
流式计算就是将源源不断产生的数据实时收集并实时计算,尽可能快的得到计算结果。
二、Strom介绍
Storm用来实时计算数据框架,特点:低延迟、高可用、分布式、可扩展、数据不丢失。提供简单容易理解的接口,便于开发。
Storm与Hadoop区别
Storm用于实时计算,Storm处理的数据保存在内存中,数据源源不断,通过网络传输进来;
Hadoop用于离线计算,处理的一批一批数据保存在文件系统中,数据保存在磁盘中。
三、Strom编程模型
Nimbus : 任务分配,对任务监督;
Zookeeper : 保存任务分配的信息、心跳信息、元数据信息。
Supervisor : 接受任务,并启动worker。worker的数量根据端口号来的;
Worker : 执行任务的具体组件(其实就是一个JVM),可以执行Spout或者bolt两种类型的任务; 一个worker就是一个端口号;
Task : Task=线程=executor, 一个Task属于一个Spout或者Bolt并发任务;
并发度:
用户指定的一个任务,可以被多个线程执行,对应到storm中就是一个task,并发度的数量等于线程的数量。一个任务的多个线程,会被运行在多个Worker(JVM)上,有一种类似于平均算法的负载均衡策略。尽可能减少网络IO;
一个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) {
//不輸出
}
}