原本打算将storm直接与flume直连,发现相应组件支持比较弱,topology任务对应的supervisor也不一定在哪个节点上,只能采用统一的分布式消息服务Kafka。
 
原本打算将结构设置为:


 
最后结构更改为:
 


  

集成Kafka

 
storm中已经写好了KafkaSpout用来接收Kafka中间件上的消息,并发射到Bolt中,只需要依赖 storm-kafka即可:
 
  1. <dependency>
  2. <groupId>org.apache.storm</groupId>
  3. <artifactId>storm-kafka</artifactId>
  4. <version>${storm.version}</version>
  5. </dependency>
 
调用org.apache.storm.kafka.KafkaSpout,  需要传递一个SpoutConfig用来配置kafka对应的zookeeper以及topic:
 
  1. String zks = "192.168.1.1xx:2181,192.168.1.1xx:2181,192.168.1.1xx:2181/kafka";
  2. String topic = "log-storm";
  3.  
  4. BrokerHosts brokerHosts = new ZkHosts(zks);
  5. SpoutConfig spoutConfig = new SpoutConfig(brokerHosts, topic, "/"+ topic, UUID.randomUUID().toString());
  6. spoutConfig.scheme = new SchemeAsMultiScheme(new StringScheme());
  7. spoutConfig.zkServers = Arrays.asList("192.168.1.1xx","192.168.1.1xx","192.168.1.1xx");
  8. spoutConfig.zkPort = 2181;
 
建立KafkaSpout(spoutConfig)即可。
 
需要注意的是,我们在Bolt中需要对收到的消息进行主动ack/fail,否则会出现消息重复发送的情况,一般情况下Bolt的写法类似下面,在prepare中缓存collector,executor中通过try/catch块决定是否确认消息(以通知Spout是否需要对消息进行重发),declareOutputFields中声明需要输出的字段。
 
  1. private OutputCollector collector;
  2.  
  3. @Override
  4. public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
  5. this.collector = collector;
  6. }
  7.  
  8. @Override
  9. public void execute(Tuple input) {
  10. try {
  11. String msgBody = input.getString(0);
  12. int traceIndex = msgBody.indexOf(TRACE_CONST);
  13. if (traceIndex >= 0) {
  14. String completeLog = msgBody.substring(traceIndex + TRACE_CONST.length());
  15. collector.emit(new Values(completeLog));
  16. }
  17. collector.ack(input);
  18. } catch (Exception e) {
  19. collector.fail(input);
  20. }
  21. }
  22.  
  23. @Override
  24. public void declareOutputFields(OutputFieldsDeclarer declarer) {
  25. declarer.declare(new Fields("log"));
  26. }
 
Storm中需要有一个main函数,用于构建和启动topology,以便将spout,bolt等组件连接起来,代码类似下面:
 
  1. TopologyBuilder builder = new TopologyBuilder();
  2. builder.setSpout("kafka-reader", new KafkaSpoutNoMetrics(spoutConfig), 3);
  3.  
  4. builder.setBolt("log-extractor", new LogExtractorBolt(), 2).shuffleGrouping("kafka-reader");
  5. builder.setBolt("log-splitter", new LogSplitterBolt(), 2).shuffleGrouping("log-extractor");
  6. builder.setBolt("memcached-store", new MemcachedBolt()).fieldsGrouping("log-splitter", new Fields("md"));
  7.  
  8. Config config = new Config();
  9. String name = "LogStormProcessor";
  10.  
  11. config.setNumWorkers(1);
  12. StormSubmitter.submitTopologyWithProgressBar(name, config, builder.createTopology());
 
 

使用Storm Trident

 
Trident在Storm上进行了高级抽象,例如事务处理和状态管理的细节,可以让一批tuple进行离散的事务处理,还允许topology在数据上执行函数功能、过滤和聚合操作。
 


 
 
 
使用Trident,我们需要使用TridentTopology替换原有的TopologyBuilder构造Storm的拓扑图。在Trident中的spout引入了数据批次(batch)的概念,不像Strom中的spout,Trident Spout必须成批地发送tuple。
 
在Trident中,spout并没有真正发射tuple,而是把这项工作分解给了BatchCoordinator和Emitter方法,Emitter方法负责发送tuple,BatchCoordinator负责管理批次和元数据,Emitter需要依靠元数据来恰当地进行批次的数据重放。
 
