package com.sxt.storm.test; import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.StormSubmitter; import backtype.storm.generated.AlreadyAliveException; import backtype.storm.generated.AuthorizationException; import backtype.storm.generated.InvalidTopologyException; import backtype.storm.generated.StormTopology; import backtype.storm.topology.TopologyBuilder; public class TestTopology { public static void main(String[] args) { TopologyBuilder topologyBuilder = new TopologyBuilder(); topologyBuilder.setSpout("myspout",new TestSpout()); topologyBuilder.setBolt("mybolt",new TestBolt()).shuffleGrouping("myspout"); Config conf = new Config(); // conf.setNumWorkers(4); StormTopology topology = topologyBuilder.createTopology(); if (args.length > 0) { try { StormSubmitter.submitTopology(args[0], conf, topology); } catch (AlreadyAliveException e) { e.printStackTrace(); } catch (InvalidTopologyException e) { e.printStackTrace(); } catch (AuthorizationException e) { e.printStackTrace(); } } else { LocalCluster localCluster = new LocalCluster(); localCluster.submitTopology("mytopology", conf, topology); } } }
下游mybolt衔接上游mysout,mybolt;可能还有下游,如mybolt1,那么mybolt1要衔接上游mybolt,以此类推
package com.sxt.storm.grouping; import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.StormSubmitter; import backtype.storm.generated.AlreadyAliveException; import backtype.storm.generated.AuthorizationException; import backtype.storm.generated.InvalidTopologyException; import backtype.storm.topology.TopologyBuilder; import backtype.storm.tuple.Fields; public class Main { /** * @param args */ public static void main(String[] args) { TopologyBuilder builder = new TopologyBuilder(); builder.setSpout("spout", new MySpout(), 2); // shuffleGrouping其实就是随机往下游去发,不自觉的做到了负载均衡 // builder.setBolt("bolt", new MyBolt(),2).shuffleGrouping("spout"); // fieldsGrouping其实就是MapReduce里面理解的Shuffle,根据fields求hash来取模 // builder.setBolt("bolt", new MyBolt(), 2).fieldsGrouping("spout", new Fields("session_id")); // 只往一个里面发,往taskId小的那个里面去发送 builder.setBolt("bolt", new MyBolt(), 2).globalGrouping("spout"); // 等于shuffleGrouping // builder.setBolt("bolt", new MyBolt(), 2).noneGrouping("spout"); // 广播 builder.setBolt("bolt", new MyBolt(), 5).allGrouping("spout"); // Map conf = new HashMap(); // conf.put(Config.TOPOLOGY_WORKERS, 4); Config conf = new Config(); conf.setDebug(false); conf.setMessageTimeoutSecs(30); if (args.length > 0) { try { StormSubmitter.submitTopology(args[0], conf, builder.createTopology()); } catch (AlreadyAliveException e) { e.printStackTrace(); } catch (InvalidTopologyException e) { e.printStackTrace(); } catch (AuthorizationException e) { e.printStackTrace(); } } else { LocalCluster localCluster = new LocalCluster(); localCluster.submitTopology("mytopology", conf, builder.createTopology()); } } }
美团Flume架构
http://tech.meituan.com/mt-log-system-arch.html
Flume的负载均衡
http://flume.apache.org/FlumeUserGuide.html#load-balancing-sink-processor
Task数量在整个Topology生命周期中保持不变,Executor数量可以变化或手动调整
(默认情况下,Task数量和Executor是相同的,即每个Executor线程中默认运行一个Task任务)
//设置Worker进程数 Config.setNumWorkers(int workers) //设置Executor线程数 TopologyBuilder.setSpout(String id, IRichSpout spout, Number parallelism_hint) TopologyBuilder.setBolt(String id, IRichBolt bolt, Number parallelism_hint) //其中, parallelism_hint即为executor线程数 //设置Task数量 ComponentConfigurationDeclarer.setNumTasks(Number val) //例: Config conf = new Config() ; conf.setNumWorkers(2); TopologyBuilder topologyBuilder = new TopologyBuilder(); topologyBuilder.setSpout("spout", new MySpout(), 1); topologyBuilder.setBolt("green-bolt", new GreenBolt(), 2) .setNumTasks(4) .shuffleGrouping("blue-spout);
Rebalance – 再平衡
即,动态调整Topology拓扑的Worker进程数量、以及Executor线程数量
支持两种调整方式:
1、通过Storm UI
2、通过Storm CLI
通过Storm CLI动态调整:
例:storm rebalance mytopology -n 5 -e blue-spout=3 -e yellow-bolt=10
将mytopology拓扑worker进程数量调整为5个
“ blue-spout ” 所使用的线程数量调整为3个
“ yellow-bolt ”所使用的线程数量调整为10个
1、集群节点宕机
Nimbus服务器
单点故障?
非Nimbus服务器
故障时,该节点上所有Task任务都会超时,Nimbus会将这些Task任务重新分配到其他服务器上运行
2、进程挂掉
Worker
挂掉时,Supervisor会重新启动这个进程。如果启动过程中仍然一直失败,并且无法向Nimbus发送心跳,Nimbus会将该Worker重新分配到其他服务器上
Supervisor
无状态(所有的状态信息都存放在Zookeeper中来管理)
快速失败(每当遇到任何异常情况,都会自动毁灭)
Nimbus
无状态(所有的状态信息都存放在Zookeeper中来管理)
快速失败(每当遇到任何异常情况,都会自动毁灭)
3、消息的完整性
从Spout中发出的Tuple,以及基于他所产生Tuple(例如上个例子当中Spout发出的句子,以及句子当中单词的tuple等)
由这些消息就构成了一棵tuple树
当这棵tuple树发送完成,并且树当中每一条消息都被正确处理,就表明spout发送消息被“完整处理”,即消息的完整性
Acker – 消息完整性的实现机制
Storm的拓扑当中特殊的一些任务
负责跟踪每个Spout发出的Tuple的DAG(有向无环图)
storm 中有一个系统级别的组件是 acker,acker 追踪从 spout 发射出
的流 ID(msgId)在每一个 task 中生成的 tuple 是否完成。spout 或者 bolt
在处理完 tuple 后,都会告诉 acker 我已经处理完了该源 tuple(如
tupleId=1),如果 emit 一个 tuple 的话,同时会告诉 acker 我发射了一个
tuple(如 tupleId=2),如果在大量的高并发的消息的情况下,传统的在内存中
跟踪执行情况的方式,内存的开销会非常大,甚至内存溢出。acker 巧妙的利
用了 xor 的机制,只需要维护一个 msgId 的标记位即可,处理方法是 acker 在
初始的时候,对每个 msgId 初始化一个校验值 ack-val(为 0),在处理完 tuple
和 emit tuple 的时候,会先对这两个个值做 xor 操作,生成的中间值再和
acker 中的当前校验值 ack-val 做 xor 生成新的 ack-val 值,当所有的 tuple
都处理完成都得到确认,那么最后的 ack-val 自然就为 0 了(因为每一个
tuple,从 emit 到 ack 都是经过两次 xor 操作,所以最后的 结果为 0 可以由上
面的那个公式可以验证出来)。
1.storm 不存数据,负责计算
2.容错能力基于异或
a) Msg ->acker
b) Acker 状态
A xor A = 0.
A xor B…xor B xor A = 0,其中每一个操作数出现且仅出现两次。
storm 中使用的巧妙方法就是基于这个定理。具体过程是这样的:在 spout 中
系统会为用户指定的 message id 生成一个对应的 64 位整数,作为一个 root
id。root id 会传递给 acker 及后续的 bolt 作为该消息单元的唯一标识。同时
无论是 spout 还是 bolt 每次新生成一个 tuple 的时候,都会赋予该 tuple 一
个 64 位的整数的 id。Spout 发射完某个 message id 对应的源 tuple 之后,会
告知 acker 自己发射的 root id 及生成的那些源 tuple 的 id。而 bolt 呢,每
次接受到一个输入 tuple 处理完之后,也会告知 acker 自己处理的输入 tuple
的 id 及新生 成的那些 tuple 的 id。Acker 只需要对这些 id 做一个简单的异或
运算,就能判断出该 root id 对应的消息单元是否处理完成了。
http://www.tuicool.com/articles/vErmIb
分布式远程过程调用
DRPC 是通过一个 DRPC 服务端(DRPC server)来实现分布式 RPC 功能的。
DRPC Server 负责接收 RPC 请求,并将该请求发送到 Storm中运行的 Topology,等待接收 Topology 发送的处理结果,并将该结果返回给发送请求的客户端。
(其实,从客户端的角度来说,DPRC 与普通的 RPC 调用并没有什么区别。)
DRPC设计目的:
为了充分利用Storm的计算能力实现高密度的并行实时计算。
(Storm接收若干个数据流输入,数据在Topology当中运行完成,然后通过DRPC将结果进行输出。)
客户端通过向 DRPC 服务器发送待执行函数的名称以及该函数的参数来获取处理结果。实现该函数的拓扑使用一个DRPCSpout 从 DRPC 服务器中接收一个函数调用流。DRPC 服务器会为每个函数调用都标记了一个唯一的 id。随后拓扑会执行函数来计算结果,并在拓扑的最后使用一个名为 ReturnResults 的 bolt 连接到 DRPC 服务器,根据函数调用的 id 来将函数调用的结果返回。
方法1:
通过LinearDRPCTopologyBuilder (该方法也过期,不建议使用)
该方法会自动为我们设定Spout、将结果返回给DRPC Server等,我们只需要将Topology实现
方法2:
直接通过普通的拓扑构造方法TopologyBuilder来创建DRPC拓扑
需要手动设定好开始的DRPCSpout以及结束的ReturnResults
修改配置文件conf/storm.yaml
drpc.servers:
- "node1“
启动DRPC Server
bin/storm drpc &
通过StormSubmitter.submitTopology提交拓扑