2017-08-14 flume+kafka+storm+hdfs整合

来源:互联网 发布:游戏算法书籍 编辑:程序博客网 时间:2024/06/05 20:37

基础环境:

Redhat 5.5 64位(我这里是三台虚拟机h40,h41,h42)

myeclipse 8.5

jdk1.7.0_25

Python-2.7.12

zookeeper-3.4.5集群

Hadoop-2.6.0集群

apache-storm-0.9.5集群

kafka_2.10-0.8.2.0集群

apache-flume-1.6.0-bin(h40主节点装就行)


安装hadoop集群请参考:http://blog.csdn.net/m0_37739193/article/details/71222673

安装zookeeper请参考:http://blog.csdn.net/m0_37739193/article/details/72457879

安装Kafka:http://blog.csdn.net/m0_37739193/article/details/76688408

安装flume请参考:http://blog.csdn.net/m0_37739193/article/details/72392147


整合flume+kafka
flume-1.6.0已经有自带的kafkasink,我这里用的是它自带的插件,你也可以用其他开源的flumeng-kafka-plugin.jar插件,并且将你引用的这个jar包导入到flume的lib目录下。
[hadoop@h40 conf]$ cat kafka.conf 

[plain] view plain copy
  1. a1.sources = r1  
  2. a1.sinks = k1  
  3. a1.channels = c1  
  4.   
  5. # Describe/configure the source  
  6. a1.sources.r1.type = exec  
  7. a1.sources.r1.command = tail -F /home/hadoop/data.txt  
  8. a1.sources.r1.port = 44444  
  9. a1.sources.r1.host = 192.168.8.40  
  10. a1.sources.r1.channels = c1  
  11.   
  12. # Describe the sink  
  13. #引用开源的flumeng-kafka-plugin.jar的sink配置  
  14. #a1.sinks.k1.type = org.apache.flume.plugins.KafkaSink  
  15. #a1.sinks.k1.metadata.broker.list=h40:9092,h41:9092,h42:9092  
  16. #a1.sinks.k1.partition.key=0  
  17. #a1.sinks.k1.partitioner.class=org.apache.flume.plugins.SinglePartition  
  18. #a1.sinks.k1.serializer.class=kafka.serializer.StringEncoder  
  19. #a1.sinks.k1.request.required.acks=0  
  20. #a1.sinks.k1.max.message.size=1000000  
  21. #a1.sinks.k1.producer.type=sync  
  22. #a1.sinks.k1.custom.encoding=UTF-8  
  23. #a1.sinks.k1.custom.topic.name=test  
  24.   
  25. #kafka自带的kafkasink的sink配置  
  26. a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink  
  27. a1.sinks.k1.topic = test  
  28. a1.sinks.k1.brokerList = h40:9092,h41:9092,h42:9092  
  29. a1.sinks.k1.requiredAcks = 1  
  30. a1.sinks.k1.batchSize = 20  
  31. a1.sinks.k1.channel = c1  
  32.   
  33. # Use a channel which buffers events in memory  
  34. a1.channels.c1.type = memory  
  35. a1.channels.c1.capacity = 1000  
  36. a1.channels.c1.transactionCapacity = 100  
  37.   
  38. # Bind the source and sink to the channel  
  39. a1.sources.r1.channels = c1  
  40. a1.sinks.k1.channel = c1  


启动Kafka监控工具:
KafkaOffsetMonitor运行比较简单,因为所有运行文件,资源文件,jar文件都打包到KafkaOffsetMonitor-assembly-0.2.0.jar了,直接运行就可以,这种方式太棒了。既不用编译也不用配置,呵呵,也不是绝对不配置。
a.新建一个目录kafka-offset-console,然后把jar拷贝到该目录下

参考:
http://blog.csdn.NET/lizhitao/article/details/27199863
http://www.cnblogs.com/smartloli/p/4615908.html


[hadoop@h40 ~]$ mkdir kafka-offset-console
[hadoop@h40 ~]$ ls kafka-offset-console/
KafkaOffsetMonitor-assembly-0.2.0.jar


在kafka-offset-console目录下执行该命令运行在后台:

[plain] view plain copy
  1. java -cp KafkaOffsetMonitor-assembly-0.2.0.jar \  
  2.  com.quantifind.kafka.offsetapp.OffsetGetterWeb \  
  3.  --zk h40:2181,h41:2181,h42:2181/kafka \  
  4.  --port 8089 \  
  5.  --refresh 10.seconds \  
  6.  --retain 1.days 1>/dev/null 2>&1 &  

[hadoop@h40 apache-flume-1.6.0-bin]$ bin/flume-ng agent -c . -f conf/kafka.conf -n a1 -Dflume.root.logger=INFO,console
[hadoop@h40 ~]$ echo "hello world" >> data.txt


检验:
在h40节点的kafka消费者窗口可见“hello world”,说明整合成功!
[hadoop@h40 kafka_2.10-0.8.2.0]$ bin/kafka-console-consumer.sh --zookeeper h40:2181,h41:2181,h42:2181/kafka --topic  test  --from-beginning
hello world