首先,需要根据ITridentSpout新建一个数据流,
 

  1. Stream stream = tridentTopology.newStream("event", kafkaSpout);
在使用KafkaSpout作为TridentSpout时,其默认的输出字段名称为str,
 
 
  1. Exception in thread "main" java.lang.IllegalArgumentException: Trying to select non-existent field: 'event' from stream containing fields fields: <[str]>
  2. at org.apache.storm.trident.Stream.projectionValidation(Stream.java:853)
  3. at org.apache.storm.trident.Stream.each(Stream.java:320)
  4. at com.zhen.log.processor.trident.Main.main(Main.java:48)
 
我们原来使用的KafkaSpout,虽然可以将其直接用于newStream方法,但是运行时会出现错误:
 


 
 
原因就在于进行适配的过程中,注册方法registerMetric只能够被宰ISpout::open()方法中被调用,虽然可以进行合理地改造(由于有一些包访问控制权限的相关依赖,新建一个同名package,并将其中的registerMetric方法删除),但是其事务性不能得到保证,在本人测试的过程中,Kafka的消息不能被正常消费,每次重启服务都会读到完整的所有数据。
 
但是这并不是推荐的用法,storm-kafka中存在另外一个实现:org.apache.storm.kafka.trident.OpaqueTridentKafkaSpout,可以满足要求:
 
  1. TridentKafkaConfig kafkaConfig = new TridentKafkaConfig(brokerHosts, topic);
  2. OpaqueTridentKafkaSpout kafkaSpout = new OpaqueTridentKafkaSpout(kafkaConfig);
  3.  
  4. TridentTopology tridentTopology = new TridentTopology();
  5. Stream stream = tridentTopology.newStream("event", kafkaSpout);
 
而使用OpaqueTridentKafkaSpout时,默认的输出名称为“bytes”,其输出格式也并不是String字符串而是byte[],需要根据编码格式将其转换为String。
 
在Spout编写完成后,就可以加入后续的运算操作,trident处理是通过创建Stream的各种operation并连接来进行处理的,比较常用的两种运算:filter和function,例如我们下面处理流的方式,每次返回Stream都可以继续用来创建新的数据流:
 
  1. Stream logStream = stream.each(new Fields("bytes"), new LogExtractorFunction(), new Fields("log"))
  2. .each(new Fields("log"), new LogSplitterFunction(), new Fields("logObject"))
  3. .each(new Fields("logObject"), new LogTypeFilter("TRACE"));
在filter中,继承自BaseFilter,唯一的isKeep方法会根据tuple的属性进行相应过滤操作,需要指定对应输入的Field,filter没有额外输出的多余字段。注意filter中不能改变tuple,如果既想要过滤又想添加字段时必须使用function。
 
在function中,继承自BaseFunction,通过execute方法来对所有的数据增加额外的字段,并不会删除或者变更已有的字段。使用function需要指定多余输出的Fields,function中发射的字段数要与声明的fields字段数据保持一致。
 
和function比较类似,aggregator允许topology组合tuple,不同的是,它会替换tuple字段和值,有三种聚合器可以被使用:CombinerAggregator,ReducerAggregator和Aggregator。这里,我们使用的是CombinerAggregator。
 
CombinerAggregator用来将一个集合的tuple组合到一个单独到一个单独的字段中,定义如下:
 
  1. public interface CombinerAggregator<T> extends Serializable {
  2. T init(TridentTuple tuple);
  3. T combine(T val1, T val2);
  4. T zero();
  5. }
 
Storm会对每个tuple调用init方法,然后重复调用combiner方法指导一个分片的数据处理完成,传递给combine方法的两个参数是局部聚合的结果,以及调用了init返回的值,如果没有聚合结果,会直接调用zero方法返回一个自定义空值。
 
聚合一般需要首先对数据流进行groupBy操作后,在GroupedStream流上进行实际操作,一般情况下,首先根据前面的流输出一个用于分组的键值用于groupBy,然后进行persistentAggregate,根据分组将数据归并计算合并结果。
 
  1. logStream
  2. .each(new Fields("logObject"), new LogGroupFunction(), new Fields("key")).groupBy(new Fields("key"))
  3. .persistentAggregate(MemcachedState.nonTransactional(servers), new Fields("logObject"), new LogCombinerAggregator(),
  4. new Fields("statistic"))
 
 
