基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_reading/canal
本文将对canal的启动模块deployer进行分析。
Deployer模块(绿色部分)在整个系统中的角色如下图所示,用来启动canal-server.
模块内的类如下:
为了能带着目的看源码,以几个问题开头,带着问题来一起探索deployer模块的源码。
- CanalServer启动过程中配置如何加载?
- CanalServer启动过程中涉及哪些组件?
- 集群模式的canalServer,是如何实现instance的HA呢?
- 每个canalServer又是怎么获取admin上的配置变更呢?
1.入口类CanalLauncher
这个类是整个canal-server的入口类。负责配置加载和启动canal-server。
主流程如下:
- 加载canal.properties的配置内容
- 根据canal.admin.manager是否为空判断是否是admin控制,如果不是admin控制,就直接根据canal.properties的配置来了
- 如果是admin控制,使用PlainCanalConfigClient获取远程配置
- 新开一个线程池每隔五秒用http请求去admin上拉配置进行merge(这里依赖了instance模块的相关配置拉取的工具方法)
- 用md5进行校验,如果canal-server配置有更新,那么就重启canal-server
- 核心是用canalStarter.start()启动
- 使用CountDownLatch保持主线程存活
- 收到关闭信号,CDL-1,然后关闭配置更新线程池,优雅退出
public static void main(String[] args) {
try {
//note:设置全局未捕获异常的处理
setGlobalUncaughtExceptionHandler();
/**
* note:
* 1.读取canal.properties的配置
* 可以手动指定配置路径名称
*/
String conf = System.getProperty("canal.conf", "classpath:canal.properties");
Properties properties = new Properties();
if (conf.startsWith(CLASSPATH_URL_PREFIX)) {
conf = StringUtils.substringAfter(conf, CLASSPATH_URL_PREFIX);
properties.load(CanalLauncher.class.getClassLoader().getResourceAsStream(conf));
} else {
properties.load(new FileInputStream(conf));
}
final CanalStarter canalStater = new CanalStarter(properties);
String managerAddress = CanalController.getProperty(properties, CanalConstants.CANAL_ADMIN_MANAGER);
/**
* note:
* 2.根据canal.admin.manager是否为空判断是否是admin控制,如果不是admin控制,就直接根据canal.properties的配置来了
*/
if (StringUtils.isNotEmpty(managerAddress)) {
String user = CanalController.getProperty(properties, CanalConstants.CANAL_ADMIN_USER);
//省略一部分。。。。。。
/**
* note:
* 2.1使用PlainCanalConfigClient获取远程配置
*/
final PlainCanalConfigClient configClient = new PlainCanalConfigClient(managerAddress,
user,
passwd,
registerIp,
Integer.parseInt(adminPort),
autoRegister,
autoCluster);
PlainCanal canalConfig = configClient.findServer(null);
if (canalConfig == null) {
throw new IllegalArgumentException("managerAddress:" + managerAddress
+ " can't not found config for [" + registerIp + ":" + adminPort
+ "]");
}
Properties managerProperties = canalConfig.getProperties();
// merge local
managerProperties.putAll(properties);
int scanIntervalInSecond = Integer.valueOf(CanalController.getProperty(managerProperties,
CanalConstants.CANAL_AUTO_SCAN_INTERVAL,
"5"));
/**
* note:
* 2.2 新开一个线程池每隔五秒用http请求去admin上拉配置进行merge(这里依赖了instance模块的相关配置拉取的工具方法)
*/
executor.scheduleWithFixedDelay(new Runnable() {
private PlainCanal lastCanalConfig;
public void run() {
try {
if (lastCanalConfig == null) {
lastCanalConfig = configClient.findServer(null);
} else {
PlainCanal newCanalConfig = configClient.findServer(lastCanalConfig.getMd5());
/**
* note:
* 2.3 用md5进行校验,如果canal-server配置有更新,那么就重启canal-server
*/
if (newCanalConfig != null) {
// 远程配置canal.properties修改重新加载整个应用
canalStater.stop();
Properties managerProperties = newCanalConfig.getProperties();
// merge local
managerProperties.putAll(properties);
canalStater.setProperties(managerProperties);
canalStater.start();
lastCanalConfig = newCanalConfig;
}
}
} catch (Throwable e) {
//....
}
}
}, 0, scanIntervalInSecond, TimeUnit.SECONDS);
canalStater.setProperties(managerProperties);
} else {
canalStater.setProperties(properties);
}
canalStater.start();
//note: 这样用CDL处理和while(true)有点类似
runningLatch.await();
executor.shutdownNow();
} catch (Throwable e) {
//......
}
}