预览:(在浏览器上输入http://192.168.8.40:8089)
我们通过Kafka的监控工具,来预览我们上传的日志记录,有没有在Kafka中产生消息数据
(如果对英文不是很熟悉的话,还可以用谷歌浏览器将页面翻译成中文,这样更方便读取信息)
(后来我又在网上看到了另一个kafka的监控工具https://github.com/smartloli/kafka-eagle,但感觉这个比上一个要复杂一些,这个我还没有亲自测试安装,不知道效果如何)


Storm安装配置:

Storm集群也依赖Zookeeper集群,要保证Zookeeper集群正常运行。Storm的安装配置比较简单,我们仍然使用下面3台机器搭建:
192.168.4.142   h40
192.168.4.143   h41
192.168.4.144   h42

[hadoop@h40 ~]$ tar -zxvf apache-storm-0.9.5.tar.gz

然后,修改配置文件conf/storm.yaml,添加如下内容:

[plain] view plain copy
  1. storm.zookeeper.servers:  
  2.     - "h40"  
  3.     - "h41"  
  4.     - "h42"  
  5. storm.zookeeper.port: 2181  
  6.   
  7. nimbus.host: "h40"  
  8.   
  9. supervisor.slots.ports:  
  10.     - 6700  
  11.     - 6701  
  12.     - 6702  
  13.     - 6703  
  14. 这个配置非常的膈应人,在某些地方必须加空格,否则启动会报错)  

将配置好的安装文件,分发到其他节点上:
[hadoop@h40 ~]$ scp -r apache-storm-0.9.5/ h41:/home/hadoop/
[hadoop@h40 ~]$ scp -r apache-storm-0.9.5/ h42:/home/hadoop/


Storm集群的主节点为Nimbus,从节点为Supervisor,我们需要在h40上启动Nimbus服务,在从节点h41、h42上启动Supervisor服务:
[hadoop@h40 apache-storm-0.9.5]$ bin/storm nimbus
却报这个错:

[plain] view plain copy
  1. File "bin/storm", line 61  
  2.   normclasspath = cygpath if sys.platform == 'cygwin' else identity  
  3.                            ^  
  4. ntaxError: invalid syntax  

百度了一下说是Python版本过低造成的,我用python -V看来一下,果然是很古老的Python 2.4.3版本,否则Python的好多新功能它都没有,于是我打算重新安装Python(如果你的Python版本够高可忽略此步骤,2.7以上就可以了),去官网下了个2.7.12的,下载地址:https://www.python.org/downloads/


Python2.7.12安装:三台机器都重复一下步骤:

[root@h40 usr]# tar -zxvf python-2.7.12.tgz
[root@h40 usr]# cd Python-2.7.12/


编译前,请先确认gcc、make、patch等编译工具是否已安装,并可正常使用。
[root@localhost ~]# yum -y install gcc*


[root@h40 Python-2.7.12]# ./configure

[root@h40 Python-2.7.12]# make && make install


[root@h40 Python-2.7.12]# rm -rf /usr/bin/python


错误做法:
[root@h40 Python-2.7.12]# ln -s python /usr/bin/python
[root@h40 Python-2.7.12]# ll /usr/bin/python
lrwxrwxrwx 1 root root 6 May 10 10:33 /usr/bin/python -> python
否则在执行bin/storm nimbus的时候会报这个错:
[hadoop@h40 apache-storm-0.9.5]$ bin/storm supervisor
-bash: bin/storm: /usr/bin/python: bad interpreter: Too many levels of symbolic links
正确做法:
[root@h40 ~]# ln -s /usr/Python-2.7.12/python /usr/bin/python
[root@h40 ~]# ll /usr/bin/python
lrwxrwxrwx 1 root root 25 May 10 10:37 /usr/bin/python -> /usr/Python-2.7.12/python


[root@h40 Python-2.7.12]# python -V
Python 2.7.12


可是再执行bin/storm nimbus的时候还是报错:
[hadoop@h40 apache-storm-0.9.5]$ bin/storm nimbus
-bash: bin/storm: /usr/bin/python: bad interpreter: Permission denied
解决方案:在/home/hadoop/apache-storm-0.9.5/bin/storm中,用你的实际Python安装路径#!/usr/Python-2.7.12/python替换第一行#!/usr/bin/python
(这里我不太懂的是前面的#不是注释的意思吗,那修不修改又有什么意义呢,可是结果它还好使。后来我在第二次试验的时候并没有报这个错,我也不知道啥原因,如果你没有出现该报错可忽略)


[hadoop@h40 apache-storm-0.9.5]$ bin/storm supervisor(不知道为什么这个主节点的supervisor也得开,我当时不开的话再后面的试验中无法将kafka中的数据实时传到storm做分析,三个节点都开supervisor的时候就正常,除了主节点只开两个节点的supervisor的话就产生空文件无数据产生。后来开主节点和一个从节点的supervisor并且关另一个从节点的supervisor却也好使,并且还会在有supervisor进程的主节点h40中再创建一个新的文件写入,我已将被玩坏了。。。。。
但是storm的容错性不是很好吗,只缺一个supervisor咋么会出错呢,再说我看人家博客中也没说要必须主节点也得开supervisor进程也成功了啊,不知道大家有没有遇到这个问题,很是困惑我。后来我在本地模式下把主节点即使把其他两节点的supervisor关掉却都能正常往里写文件里写数据,就是稍微等一下而已,并且发现开启的supervisor进程越多所等待的时间越少,我也真是奇了怪了!顺便这里提一句正常的情况下提交完Topology后会产生空文件,但是得等好长一会儿才能将kafka中的数据写入,时间长到你都怀疑试验失败了。。)
[hadoop@h41 apache-storm-0.9.5]$ bin/storm supervisor
[hadoop@h42 apache-storm-0.9.5]$ bin/storm supervisor
(还有就是在提交Topology后,在启动supervisor进程的控制台总是打印出如kill 24361: No such process之类的,其中数字不断的变化,但却不影响正常使用,我不太明白是什么原因)


为了方便监控,可以启动Storm UI,可以从Web页面上监控Storm Topology的运行状态,例如在h40上启动:
[hadoop@h40 apache-storm-0.9.5]$ bin/storm ui &
这样可以通过访问http://192.168.8.40:8080(我用http://h40:8080没有访问成功)来查看Topology的运行状况。


整合Kafka+Storm

消息通过各种方式进入到Kafka消息中间件,比如可以通过使用Flume来收集日志数据,然后在Kafka中路由暂存,然后再由实时计算程序Storm做实时分析,这时我们就需要将在Storm的Spout中读取Kafka中的消息,然后交由具体的Spot组件去分析处理。
下面,我们开发了一个简单WordCount示例程序,从Kafka读取订阅的消息行,通过空格拆分出单个单词,然后再做词频统计计算,实现的Topology的代码,如下所示:

[java] view plain copy
  1. package org.shirdrn.storm.examples;  
  2.   
  3. import java.io.FileWriter;  
  4. import java.io.IOException;  
  5. import java.util.HashMap;  
  6. import java.util.Map;  
  7. import java.util.StringTokenizer;  
  8. import java.util.UUID;  
  9.   
  10. import storm.kafka.BrokerHosts;  
  11. import storm.kafka.KafkaSpout;  
  12. import storm.kafka.SpoutConfig;  
  13. import storm.kafka.StringScheme;  
  14. import storm.kafka.ZkHosts;  
  15. import backtype.storm.Config;  
  16. import backtype.storm.LocalCluster;  
  17. import backtype.storm.StormSubmitter;  
  18. import backtype.storm.spout.SchemeAsMultiScheme;  
  19. import backtype.storm.task.TopologyContext;  
  20. import backtype.storm.topology.BasicOutputCollector;  
  21. import backtype.storm.topology.OutputFieldsDeclarer;  
  22. import backtype.storm.topology.TopologyBuilder;  
  23. import backtype.storm.topology.base.BaseBasicBolt;  
  24. import backtype.storm.tuple.Fields;  
  25. import backtype.storm.tuple.Tuple;  
  26. import backtype.storm.tuple.Values;  
  27.   
  28. public class MyKafkaTopology {  
  29.     public static class WordSpliter extends BaseBasicBolt{    
  30.         @Override    
  31.         public void execute(Tuple tuple, BasicOutputCollector collector){    
  32.             // 接收到一个句子    
  33.             String sentence = tuple.getString(0);  
  34.             // 把句子切割为单词    
  35.             StringTokenizer iter = new StringTokenizer(sentence);    
  36.             // 发送每一个单词    
  37.             while(iter.hasMoreElements()){    
  38.                 collector.emit(new Values(iter.nextToken()));    
  39.             }    
  40.         }    
  41.             
  42.         @Override    
  43.         public void declareOutputFields(OutputFieldsDeclarer declarer){    
  44.             // 定义一个字段    
  45.             declarer.declare(new Fields("word"));    
  46.         }    
  47.     }    
  48.       
  49.     public static class WriterBolt extends BaseBasicBolt {    
  50.         Map<String, Integer> counts = new HashMap<String, Integer>();    
  51.           
  52.         private FileWriter writer = null;  
  53.         @Override  
  54.         public void prepare(Map stormConf, TopologyContext context) {  
  55.             try {  
  56.                 writer = new FileWriter("/home/hadoop/stormoutput/" + "wordcount"+UUID.randomUUID().toString());  
  57. //将storm处理后的数据写入相应的路径下,如果在Linux上要写为/home/hadoop/stormoutput/的形式,如果是在Windows下要写成C:\\Users\\huiqiang\\Desktop\\stormoutput\\的形式,否则找不到指定的路径  
  58. //其实写入文件这步是没有必要的,在Linux中提交本地模式的时候可以将storm处理的数据打印到控制台上的,但是提交集群模式的时候却无法打印,并且提交Topology后程序就结束了,虽然Topology是提交成功了但是不知道storm处理后的数据跑哪了,所以才来了这么一步想直观点  
  59. //顺便说一句在Linux上提交本地模式的时候写入的文件会只在h40的/home/hadoop/stormoutput/下产生三个文件,而提交集群模式的话会在每个节点下的/home/hadoop/stormoutput/目录下产生一个文件  
  60.             } catch (IOException e) {  
  61.                 throw new RuntimeException(e);  
  62.             }  
  63.         }  
  64.             
  65.         @Override    
  66.         public void execute(Tuple tuple, BasicOutputCollector collector){    
  67.             // 接收一个单词    
  68.             String word = tuple.getString(0);    
  69.             // 获取该单词对应的计数    
  70.             Integer count = counts.get(word);    
  71.             if(count == null)    
  72.                 count = 0;    
  73.             // 计数增加    
  74.             count++;    
  75.             // 将单词和对应的计数加入map中    
  76.             counts.put(word,count);    
  77.             System.out.println("hello word!");    
  78.             System.out.println(word +"  "+count);   
  79.               
  80.             //用io流将数据写入指定的文件中  
  81.             try {  
  82.                 writer.write(word +"  "+count);  
  83.                 writer.write("\n");  
  84.                 writer.flush();  
  85.             } catch (IOException e) {  
  86.                 throw new RuntimeException(e);  
  87.             }  
  88.             // 发送单词和计数(分别对应字段word和count)    
  89.             collector.emit(new Values(word, count));    
  90.         }    
  91.             
  92.         @Override    
  93.         public void declareOutputFields(OutputFieldsDeclarer declarer){    
  94.             // 定义两个字段word和count    
  95.             declarer.declare(new Fields("word","count"));    
  96.         }    
  97.     }    
  98.       
  99.     public static void main(String[] args) throws Exception {  
  100.           
  101.         String topic = "test";  
  102.         String zkRoot = "/kafka-storm";  
  103.         String id = "old";  
  104.         BrokerHosts brokerHosts = new ZkHosts("h40:2181,h41:2181,h42:2181","/kafka/brokers");   
  105. //配置kafka时,如果使用zookeeper create /kafka创建了节点,kafka与storm集成时new ZkHosts(zks) 需要改成 new ZkHosts(zks,”/kafka/brokers”),不然会报  
  106. //java.lang.RuntimeException: java.lang.RuntimeException: org.apache.zookeeper.KeeperException$NoNodeException: KeeperErrorCode = NoNode for /brokers/topics/test/partitions  
  107.         SpoutConfig spoutConfig = new SpoutConfig(brokerHosts, topic, zkRoot, id);  
  108.         spoutConfig.forceFromStart = true;  
  109.         spoutConfig.scheme = new SchemeAsMultiScheme(new StringScheme());  
  110.         TopologyBuilder builder = new TopologyBuilder();  
  111.         //设置一个spout用来从kaflka消息队列中读取数据并发送给下一级的bolt组件,此处用的spout组件并非自定义的,而是storm中已经开发好的KafkaSpout  
  112.         builder.setSpout("KafkaSpout"new KafkaSpout(spoutConfig));  
  113.         builder.setBolt("word-spilter"new WordSpliter()).shuffleGrouping("KafkaSpout");  
  114.         builder.setBolt("writer"new WriterBolt(), 3).fieldsGrouping("word-spilter"new Fields("word"));  
  115.         Config conf = new Config();  
  116.         conf.setNumWorkers(4);  
  117.         conf.setNumAckers(0);  
  118.         conf.setDebug(false);  
  119.           
  120.         if (args != null && args.length > 0) {  
  121.         //提交topology到storm集群中运行  
  122.         StormSubmitter.submitTopology("sufei-topo", conf, builder.createTopology());  
  123.         } else {  
  124.         //LocalCluster用来将topology提交到本地模拟器运行,方便开发调试  
  125.         LocalCluster cluster = new LocalCluster();  
  126.         cluster.submitTopology("WordCount", conf, builder.createTopology());  
  127.         }  
  128.     }  
  129. }  

上面Topology实现代码中,有一个很关键的配置对象SpoutConfig,配置属性如下所示:
spoutConf.forceFromStart = false;
该配置是指,如果该Topology因故障停止处理,下次正常运行时是否从Spout对应数据源Kafka中的该订阅Topic的起始位置开始读取,如果forceFromStart=true,则之前处理过的Tuple还要重新处理一遍,否则会从上次处理的位置继续处理,保证Kafka中的Topic数据不被重复处理,是在数据源的位置进行状态记录。


将所依赖的jar包导入到storm主节点的lib目录下:
[hadoop@h40 ~]$ cp kafka_2.10-0.8.2.0/libs/kafka_2.10-0.8.2.0.jar apache-storm-0.9.5/lib/
[hadoop@h40 ~]$ cp kafka_2.10-0.8.2.0/libs/Scala-library-2.10.4.jar apache-storm-0.9.5/lib/
[hadoop@h40 ~]$ cp kafka_2.10-0.8.2.0/libs/metrics-core-2.2.0.jar apache-storm-0.9.5/lib/
[hadoop@h40 ~]$ cp kafka_2.10-0.8.2.0/libs/snappy-Java-1.1.1.6.jar apache-storm-0.9.5/lib/
[hadoop@h40 ~]$ cp kafka_2.10-0.8.2.0/libs/zkclient-0.3.jar apache-storm-0.9.5/lib/
[hadoop@h40 ~]$ cp kafka_2.10-0.8.2.0/libs/log4j-1.2.16.jar apache-storm-0.9.5/lib/
[hadoop@h40 ~]$ cp kafka_2.10-0.8.2.0/libs/slf4j-api-1.7.6.jar apache-storm-0.9.5/lib/
[hadoop@h40 ~]$ cp kafka_2.10-0.8.2.0/libs/jopt-simple-3.2.jar apache-storm-0.9.5/lib/
[hadoop@h40 ~]$ cp kafka_2.10-0.8.2.0/libs/zookeeper-3.4.6.jar apache-storm-0.9.5/lib/
[hadoop@h40 ~]$ cp kafka_2.10-0.8.2.0/libs/kafka_2.10-0.8.2.0-test.jar apache-storm-0.9.5/lib/(这个在eclipse里不用导入不报错,但是在Linux中却报java.lang.NoClassDefFoundError: org/apache/kafka/common/utils/Utils)
(以上这些步骤在linux中必须在所有节点的storm的lib目录下都导入这些依赖包,集群模式下如此,如果是在本地模式下则只在storm的主节点的lib目录下导入这些依赖包即可)
(在Linux中可以把kafka的libs目录下的jar包全部(除slf4j-log4j12-1.6.1.jar)导入到storm的lib目录下,在eclipse中也不能将kafka的libs目录下的slf4j-log4j12-1.6.1.jar导入Libraries中,否则会报错,参考:http://blog.csdn.net/ouyang111222/article/details/49700733)
(还得将curator-client-2.4.0.jar、curator-framework-2.4.0.jar、guava-13.0.jar这三个jar包导入到storm主节点的的lib目录下,apache-storm-0.9.2-incubating版本中的lib目录下就有,但是这个版本这提供了storm与kafka整合的jar包,而storm-0.9.5还提供了storm与hdfs和Hbase整合的jar包)


用myeclipse将代码打包成wordcount.jar上传到h40的/home/hadoop/apache-storm-0.9.5目录下,然后,就可以提交我们开发的Topology程序了:(好多博客中都是用maven打成jar包,而我却用的是myeclipse 8.5,一是因为我对maven不是很了解,二是eclipse和maven还得整合就有多了一道工序,所以我这里用了大家都比较熟悉的eclipse)
[hadoop@h40 apache-storm-0.9.5]$ bin/storm jar wordcount.jar org.shirdrn.storm.examples.MyKafkaTopology h40
可是会报这个错:

[plain] view plain copy
  1. Exception in thread "main" java.lang.NoClassDefFoundError: storm/kafka/BrokerHosts  
  2.         at java.lang.Class.getDeclaredMethods0(Native Method)  
  3.         at java.lang.Class.privateGetDeclaredMethods(Class.java:2521)  
  4.         at java.lang.Class.getMethod0(Class.java:2764)  
  5.         at java.lang.Class.getMethod(Class.java:1653)  
  6.         at sun.launcher.LauncherHelper.getMainMethod(LauncherHelper.java:494)  
  7.         at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:486)  
  8. Caused by: java.lang.ClassNotFoundException: storm.kafka.BrokerHosts  
  9.         at java.net.URLClassLoader$1.run(URLClassLoader.java:366)  
  10.         at java.net.URLClassLoader$1.run(URLClassLoader.java:355)  
  11.         at java.security.AccessController.doPrivileged(Native Method)  
  12.         at java.net.URLClassLoader.findClass(URLClassLoader.java:354)  
  13.         at java.lang.ClassLoader.loadClass(ClassLoader.java:424)  
  14.         at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)  
  15.         at java.lang.ClassLoader.loadClass(ClassLoader.java:357)  
  16.         ... 6 more  
原因:
server端(h40)没有storm-kafka相关jar包
解决:
[hadoop@h40 apache-storm-0.9.5]$ cp external/storm-kafka/storm-kafka-0.9.5.jar lib/
(这个版本已经自带了kafka和storm的插件,external/目录下还有hbase和hdfs整合的插件,在apache-storm-0.9.2-incubating版本中只有整合kafka的插件,而在0.8.x版本中没有自带的这些插件,还得去GitHub上下载别人开源的整合kafka-storm的插件)


我见有的博客中直接执行bin/storm jar wordcount.jar MyKafkaTopology h40也可以提交Topology,但是我却报错:
Error: Could not find or load main class MyKafkaTopology
还必须得加上包名,后来才知道不加包名的是在eclipse中没有创建包而是使用了默认的包名(default package)。


按http://shiyanjun.cn/archives/934.html中的整合Kafka+Storm中的代码修改相应参数后运行:
[hadoop@h40 apache-storm-0.9.5]$ bin/storm jar wordcount.jar org.shirdrn.storm.examples.MyKafkaTopology h40

[plain] view plain copy
  1. 。。。。。。。  
  2. Start uploading file 'wordcount.jar' to 'storm-local/nimbus/inbox/stormjar-2f5eede7-71dd-41c0-95a7-32ac5bc4097f.jar' (6980 bytes)  
  3. [==================================================] 6980 / 6980  
  4. File 'wordcount.jar' uploaded to 'storm-local/nimbus/inbox/stormjar-2f5eede7-71dd-41c0-95a7-32ac5bc4097f.jar' (6980 bytes)  
  5. (我不知道输出结果在哪里。。。。。。。并且用bin/storm list命令查看Topology还提交成功了)  

后来用我自己的代码:
[hadoop@h40 apache-storm-0.9.5]$ bin/storm jar wordcount.jar org.shirdrn.storm.examples.MyKafkaTopology h40

[plain] view plain copy
  1. 337  [main] INFO  backtype.storm.StormSubmitter - Jar not uploaded to master yet. Submitting jar...  
  2. 348  [main] INFO  backtype.storm.StormSubmitter - Uploading topology jar wordcount.jar to assigned location: storm-local/nimbus/inbox/stormjar-a0260e30-7c9a-465e-b796-38fe25a58a13.jar  
  3. 364  [main] INFO  backtype.storm.StormSubmitter - Successfully uploaded topology jar to assigned location: storm-local/nimbus/inbox/stormjar-a0260e30-7c9a-465e-b796-38fe25a58a13.jar  
  4. 364  [main] INFO  backtype.storm.StormSubmitter - Submitting topology sufei-topo in distributed mode with conf {"topology.workers":4,"topology.acker.executors":0,"topology.debug":false}  
  5. 593  [main] INFO  backtype.storm.StormSubmitter - Finished submitting topology: sufei-topo  
  6. (到这程序就结束了,并没有阻塞。。。。。。)  

[hadoop@h40 apache-storm-0.9.5]$ bin/storm list

[plain] view plain copy
  1. 。。。  
  2. 775  [main] INFO  backtype.storm.thrift - Connecting to Nimbus at h40:6627  
  3. Topology_name        Status     Num_tasks  Num_workers  Uptime_secs  
  4. -------------------------------------------------------------------  
  5. sufei-topo           ACTIVE     5          4            60    
  6. (Topology提交成功)  

[hadoop@h40 zookeeper-3.4.5]$ ./bin/zkCli.sh 

[plain] view plain copy
  1. [zk: localhost:2181(CONNECTED) 15] ls /  
  2. [zookeeper, admin, kafka-storm, consumers, config, controller, storm, brokers, controller_epoch]  
  3. [zk: localhost:2181(CONNECTED) 16] ls /kafka-storm  
  4. [old]  
  5. [zk: localhost:2181(CONNECTED) 17] ls /kafka-storm/old  
  6. [partition_1, partition_0]  
  7. (登录zookeeper客户端可发现新产生了/kafka-storm/old)  

在三个节点下的stormoutput目录下产生相应的文件,这里产生的规则应该是产生文件的数量和builder.setBolt("writer", new WriterBolt(), 3).fieldsGrouping("word-spilter", new Fields("word"));中的数字参数有关,并且每个节点下的文件数应该是随机的吧,但是产生文件的实际情况比较复杂我没有找到相应的规律,甚至还在一个节点的该目录下产生了很多的空文件,等过了一会儿才产生有内容的文件,我也是醉了
在kafka生产者中写入数据,则相应stormoutput目录下的文件也会相应增加内容。。


可以通过查看日志文件(logs/目录下)或者Storm UI来监控Topology的运行状况。如果程序没有错误,可以使用前面我们使用的Kafka Producer来生成消息,就能看到我们开发的Storm Topology能够实时接收到并进行处理。


(通过代码可知当有参数h40的时候提交的是集群模式,没有任何参数提交的是本地模式)
在Linux上提交本地模式:
[hadoop@h40 apache-storm-0.9.5]$ bin/storm jar wordcount.jar org.shirdrn.storm.examples.MyKafkaTopology

[plain] view plain copy
  1. 运行结果:  
  2. hello word!  
  3. hello  1  
  4. hello word!  
  5. hadoop  1  
  6. hello word!  
  7. hello  2  
  8. hello word!  
  9. hadoop  2  
  10. hello word!  
  11. hello  3  
  12. hello word!  
  13. hive  1  
  14. (程序阻塞在这里,有新的内容则会再将新结果打印出来)  

用bin/storm list命令查看并没有Topology产生,登录zookeeper客户端也不会看到有/kafka-storm目录生成


[hadoop@h40 kafka_2.10-0.8.2.0]$ bin/kafka-console-consumer.sh --zookeeper h40:2181,h41:2181,h42:2181 --topic hui  --from-beginning

[plain] view plain copy
  1. hello hadoop  
  2. hello hadoop  
  3. hello hive  

[hadoop@h40 ~]$ ll stormoutput/
[plain] view plain copy
  1. total 8  
  2. -rw-rw-r-- 1 hadoop hadoop  0 May 11 11:39 wordcount29dbd1db-06ad-44bf-8b38-1a4812992c04  
  3. -rw-rw-r-- 1 hadoop hadoop 47 May 11 11:39 wordcount9398f5ce-792f-421f-b12c-9b3fe5c7bb8c  
  4. -rw-rw-r-- 1 hadoop hadoop  8 May 11 11:39 wordcountab5b6a8e-31e1-4597-a5be-6353cdf0ba2b  

[hadoop@h40 kafka_2.10-0.8.2.0]$ bin/kafka-console-producer.sh --broker-list h40:9092,h41:9092,h42:9092 --topic hui
[2017-05-11 11:41:08,428] WARN Property topic is not valid (kafka.utils.VerifiableProperties)
hello hadoop
(在kafka生产者中输入数据,则storm的Topology端会实时输出处理信息,stormoutput目录下的文件也会动态增加信息):
hello word!
hello  4
hello word!
hadoop  3
[hadoop@h40 ~]$ tail -f stormoutput/wordcount29dbd1db-06ad-44bf-8b38-1a4812992c04 stormoutput/wordcount9398f5ce-792f-421f-b12c-9b3fe5c7bb8c stormoutput/wordcountab5b6a8e-31e1-4597-a5be-6353cdf0ba2b
==> stormoutput/wordcount9398f5ce-792f-421f-b12c-9b3fe5c7bb8c <==
hello  4
hadoop  3


注意:
当在eclipse中提交本地模式的时候可能会报这个错(在主方法中直接右键点击Run As-->Java Application就是提交的本地模式):

[plain] view plain copy
  1. java.net.UnknownHostException: h40  
  2.     at java.net.Inet6AddressImpl.lookupAllHostAddr(Native Method) ~[na:1.6.0_13]  
  3.     at java.net.InetAddress$1.lookupAllHostAddr(InetAddress.java:849) ~[na:1.6.0_13]  
  4.     at java.net.InetAddress.getAddressFromNameService(InetAddress.java:1200) ~[na:1.6.0_13]  
  5.     at java.net.InetAddress.getAllByName0(InetAddress.java:1153) ~[na:1.6.0_13]  
  6.     at java.net.InetAddress.getAllByName(InetAddress.java:1083) ~[na:1.6.0_13]  
  7.     at java.net.InetAddress.getAllByName(InetAddress.java:1019) ~[na:1.6.0_13]  
  8.     at org.apache.zookeeper.client.StaticHostProvider.<init>(StaticHostProvider.java:60) ~[zookeeper-3.4.5.jar:3.4.5-1392090]  
  9.     at org.apache.zookeeper.ZooKeeper.<init>(ZooKeeper.java:445) ~[zookeeper-3.4.5.jar:3.4.5-1392090]  
  10.     at org.apache.curator.utils.DefaultZookeeperFactory.newZooKeeper(DefaultZookeeperFactory.java:29) ~[curator-client-2.4.0.jar:na]  
  11.     at org.apache.curator.framework.imps.CuratorFrameworkImpl$2.newZooKeeper(CuratorFrameworkImpl.java:169) ~[curator-framework-2.4.0.jar:na]  
  12.     at org.apache.curator.HandleHolder$1.getZooKeeper(HandleHolder.java:94) ~[curator-client-2.4.0.jar:na]  
  13.     at org.apache.curator.HandleHolder.getZooKeeper(HandleHolder.java:55) ~[curator-client-2.4.0.jar:na]  
  14.     at org.apache.curator.ConnectionState.reset(ConnectionState.java:219) ~[curator-client-2.4.0.jar:na]  
  15.     at org.apache.curator.ConnectionState.start(ConnectionState.java:103) ~[curator-client-2.4.0.jar:na]  
  16.     at org.apache.curator.CuratorZookeeperClient.start(CuratorZookeeperClient.java:188) ~[curator-client-2.4.0.jar:na]  
  17.     at org.apache.curator.framework.imps.CuratorFrameworkImpl.start(CuratorFrameworkImpl.java:234) ~[curator-framework-2.4.0.jar:na]  
  18.     at storm.kafka.DynamicBrokersReader.<init>(DynamicBrokersReader.java:53) [storm-kafka-0.9.2-incubating.jar:0.9.2-incubating]  
  19.     at storm.kafka.trident.ZkBrokerReader.<init>(ZkBrokerReader.java:41) [storm-kafka-0.9.2-incubating.jar:0.9.2-incubating]  
  20.     at storm.kafka.KafkaUtils.makeBrokerReader(KafkaUtils.java:57) [storm-kafka-0.9.2-incubating.jar:0.9.2-incubating]  
  21.     at storm.kafka.KafkaSpout.open(KafkaSpout.java:87) [storm-kafka-0.9.2-incubating.jar:0.9.2-incubating]  
  22.     at backtype.storm.daemon.executor$fn__5573$fn__5588.invoke(executor.clj:520) [storm-core-0.9.2-incubating.jar:0.9.2-incubating]  
  23.     at backtype.storm.util$async_loop$fn__457.invoke(util.clj:429) [storm-core-0.9.2-incubating.jar:0.9.2-incubating]  
  24.     at clojure.lang.AFn.run(AFn.java:24) [clojure-1.5.1.jar:na]  
  25.     at java.lang.Thread.run(Thread.java:619) [na:1.6.0_13]  
解决:修改C:\Windows\System32\drivers\etc\hosts文件,添加如下内容,可能无法保存,请参考:https://jingyan.baidu.com/article/624e7459b194f134e8ba5a8e.html(Windows10),https://jingyan.baidu.com/article/e5c39bf56564a539d7603312.html(Windows7)
在末尾添加(你的storm集群的IP和主机名):
192.168.8.40h40
192.168.8.41h41
192.168.8.42h42


提交本地模式运行的时候会出现这个,但不必理会不影响正常使用

[plain] view plain copy
  1. java.net.SocketException: Address family not supported by protocol family: connect  
  2.     at sun.nio.ch.Net.connect(Native Method) ~[na:1.6.0_13]  
  3.     at sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:507) ~[na:1.6.0_13]  
  4.     at org.apache.zookeeper.ClientCnxnSocketNIO.registerAndConnect(ClientCnxnSocketNIO.java:266) ~[zookeeper-3.4.5.jar:3.4.5-1392090]  
  5.     at org.apache.zookeeper.ClientCnxnSocketNIO.connect(ClientCnxnSocketNIO.java:276) ~[zookeeper-3.4.5.jar:3.4.5-1392090]  
  6.     at org.apache.zookeeper.ClientCnxn$SendThread.startConnect(ClientCnxn.java:958) ~[zookeeper-3.4.5.jar:3.4.5-1392090]  
  7.     at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:993) ~[zookeeper-3.4.5.jar:3.4.5-1392090]  