注意,使用Trident时也是可以分成多个流的,只需要在特定的节点上,保存本地变量,就可以在其上执行多次each,分出多条路径流进行独立处理(也可以对多条输入流进行合并,这里没有使用到这样高级的功能)。
 
  1. Stream logStream = stream.each(new Fields("bytes"), new LogExtractorFunction(), new Fields("log"))
  2. .each(new Fields("log"), new LogSplitterFunction(), new Fields("logObject"))
  3. .each(new Fields("logObject"), new LogTypeFilter("TRACE"));
  4.  
  5. logStream.each(new Fields("log"), new LocalFileSaveFunction(), new Fields());
  6.  
  7. logStream
  8. .each(new Fields("logObject"), new LogGroupFunction(), new Fields("key")).groupBy(new Fields("key"))
  9. .persistentAggregate(MemcachedState.nonTransactional(servers), new Fields("logObject"), new LogCombinerAggregator(),
  10. new Fields("statistic"))
  11. ;
 
在使用任何function,aggregator时,都可以通过声明Fields的方式来设置使用到的字段名称,Combiner中可以不使用任何定义的Fields,此时传递给Trident的Tuple中将不会包含任何字段(一般代码示例中如此)。使用聚合时,还需要持续存储聚合的Trident状态,持久化操作从状态管理开始,Trident对状态有底层的操作原语,可以参考State接口的方法。
 
Storm中用State来持久化存储信息,有三种状态类型:非事务型,重复事务型以及不透明事务型,在分布式环境下,数据可能被重放,为了支持计数和状态更新,Trident将状态更新操作进行序列化,使用不同的状态更新模式对重放和错误数据进行容错。
 
我们存储中间数据状态使用了memcached作为媒介,关于trident与memcached进行事务处理相关代码,可以参考工程(storm创建者编写)
 
 
其中调用了twitter中定义的所以使用改造过的客户端:
  1. <dependency>
  2. <groupId>com.twitter</groupId>
  3. <artifactId>finagle-memcached_2.9.2</artifactId>
  4. <version>6.20.0</version>
  5. </dependency>
