共计 2532 个字符,预计需要花费 7 分钟才能阅读完成。
这篇文章主要介绍“component 的生命周期怎么理解”,在日常操作中,相信很多人在 component 的生命周期怎么理解问题上存在疑惑,丸趣 TV 小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”component 的生命周期怎么理解”的疑惑有所帮助!接下来,请跟着丸趣 TV 小编一起来学习吧!
storm 中的 spout,bolt 的生命周期,都是 storm 帮你管理好,你只要新建一个实例,指定该 component 的并行数,就可以。那么 topology 中所用到的工具类的生命周期该如何处理,例如数据库连接池、redis 的连接池,这些对象该放在哪里初始化?本文以 jdbc 连接池为例,讨论此问题。阅读本文前请先自行了解 storm topology 的运行流程。
Storm Component 的生命周期
storm component 泛指 topoloty 中的 spout、bolt,是 spout 和 bolt 的一个统称。而 component 生命周期,我结合 storm 作者 nathanmarz 大神的回答和自己的理解,描述一下:
1,当提交了 topology 后,spout、bolt 的实例会在本地机器(执行 storm submit 所在的机器)上被创建,并且在本地序列化。另外,所有的 component 的构造函数、declareOutputFields 方法都本地会运行一次。
2,整个 topology 被上传到 nimbus 上。
3,对应 supervisor 会向 nimbus 获取序列化后的 topology 代码,交给对应的 worker 执行。
4,每个 worker 执行如下:反序列化代码,运行对应 component 的 prepare/open(初始化对应的 component)。举个例子,某 spout 在 topology 中设置了 3 个并行度,且 topology 设置了 3 个 worker,该 open 方法就会在 3 个不同的 worker 上分别调用 1 次,共调用 3 次。
5,worker 通知 executor 不断循环去跑 nextTuple/execute 方法。
需要注意一点:component 的初始化相关的操作应放在 prepare/open 方法中执行, 而不是在实例化 component 的时候进行或者 topology 的 main 函数。
Jdbc Pool 初始化方案
参考 component 的生命周期,能想到有两种方案:
1,每个 component 都维持一个与数据库的连接,此种方案适合于写操作非常频繁,component 数目比较少,且中间不会有一段时间没有任何写、读操作,否则会发生 mysql connection timeout;另外可省去从连接池获取连接,用完连接后返回给连接池这两步操作。如果确定这种方案,则只要将数据库的配置放到 config 中,在对应的 component 中初始化数据库连接即可。
2,在每 worker 中维持一个连接池,worker 中所有 executor 需要用到连接时,从连接池中获取,用后归还。一般还是推荐这种方案。
下面说一下第二种方案的连接池,应该在什么时候初始化。
首先明确一点,在 topology 运行中,一个 worker 相当于 jvm 进程,相当于有 N 个 worker,就会有 N 个连接池。初始化连接池的动作需要在 worker 端进行,一个 worker 中只有一个连接池的实例,适合用单例模式。
初始化思路:每个 component 接收数据库配置信息,在其 prepare/open 时,初始化连接池,这里需要保证在单个 worker 中只需要初始化一次就可以,即对初始化的代码加锁处理,保证只能有一个 component 初始化。
在 storm 源码包中,提供了 storm-jdbc 的实现,其中用了 JDBC 连接池组件,具体实现思路就是上面讨论的第二种方案。以下为初始化时加锁的部分。
public class HikariCPConnectionProvider implements ConnectionProvider {
private Map String, Object configMap;
private transient HikariDataSource dataSource;
public HikariCPConnectionProvider(Map String, Object hikariCPConfigMap) {
this.configMap = hikariCPConfigMap;
}
@Override
public synchronized void prepare() { if(dataSource == null) { Properties properties = new Properties();
properties.putAll(configMap);
HikariConfig config = new HikariConfig(properties);
this.dataSource = new HikariDataSource(config);
this.dataSource.setAutoCommit(false);
}
}
@Override
public Connection getConnection() {
try { return this.dataSource.getConnection();
} catch (SQLException e) { throw new RuntimeException(e);
}
}
@Override
public void cleanup() { if(dataSource != null) { dataSource.shutdown();
}
}
}
看到,初始化 prepare 被上锁了,以保证只初始化一次。其实这里可以再优化一下:不要对方法加锁,而是对具体初始化连接的代码加锁,减少锁影响的范围。
到此,关于“component 的生命周期怎么理解”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注丸趣 TV 网站,丸趣 TV 小编会继续努力为大家带来更多实用的文章!