以上kafka、nimbus、supervisor进程的启动我都在窗口中直接启动了,我这里是为了方便观察和学习,正常情况下可以在后台运行程序:
如:
bin/kafka-server-start.sh config/server.properties &
(但是这种可能会在窗口莫名奇妙的弹出信息不太好,我百度的博客中大多数用的都是这种)
下面这种更好:
bin/kafka-server-start.sh config/server.properties 1>/dev/null 2>&1 &
(1>/dev/null的意思将标准输出重定向到/dev/null中,2>&1的意思是将错误输出也和标准输出重定向到相同的地方,&的意思是在后台运行)


整合Storm+HDFS

Storm实时计算集群从Kafka消息中间件中消费消息,有实时处理需求的可以走实时处理程序,还有需要进行离线分析的需求,如写入到HDFS进行分析。下面实现了一个Topology,代码如下所示:

[java] view plain copy
  1. package org.shirdrn.storm.examples;  
  2.   
  3. import java.text.DateFormat;  
  4. import java.text.SimpleDateFormat;  
  5. import java.util.Date;  
  6. import java.util.Map;  
  7. import java.util.Random;  
  8.   
  9. import org.apache.commons.logging.Log;  
  10. import org.apache.commons.logging.LogFactory;  
  11. import org.apache.storm.hdfs.bolt.HdfsBolt;  
  12. import org.apache.storm.hdfs.bolt.format.DefaultFileNameFormat;  
  13. import org.apache.storm.hdfs.bolt.format.DelimitedRecordFormat;  
  14. import org.apache.storm.hdfs.bolt.format.FileNameFormat;  
  15. import org.apache.storm.hdfs.bolt.format.RecordFormat;  
  16. import org.apache.storm.hdfs.bolt.rotation.FileRotationPolicy;  
  17. import org.apache.storm.hdfs.bolt.rotation.TimedRotationPolicy;  
  18. import org.apache.storm.hdfs.bolt.rotation.TimedRotationPolicy.TimeUnit;  
  19. import org.apache.storm.hdfs.bolt.sync.CountSyncPolicy;  
  20. import org.apache.storm.hdfs.bolt.sync.SyncPolicy;  
  21.   
  22. import backtype.storm.Config;  
  23. import backtype.storm.LocalCluster;  
  24. import backtype.storm.StormSubmitter;  
  25. import backtype.storm.generated.AlreadyAliveException;  
  26. import backtype.storm.generated.InvalidTopologyException;  
  27. import backtype.storm.spout.SpoutOutputCollector;  
  28. import backtype.storm.task.TopologyContext;  
  29. import backtype.storm.topology.OutputFieldsDeclarer;  
  30. import backtype.storm.topology.TopologyBuilder;  
  31. import backtype.storm.topology.base.BaseRichSpout;  
  32. import backtype.storm.tuple.Fields;  
  33. import backtype.storm.tuple.Values;  
  34. import backtype.storm.utils.Utils;  
  35.   
  36. public class StormToHDFSTopology {  
  37.   
  38.      public static class EventSpout extends BaseRichSpout {  
  39.   
  40.           private static final Log LOG = LogFactory.getLog(EventSpout.class);  
  41.           private static final long serialVersionUID = 886149197481637894L;  
  42.           private SpoutOutputCollector collector;  
  43.           private Random rand;  
  44.           private String[] records;  
  45.            
  46.           @Override  
  47.           public void open(Map conf, TopologyContext context,  
  48.                     SpoutOutputCollector collector) {  
  49.                this.collector = collector;      
  50.                rand = new Random();  
  51.                records = new String[] {  
  52.                          "10001     ef2da82d4c8b49c44199655dc14f39f6     4.2.1     HUAWEI G610-U00     HUAWEI     2     70:72:3c:73:8b:22     2014-10-13 12:36:35",  
  53.                          "10001     ffb52739a29348a67952e47c12da54ef     4.3     GT-I9300     samsung     2     50:CC:F8:E4:22:E2     2014-10-13 12:36:02",  
  54.                          "10001     ef2da82d4c8b49c44199655dc14f39f6     4.2.1     HUAWEI G610-U00     HUAWEI     2     70:72:3c:73:8b:22     2014-10-13 12:36:35"  
  55.                };  
  56.           }  
  57.   
  58.   
  59.           @Override  
  60.           public void nextTuple() {  
  61.                Utils.sleep(1000);  
  62.                DateFormat df = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");  
  63.                Date d = new Date(System.currentTimeMillis());  
  64.                String minute = df.format(d);  
  65.                String record = records[rand.nextInt(records.length)];  
  66.                LOG.info("EMIT[spout -> hdfs] " + minute + " : " + record);  
  67.                collector.emit(new Values(minute, record));  
  68.           }  
  69.   
  70.           @Override  
  71.           public void declareOutputFields(OutputFieldsDeclarer declarer) {  
  72.                declarer.declare(new Fields("minute""record"));           
  73.           }  
  74.   
  75.   
  76.      }  
  77.       
  78.      public static void main(String[] args) throws AlreadyAliveException, InvalidTopologyException, InterruptedException {  
  79.           // use "|" instead of "," for field delimiter  
  80.           RecordFormat format = new DelimitedRecordFormat()  
  81.                   .withFieldDelimiter(" : ");  
  82.   
  83.           // sync the filesystem after every 1k tuples  
  84.           SyncPolicy syncPolicy = new CountSyncPolicy(1000);  
  85.   
  86.           // rotate files   
  87.           FileRotationPolicy rotationPolicy = new TimedRotationPolicy(1.0f, TimeUnit.MINUTES);  
  88.   
  89.           FileNameFormat fileNameFormat = new DefaultFileNameFormat()  
  90.                   .withPath("/storm/").withPrefix("app_").withExtension(".log");  
  91.   
  92.           HdfsBolt hdfsBolt = new HdfsBolt()  
  93.                   .withFsUrl("hdfs://h40:9000")  
  94.                   .withFileNameFormat(fileNameFormat)  
  95.                   .withRecordFormat(format)  
  96.                   .withRotationPolicy(rotationPolicy)  
  97.                   .withSyncPolicy(syncPolicy);  
  98.            
  99.           TopologyBuilder builder = new TopologyBuilder();  
  100.           builder.setSpout("event-spout"new EventSpout(), 3);  
  101.           builder.setBolt("hdfs-bolt", hdfsBolt, 2).fieldsGrouping("event-spout"new Fields("minute"));  
  102.            
  103.           Config conf = new Config();  
  104.            
  105.           String name = StormToHDFSTopology.class.getSimpleName();  
  106.           if (args != null && args.length > 0) {  
  107.                conf.put(Config.NIMBUS_HOST, args[0]);  
  108.                conf.setNumWorkers(3);  
  109.                StormSubmitter.submitTopologyWithProgressBar(name, conf, builder.createTopology());  
  110.           } else {  
  111.                conf.setMaxTaskParallelism(3);  
  112.                LocalCluster cluster = new LocalCluster();  
  113.                cluster.submitTopology(name, conf, builder.createTopology());  
  114.                Thread.sleep(60000);  
  115.                cluster.shutdown();  
  116.           }  
  117.      }  
  118.   
  119. }  