但是将源码copy到工程中并添加对应的maven依赖(多数是twitter相关的依赖),其中的twitter依赖始终找不到:
 
  1. [INFO] ------------------------------------------------------------------------
  2. [ERROR] Failed to execute goal on project log-storm-processor: Could not resolve dependencies for project com.zhen:log-storm-processor:jar:1.0.0-SNAPSHOT: The following artifacts could not be resolved: com.twitter.common.zookeeper:server-set:jar:1.0.83, com.twitter.common.zookeeper:client:jar:0.0.60, com.twitter.common.zookeeper:group:jar:0.0.78: Failure to find com.twitter.common.zookeeper:server-set:jar:1.0.83 in http://192.168.1.14:8081/nexus/content/repositories/releases/ was cached in the local repository, resolution will not be reattempted until the update interval of nexus-releases has elapsed or updates are forced -> [Help 1]
  3. [ERROR]
  4. [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
 
因此只能将其进行改造,使用com.whalin对应的memcached客户端jar包,以满足从Storm存储到memcached的需求。
 
storm trident的结构:
 


 
 
 
storm中还存在其他的工具,Storm-contribute项目地址:https://github.com/nathanmarz/storm-contrib,同样是作者所写,加入了支持Redis,Kafka,MongoDB等数据源。

Storm集成Kafka的Trident实现的更多相关文章

  1. storm集成kafka的应用,从kafka读取,写入kafka

    storm集成kafka的应用,从kafka读取,写入kafka by 小闪电 0前言 storm的主要作用是进行流式的实时计算,对于一直产生的数据流处理是非常迅速的,然而大部分数据并不是均匀的数据流 ...

  2. Storm集成Kafka应用的开发

    我们知道storm的作用主要是进行流式计算,对于源源不断的均匀数据流流入处理是非常有效的,而现实生活中大部分场景并不是均匀的数据流,而是时而多时而少的数据流入,这种情况下显然用批量处理是不合适的,如果 ...

  3. storm集成kafka

    kafkautil: import java.util.Properties; import kafka.javaapi.producer.Producer; import kafka.produce ...

  4. Storm 学习之路(九)—— Storm集成Kafka

    一.整合说明 Storm官方对Kafka的整合分为两个版本,官方说明文档分别如下: Storm Kafka Integration : 主要是针对0.8.x版本的Kafka提供整合支持: Storm ...

  5. Storm 系列(九)—— Storm 集成 Kafka

    一.整合说明 Storm 官方对 Kafka 的整合分为两个版本,官方说明文档分别如下: Storm Kafka Integration : 主要是针对 0.8.x 版本的 Kafka 提供整合支持: ...

  6. Storm集成Kafka编程模型

    原创文章,转载请注明: 转载自http://www.cnblogs.com/tovin/p/3974417.html 本文主要介绍如何在Storm编程实现与Kafka的集成 一.实现模型 数据流程: ...

  7. 5、Storm集成Kafka

    1.pom文件依赖 <!--storm相关jar --> <dependency> <groupId>org.apache.storm</groupId> ...

  8. Storm应用系列之——集成Kafka

    本文系原创系列,转载请注明. 原帖地址:http://blog.csdn.net/xeseo 前言 在前面Storm系列之——基本概念一文中,提到过Storm的Spout应该是源源不断的取数据,不能间 ...

  9. spark streaming集成kafka

    Kakfa起初是由LinkedIn公司开发的一个分布式的消息系统,后成为Apache的一部分,它使用Scala编写,以可水平扩展和高吞吐率而被广泛使用.目前越来越多的开源分布式处理系统如Clouder ...

随机推荐

  1. 解决:编辑一条彩信,附件选择添加音频,返回到编辑界面选择play,不能播放,没有声音

    [操作步骤]:编辑一条彩信,附件选择添加音频(外部音频),返回到编辑界面选择play,菜单键选择view slideshow [测试结果]:不能播放,没有声音 [预期结果]:可以播放 根据以往的经验( ...

  2. [HTML]去除li前面的小黑点,和ul、LI部分属性

    [转] 对于很多人用div来做网站时,总会用到,但在显示效果时前面总是会有一个小黑点,这个令很多人头痛,但又找不到要源,其它我们可以用以下方法来清除.[HTML]去除li前面的小黑点,和ul.LI部分 ...

  3. David Silver 强化学习原理 (中文版 链接)

    教程的在线视频链接: http://www.bilibili.com/video/av9831889/ 全部视频链接: https://space.bilibili.com/74997410/vide ...

  4. how to check CAN frame

    1. check buffer size getsockopt(s, SOL_SOCKET, SO_SNDBUF,&snd_size, &optlen); setsockopt(s, ...

  5. BZOJ4899: 记忆的轮廓【概率期望DP】【决策单调性优化DP】

    Description 通往贤者之塔的路上,有许多的危机. 我们可以把这个地形看做是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增, 在[1,n]中,一共有n个节点.我 ...

  6. dubbo监控工具

    现在企业使用dubbo技术太普遍,所以dubbo的监控工具也应运而生,而且还很稳定,他就是一个web项目, 部署起来非常简单,下载个项目war包(dubbo-admin-2.5.4-SNAPSHOT. ...

  7. 51Nod:1268 和为K的组合

    1268 和为K的组合  基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题  收藏  关注 给出N个正整数组成的数组A,求能否从中选出若干个,使他们的和为K.如果可以 ...

  8. 记录几个ubuntu环境下的php相关的路径

    php路径 /usr/bin/php phpize5 /usr/bin/phpize5 php5-fpm /usr/sbin/php5-fpm php所有的配置文件 /etc/php5/fpm 重启p ...

  9. 【BZOJ2683】简单题

    cdq分治妙啊 (被改过题面的)原题: dydxh所出的题目是这样的:有一个N*N矩阵,给出一系列的修改和询问,修改是这样的:将(x,y)中的数字加上k,而询问是这样的:求(x1,y1)到(x2,y2 ...

  10. apache spark kubernets 部署试用

    spark 是一个不错的平台,支持rdd 分析stream 机器学习... 以下为使用kubernetes 部署的说明,以及注意的地方 具体的容器镜像使用别人已经构建好的 deploy yaml 文件 ...