转载自:
https://blog.youkuaiyun.com/dslztx/article/details/46687833
https://blog.youkuaiyun.com/dslztx/article/details/46691841
https://blog.youkuaiyun.com/weijonathan/article/details/18792719
Topology
"storm jar"
命令用于启动一个Topology。storm jar
设置的环境变量会提供给StormSubmiiter
后续使用。在代码中调用StormSubmitter#submitTopology
或者StormSubmitter#submitTopologyWithProgressBar
时,最终都会调用StormSubmitter#submitTopologyAs
。
在submitTopologyAs方法中,首先是加载配置:
if(!Utils.isValidConf(stormConf)) {
throw new IllegalArgumentException("Storm conf is not valid. Must be json-serializable");
}
stormConf = new HashMap(stormConf);
stormConf.putAll(Utils.readCommandLineOpts());
Map conf = Utils.readStormConfig();
conf.putAll(stormConf);
stormConf.putAll(prepareZookeeperAuthentication(conf));
validateConfs(conf, topology);
先检查拓扑传进来的stormConf
是否有效,能否json化,然后将其转换为HashMap,这里的stormConf
是用户在建立拓扑创建的;然后将命令行中的参数加载进stormConf
中;
调用readStormConfig
,加载配置文件中的内容,先加载defaults.yaml
, 然后再加载storm.yaml
,并合并stormConf
中的配置到conf
中;
最后,加载zk认证相关信息,并校验配置项。
当配置准备好以后,就开始向nimbus提交拓扑。在storm中,nimbus是一个thrift服务器,它接受客户端通过json文件提交RPC调用,即NimbusClient
向nimbus提供一份json格式的字符串,用于提交拓扑信息。
String serConf = JSONValue.toJSONString(stormConf);
try (NimbusClient client = NimbusClient.getConfiguredClientAs(conf, asUser)) {
if (topologyNameExists(name, client)) {
throw new RuntimeException("Topology with name `" + name + "` already exists on cluster");
}
// Dependency uploading only makes sense for distributed mode
List<String> jarsBlobKeys = Collections.emptyList();
List<String> artifactsBlobKeys;
DependencyUploader uploader = new DependencyUploader();
try {
uploader.init();
jarsBlobKeys = uploadDependencyJarsToBlobStore(uploader);
artifactsBlobKeys = uploadDependencyArtifactsToBlobStore(uploader);
} catch (Throwable e) {
// remove uploaded jars blobs, not artifacts since they're shared across the cluster
uploader.deleteBlobs(jarsBlobKeys);
uploader.shutdown();
throw e;
}
try {
setDependencyBlobsToTopology(topology, jarsBlobKeys, artifactsBlobKeys);
submitTopologyInDistributeMode(name, topology, opts, progressListener, asUser, conf, serConf, client);
} catch (AlreadyAliveException | InvalidTopologyException | AuthorizationException e) {
// remove uploaded jars blobs, not artifacts since they're shared across the cluster
// Note that we don't handle TException to delete jars blobs
// because it's safer to leave some blobs instead of topology not running
uploader.deleteBlobs(jarsBlobKeys);
throw e;
} finally {
uploader.shutdown();
}
}
先将配置文件改为json格式的string,然后获取Nimbus client对象,检查拓扑名称是否已经存在,将jar包上传BlobStore,调用submitTopologyInDistributeMode
正式向NimbusClient
提交请求:
private static void submitTopologyInDistributeMode(String name, StormTopology topology, SubmitOptions opts,
ProgressListener progressListener, String asUser, Map conf,
String serConf, NimbusClient client) throws TException {
try {
String jar = submitJarAs(conf, System.getProperty("storm.jar"), progressListener, client);
LOG.info("Submitting topology {} in distributed mode with conf {}", name, serConf);
Utils.addVersions(topology);
if (opts != null) {
client.getClient().submitTopologyWithOpts(name, jar, serConf, topology, opts);
} else {
// this is for backwards compatibility
client.getClient().submitTopology(name, jar, serConf, topology);
}
LOG.info("Finished submitting topology: {}", name);
} catch (InvalidTopologyException e) {
LOG.warn("Topology submission exception: {}", e.get_msg());
throw e;
} catch (AlreadyAliveException e) {
LOG.warn("Topology already alive exception", e);
throw e;
}
}
该方法中,先将jar包上传至nimbus,然后向nimbus提交拓扑。
Nimbus接受Topology提交时,Nimbus设置Topylogy状态为静态的,jar包和configs文件会放到到stormdist/{topology id}
这个目录下,然后写任务–>组件映射到Zookeeper,创建一个Zookeeper目录使得任务可以心跳。
然后Nimbus将任务分配给各个Supervisor节点机器,由Supervisor启动具体干活的Worker。一旦Topologies分配,会写入数据到zookeeper,以便集群知道Topology是活跃的和可以从spouts中emit元组(tuple)。
运行“storm kill”
这个命令,仅仅只是调用Nimbus的Thirft接口去kill掉相对应的Topology。
Nimbus接受到kill命令,会将”kill”事务应用到topology上,修改Topology的状态为”killed”以及将“remove”事件列入到未来几秒钟的计划中,即未来几秒后会触发remove时间。这里的kill实际上停止闲相关的Worker。
默认kill的等待时间是Topology消息的超时时间,但是可以通过storm kill
命令中的-w
标志对其进行重写,设置了以上参数之后,topology会在指定的等待时间停止运行。这样给了Topology一个机会在shutdown workers之后完成当前没有处理完成的任务;删除Topology以及清理zookeeper中的分配信息和静态信息;清理存储在本地的心跳dir和jar/configs。
Spout与Bolt
在定义Topology实例过程中,就指定了Spout实例和Bolt实例。
在提交Topology实例给Nimbus的过程中,会调用TopologyBuilder
实例的createTopology()
方法,以获取定义的Topology实例。
在实例化Topology的过程中,就会实例化Spout
和Bolt
(在Nimbus机器上实例化的),并序列化。会调用Spout
和Bolt
实例上的declareOutputFields()
方法和getComponentConfiguration()
方法。declareOutputFields()
方法配置Spout和Bolt实例的输出,getComponentConfiguration()
方法输出特定于Spout和Bolt实例的配置参数值对。
在Worker Node上运行的thread,从Nimbus上复制序列化后得到的字节码文件,从中反序列化得到Spout和Bolt实例,实例的输出配置和实例的配置参数值对等数据,在thread中Spout和Bolt实例的declareOutputFields()
和getComponentConfiguration()
不会再运行。
在thread中,反序列化得到一个Spout实例后,它会先运行Spout实例的open()
方法,在这个方法调用中,需要传入一个SpoutOutputCollector
实例,后面使用该SpoutOutputCollector
实例输出Tuple。然后Spout实例处于 deactivated 模式,过段时间会变成 activated 模式,此时会调用Spout实例的activate()
方法。接下来在该thread中按配置数量建立task集合,在每个task中循环调用线程持有Spout实例的nextTuple()
,ack()
和fail()
方法。任务处理成功,调用ack()
;任务处理失败,调用fail()
。
在运行过程中,如果发送一个失效命令给Worker,那么Worker所持有的Spout实例会变成处于 deactivated 模式,并且会调用Spout实例的deactivate()
方法。在关闭一个Worker时,Worker所持有的Spout实例会调用close()
方法。不过如果是强制关闭,这个close()
方法有可能不会被调用到。
而对于Bolt,反序列化得到一个Bolt实例后,它会先运行Bolt实例的prepare()
方法,在这个方法调用中,需要传入一个OutputCollector
实例,后面使用该OutputCollector
实例输出Tuple。接下来在该thread中按照配置数量建立task集合,然后在每个task中就会循环调用thread所持有Bolt实例的execute()
方法。
一般初始化操作应该在prepare/open
方法中进行,而不是在实例化的时候进行。