上面的处理逻辑,可以对HdfsBolt进行更加详细的配置,如FileNameFormat、SyncPolicy、FileRotationPolicy(可以设置在满足什么条件下,切出一个新的日志,如可以指定多长时间切出一个新的日志文件,可以指定一个日志文件大小达到设置值后,再写一个新日志文件),更多设置可以参考storm-hdfs。


修改~/.bashrc文件,添加:
[hadoop@h40 ~]$ vi ~/.bash_profile
export CLASSPATH=.:/home/hadoop/hadoop-2.6.0/etc/hadoop:/home/hadoop/hadoop-2.6.0/share/hadoop/common/lib/*:/home/hadoop/hadoop-2.6.0/share/hadoop/common/*:/home/hadoop/hadoop-2.6.0/share/hadoop/hdfs:/home/hadoop/hadoop-2.6.0/share/hadoop/hdfs/lib/*:/home/hadoop/hadoop-2.6.0/share/hadoop/hdfs/*:/home/hadoop/hadoop-2.6.0/share/hadoop/yarn/lib/*:/home/hadoop/hadoop-2.6.0/share/hadoop/yarn/*:/home/hadoop/hadoop-2.6.0/share/hadoop/mapreduce/lib/*:/home/hadoop/hadoop-2.6.0/share/hadoop/mapreduce/*:/home/hadoop/hadoop-2.6.0/contrib/capacity-scheduler/*.jar
将hadoop classpath所依赖的jar包(除slf4j-log4j12-1.7.5.jar外,这个很重要)导入storm的lib目录下(所有节点,集群模式下)。本地模式下的话只需在storm的主节点导入就可以
[hadoop@h40 ~]$ cp apache-storm-0.9.5/external/storm-hdfs/storm-hdfs-0.9.5.jar apache-storm-0.9.5/lib/


提交Topology:

[plain] view plain copy
  1. [hadoop@h40 apache-storm-0.9.5]$ bin/storm jar wordcount.jar org.shirdrn.storm.examples.StormToHDFSTopology h40  
  2. 。。。  
  3. Start uploading file 'wordcount.jar' to 'storm-local/nimbus/inbox/stormjar-a0f3899e-29e0-4649-85b9-66569e64f15a.jar' (11285 bytes)  
  4. [==================================================] 11285 / 11285  
  5. File 'wordcount.jar' uploaded to 'storm-local/nimbus/inbox/stormjar-a0f3899e-29e0-4649-85b9-66569e64f15a.jar' (11285 bytes)  

稍等一会儿(这个时间还挺蛋疼的,有时长有时短,长的时候也不知道试验成功没就得在那里等)再查看hdfs路径:

[plain] view plain copy
  1. [hadoop@h40 ~]$ hadoop fs -lsr /  
  2. drwxr-xr-x   - hadoop supergroup          0 2017-05-11 14:52 /storm  
  3. -rw-r--r--   3 hadoop supergroup       5150 2017-05-11 14:47 /storm/app_hdfs-bolt-8-0-1494485195669.log  
  4. -rw-r--r--   3 hadoop supergroup       4628 2017-05-11 14:48 /storm/app_hdfs-bolt-8-1-1494485279754.log  
  5. [hadoop@h40 ~]$ hadoop fs -cat /storm/app_hdfs-bolt-8-0-1494485195669.log  
  6. 17/05/11 14:54:37 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable  
  7. 2017-05-11_14-46-32 : 10001     ef2da82d4c8b49c44199655dc14f39f6     4.2.1     HUAWEI G610-U00     HUAWEI     2     70:72:3c:73:8b:22     2014-10-13 12:36:35  
  8. 2017-05-11_14-46-34 : 10001     ef2da82d4c8b49c44199655dc14f39f6     4.2.1     HUAWEI G610-U00     HUAWEI     2     70:72:3c:73:8b:22     2014-10-13 12:36:35  
  9. 2017-05-11_14-46-36 : 10001     ef2da82d4c8b49c44199655dc14f39f6     4.2.1     HUAWEI G610-U00     HUAWEI     2     70:72:3c:73:8b:22     2014-10-13 12:36:35  
  10. 2017-05-11_14-46-38 : 10001     ef2da82d4c8b49c44199655dc14f39f6     4.2.1     HUAWEI G610-U00     HUAWEI     2     70:72:3c:73:8b:22     2014-10-13 12:36:35  
  11. 2017-05-11_14-46-41 : 10001     ef2da82d4c8b49c44199655dc14f39f6     4.2.1     HUAWEI G610-U00     HUAWEI     2     70:72:3c:73:8b:22     2014-10-13 12:36:35  
  12. 2017-05-11_14-46-43 : 10001     ffb52739a29348a67952e47c12da54ef     4.3     GT-I9300     samsung     2     50:CC:F8:E4:22:E2     2014-10-13 12:36:02  
  13. 2017-05-11_14-46-45 : 10001     ef2da82d4c8b49c44199655dc14f39f6     4.2.1     HUAWEI G610-U00     HUAWEI     2     70:72:3c:73:8b:22     2014-10-13 12:36:35  
  14. 。。。  

整合Flume+Kafka+Storm+HDFS

上面分别对整合Kafka+Storm和Storm+HDFS做了实践,可以将后者的Spout改成前者的Spout,从Kafka中消费消息,在Storm中可以做简单处理,然后将数据写入HDFS,最后可以在Hadoop平台上对数据进行离线分析处理。下面,写了一个简单的例子,从Kafka消费消息,然后经由Storm处理,写入到HDFS存储,代码如下所示:

[java] view plain copy
  1. package org.shirdrn.storm.examples;  
  2.   
  3. import java.util.Arrays;  
  4. import java.util.Map;  
  5.   
  6. import org.apache.commons.logging.Log;  
  7. import org.apache.commons.logging.LogFactory;  
  8. import org.apache.storm.hdfs.bolt.HdfsBolt;  
  9. import org.apache.storm.hdfs.bolt.format.DefaultFileNameFormat;  
  10. import org.apache.storm.hdfs.bolt.format.DelimitedRecordFormat;  
  11. import org.apache.storm.hdfs.bolt.format.FileNameFormat;  
  12. import org.apache.storm.hdfs.bolt.format.RecordFormat;  
  13. import org.apache.storm.hdfs.bolt.rotation.FileRotationPolicy;  
  14. import org.apache.storm.hdfs.bolt.rotation.TimedRotationPolicy;  
  15. import org.apache.storm.hdfs.bolt.rotation.TimedRotationPolicy.TimeUnit;  
  16. import org.apache.storm.hdfs.bolt.sync.CountSyncPolicy;  
  17. import org.apache.storm.hdfs.bolt.sync.SyncPolicy;  
  18.   
  19. import storm.kafka.BrokerHosts;  
  20. import storm.kafka.KafkaSpout;  
  21. import storm.kafka.SpoutConfig;  
  22. import storm.kafka.StringScheme;  
  23. import storm.kafka.ZkHosts;  
  24. import backtype.storm.Config;  
  25. import backtype.storm.LocalCluster;  
  26. import backtype.storm.StormSubmitter;  
  27. import backtype.storm.generated.AlreadyAliveException;  
  28. import backtype.storm.generated.InvalidTopologyException;  
  29. import backtype.storm.spout.SchemeAsMultiScheme;  
  30. import backtype.storm.task.OutputCollector;  
  31. import backtype.storm.task.TopologyContext;  
  32. import backtype.storm.topology.OutputFieldsDeclarer;  
  33. import backtype.storm.topology.TopologyBuilder;  
  34. import backtype.storm.topology.base.BaseRichBolt;  
  35. import backtype.storm.tuple.Fields;  
  36. import backtype.storm.tuple.Tuple;  
  37. import backtype.storm.tuple.Values;  
  38.   
  39. public class DistributeWordTopology {  
  40.       
  41.      public static class KafkaWordToUpperCase extends BaseRichBolt {  
  42.   
  43.           private static final Log LOG = LogFactory.getLog(KafkaWordToUpperCase.class);  
  44.           private static final long serialVersionUID = -5207232012035109026L;  
  45.           private OutputCollector collector;  
  46.            
  47.           @Override  
  48.           public void prepare(Map stormConf, TopologyContext context,  
  49.                     OutputCollector collector) {  
  50.                this.collector = collector;                
  51.           }  
  52.   
  53.           @Override  
  54.           public void execute(Tuple input) {  
  55.                String line = input.getString(0).trim();  
  56.                LOG.info("RECV[kafka -> splitter] " + line);  
  57.                if(!line.isEmpty()) {  
  58.                     String upperLine = line.toUpperCase();  
  59.                     LOG.info("EMIT[splitter -> counter] " + upperLine);  
  60.                     collector.emit(input, new Values(upperLine, upperLine.length()));  
  61.                }  
  62.                collector.ack(input);  
  63.           }  
  64.   
  65.           @Override  
  66.           public void declareOutputFields(OutputFieldsDeclarer declarer) {  
  67.                declarer.declare(new Fields("line""len"));           
  68.           }  
  69.            
  70.      }  
  71.       
  72.      public static class RealtimeBolt extends BaseRichBolt {  
  73.   
  74.           private static final Log LOG = LogFactory.getLog(KafkaWordToUpperCase.class);  
  75.           private static final long serialVersionUID = -4115132557403913367L;  
  76.           private OutputCollector collector;  
  77.            
  78.           @Override  
  79.           public void prepare(Map stormConf, TopologyContext context,  
  80.                     OutputCollector collector) {  
  81.                this.collector = collector;                
  82.           }  
  83.   
  84.           @Override  
  85.           public void execute(Tuple input) {  
  86.                String line = input.getString(0).trim();  
  87.                LOG.info("REALTIME: " + line);  
  88.                collector.ack(input);  
  89.           }  
  90.   
  91.           @Override  
  92.           public void declareOutputFields(OutputFieldsDeclarer declarer) {  
  93.                 
  94.           }  
  95.   
  96.      }  
  97.   
  98.      public static void main(String[] args) throws AlreadyAliveException, InvalidTopologyException, InterruptedException {  
  99.   
  100.           // Configure Kafka  
  101.           String zks = "h40:2181,h41:2181,h42:2181";  
  102.           String topic = "test";  
  103.           String zkRoot = "/storm"// default zookeeper root configuration for storm  
  104.           String id = "word";  
  105.           BrokerHosts brokerHosts = new ZkHosts(zks,"/kafka/brokers");  
  106.           SpoutConfig spoutConf = new SpoutConfig(brokerHosts, topic, zkRoot, id);  
  107.           spoutConf.scheme = new SchemeAsMultiScheme(new StringScheme());  
  108.           spoutConf.forceFromStart = false;  
  109.           spoutConf.zkServers = Arrays.asList(new String[] {"h40""h41""h42"});  
  110.           spoutConf.zkPort = 2181;  
  111.            
  112.           // Configure HDFS bolt  
  113.           RecordFormat format = new DelimitedRecordFormat()  
  114.                   .withFieldDelimiter("\t"); // use "\t" instead of "," for field delimiter  
  115.           SyncPolicy syncPolicy = new CountSyncPolicy(1000); // sync the filesystem after every 1k tuples  
  116.           FileRotationPolicy rotationPolicy = new TimedRotationPolicy(1.0f, TimeUnit.MINUTES); // rotate files  
  117.           FileNameFormat fileNameFormat = new DefaultFileNameFormat()  
  118.                   .withPath("/storm/").withPrefix("app_").withExtension(".log"); // set file name format  
  119.           HdfsBolt hdfsBolt = new HdfsBolt()  
  120.                   .withFsUrl("hdfs://h40:9000")  
  121.                   .withFileNameFormat(fileNameFormat)  
  122.                   .withRecordFormat(format)  
  123.                   .withRotationPolicy(rotationPolicy)  
  124.                   .withSyncPolicy(syncPolicy);  
  125.            
  126.           // configure & build topology  
  127.           TopologyBuilder builder = new TopologyBuilder();  
  128.           builder.setSpout("kafka-reader"new KafkaSpout(spoutConf), 5);  
  129.           builder.setBolt("to-upper"new KafkaWordToUpperCase(), 3).shuffleGrouping("kafka-reader");  
  130.           builder.setBolt("hdfs-bolt", hdfsBolt, 2).shuffleGrouping("to-upper");  
  131.           builder.setBolt("realtime"new RealtimeBolt(), 2).shuffleGrouping("to-upper");  
  132.            
  133.           // submit topology  
  134.           Config conf = new Config();  
  135.           String name = DistributeWordTopology.class.getSimpleName();  
  136.           if (args != null && args.length > 0) {  
  137.                String nimbus = args[0];  
  138.                conf.put(Config.NIMBUS_HOST, nimbus);  
  139.                conf.setNumWorkers(3);  
  140.                StormSubmitter.submitTopologyWithProgressBar(name, conf, builder.createTopology());  
  141.           } else {  
  142.                conf.setMaxTaskParallelism(3);  
  143.                LocalCluster cluster = new LocalCluster();  
  144.                cluster.submitTopology(name, conf, builder.createTopology());  
  145.                Thread.sleep(60000);  
  146.                cluster.shutdown();  
  147.           }  
  148.      }  
  149.   
  150. }  

先运行flume进程:
[hadoop@h40 apache-flume-1.6.0-bin]$ bin/flume-ng agent -c . -f conf/kafka.conf -n a1 -Dflume.root.logger=INFO,console
[hadoop@h40 ~]$ echo "hello world" >> data.txt


上面代码中,名称为to-upper的Bolt将接收到的字符串行转换成大写以后,会将处理过的数据向后面的hdfs-bolt、realtime这两个Bolt各发一份拷贝,然后由这两个Bolt分别根据实际需要(实时/离线)单独处理。
打包后,在Storm集群上部署并运行这个Topology:
[hadoop@h40 apache-storm-0.9.5]$ bin/storm jar wordcount.jar org.shirdrn.storm.examples.DistributeWordTopology h40
在kafka生产者输入数据(一开始也不知道我输入的数据少还是咋么的,在hdfs的/storm目录下产生了一些空文件,然后我在生产者终端狂输数据,这才在hdfs中产生了又内容的文件)
[hadoop@h40 ~]$ hadoop fs -lsr /

[plain] view plain copy
  1. lsr: DEPRECATED: Please use 'ls -R' instead.  
  2. drwxr-xr-x   - hadoop supergroup          0 2017-05-10 14:36 /aaa  
  3. drwxr-xr-x   - hadoop supergroup          0 2017-05-11 15:37 /storm  
  4. -rw-r--r--   3 hadoop supergroup          0 2017-05-11 15:33 /storm/app_hdfs-bolt-5-0-1494487931066.log  
  5. -rw-r--r--   3 hadoop supergroup          0 2017-05-11 15:34 /storm/app_hdfs-bolt-5-1-1494487991779.log  
  6. -rw-r--r--   3 hadoop supergroup         37 2017-05-11 15:35 /storm/app_hdfs-bolt-5-2-1494488051772.log  
  7. -rw-r--r--   3 hadoop supergroup          0 2017-05-11 15:36 /storm/app_hdfs-bolt-5-3-1494488111772.log  
  8. -rw-r--r--   3 hadoop supergroup         12 2017-05-11 15:37 /storm/app_hdfs-bolt-5-4-1494488171771.log  
  9. -rw-r--r--   3 hadoop supergroup          0 2017-05-11 15:37 /storm/app_hdfs-bolt-5-5-1494488231941.log  

[hadoop@h40 ~]$ hadoop fs -cat /storm/app_hdfs-bolt-5-2-1494488051772.log
DFAD    4
FAF     3
GWEG    4
HELLO FALWREW   13


参考:

http://shiyanjun.cn/archives/934.html
http://www.cnblogs.com/smartloli/p/4615908.html
http://blog.csdn.Net/liubiaoxin/article/details/49231731
http://blog.csdn.net/mylittlered/article/details/48029705
http://www.iyunv.com/thread-26520-1-1.html

原创粉丝点击