repair issue #3401 (#3923)

* 解决OutAdapter单例导致的问题,如 1.多个es7 outAdapter的多线程问题(共享同一个es bulk request),2.多个es7 outAdapter共用一份es配置问题

* 1. 优化adapter和监听器之间的代码结构
2. 监听器只处理匹配的配置文件
3. bugfix-es adapter配置文件回调时漏写esVersion
理论上一个adapter实例对应一个config配置文件

Co-authored-by: agapple <jianghang.loujh@alibaba-inc.com>
This commit is contained in:
zhuchao941
2022-05-13 15:02:06 +08:00
committed by GitHub
parent b12945d4a9
commit 4315f203a2
16 changed files with 594 additions and 510 deletions

View File

@@ -1,16 +1,5 @@
package com.alibaba.otter.canal.client.adapter.kudu;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.otter.canal.client.adapter.OuterAdapter;
import com.alibaba.otter.canal.client.adapter.kudu.config.KuduMappingConfig;
import com.alibaba.otter.canal.client.adapter.kudu.config.KuduMappingConfigLoader;
@@ -20,8 +9,19 @@ import com.alibaba.otter.canal.client.adapter.kudu.service.KuduSyncService;
import com.alibaba.otter.canal.client.adapter.kudu.support.KuduTemplate;
import com.alibaba.otter.canal.client.adapter.support.Dml;
import com.alibaba.otter.canal.client.adapter.support.EtlResult;
import com.alibaba.otter.canal.client.adapter.support.FileName2KeyMapping;
import com.alibaba.otter.canal.client.adapter.support.OuterAdapterConfig;
import com.alibaba.otter.canal.client.adapter.support.SPI;
import com.alibaba.otter.canal.client.adapter.support.Util;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author liuyadong
@@ -35,8 +35,6 @@ public class KuduAdapter implements OuterAdapter {
private Map<String, KuduMappingConfig> kuduMapping = new ConcurrentHashMap<>(); // 文件名对应配置
private Map<String, Map<String, KuduMappingConfig>> mappingConfigCache = new ConcurrentHashMap<>(); // 库名-表名对应配置
private String dataSourceKey;
private KuduTemplate kuduTemplate;
private KuduSyncService kuduSyncService;
@@ -45,6 +43,8 @@ public class KuduAdapter implements OuterAdapter {
private Properties envProperties;
private OuterAdapterConfig configuration;
public Map<String, KuduMappingConfig> getKuduMapping() {
return kuduMapping;
}
@@ -56,36 +56,17 @@ public class KuduAdapter implements OuterAdapter {
@Override
public void init(OuterAdapterConfig configuration, Properties envProperties) {
this.envProperties = envProperties;
this.configuration = configuration;
Map<String, KuduMappingConfig> kuduMappingTmp = KuduMappingConfigLoader.load(envProperties);
// 过滤不匹配的key的配置,获取连接key,key为配置文件名称
kuduMappingTmp.forEach((key, mappingConfig) -> {
if ((mappingConfig.getOuterAdapterKey() == null && configuration.getKey() == null)
|| (mappingConfig.getOuterAdapterKey() != null && mappingConfig.getOuterAdapterKey()
.equalsIgnoreCase(configuration.getKey()))) {
kuduMapping.put(key, mappingConfig);
dataSourceKey = mappingConfig.getDataSourceKey();
}
kuduMappingTmp.forEach((key, config) -> {
addConfig(key, config);
});
// 判断目标字段是否为空
if (kuduMapping.isEmpty()) {
throw new RuntimeException("No kudu adapter found for config key: " + configuration.getKey());
}
for (Map.Entry<String, KuduMappingConfig> entry : kuduMapping.entrySet()) {
String configName = entry.getKey();
KuduMappingConfig mappingConfig = entry.getValue();
String k;
if (envProperties != null && !"tcp".equalsIgnoreCase(envProperties.getProperty("canal.conf.mode"))) {
k = StringUtils.trimToEmpty(mappingConfig.getDestination()) + "-"
+ StringUtils.trimToEmpty(mappingConfig.getGroupId()) + "_"
+ mappingConfig.getKuduMapping().getDatabase() + "-" + mappingConfig.getKuduMapping().getTable();
} else {
k = StringUtils.trimToEmpty(mappingConfig.getDestination()) + "_"
+ mappingConfig.getKuduMapping().getDatabase() + "-" + mappingConfig.getKuduMapping().getTable();
}
Map<String, KuduMappingConfig> configMap = mappingConfigCache.computeIfAbsent(k,
k1 -> new ConcurrentHashMap<>());
configMap.put(configName, mappingConfig);
}
Map<String, String> properties = configuration.getProperties();
String kudu_master = properties.get("kudu.master.address");
@@ -203,4 +184,60 @@ public class KuduAdapter implements OuterAdapter {
}
return null;
}
private void addSyncConfigToCache(String configName, KuduMappingConfig mappingConfig) {
String k;
if (envProperties != null && !"tcp".equalsIgnoreCase(envProperties.getProperty("canal.conf.mode"))) {
k = StringUtils.trimToEmpty(mappingConfig.getDestination()) + "-"
+ StringUtils.trimToEmpty(mappingConfig.getGroupId()) + "_"
+ mappingConfig.getKuduMapping().getDatabase() + "-" + mappingConfig.getKuduMapping().getTable();
} else {
k = StringUtils.trimToEmpty(mappingConfig.getDestination()) + "_"
+ mappingConfig.getKuduMapping().getDatabase() + "-" + mappingConfig.getKuduMapping().getTable();
}
Map<String, KuduMappingConfig> configMap = mappingConfigCache.computeIfAbsent(k,
k1 -> new ConcurrentHashMap<>());
configMap.put(configName, mappingConfig);
}
public boolean addConfig(String fileName, KuduMappingConfig config) {
if (match(config)) {
kuduMapping.put(fileName, config);
addSyncConfigToCache(fileName, config);
FileName2KeyMapping.register(getClass().getAnnotation(SPI.class).value(), fileName,
configuration.getKey());
return true;
}
return false;
}
public void updateConfig(String fileName, KuduMappingConfig config) {
if (config.getOuterAdapterKey() != null && !config.getOuterAdapterKey()
.equals(configuration.getKey())) {
// 理论上不允许改这个 因为本身就是通过这个关联起Adapter和Config的
throw new RuntimeException("not allow to change outAdapterKey");
}
kuduMapping.put(fileName, config);
addSyncConfigToCache(fileName, config);
}
public void deleteConfig(String fileName) {
kuduMapping.remove(fileName);
for (Map<String, KuduMappingConfig> configMap : mappingConfigCache.values()) {
if (configMap != null) {
configMap.remove(fileName);
}
}
FileName2KeyMapping.unregister(getClass().getAnnotation(SPI.class).value(), fileName);
}
private boolean match(KuduMappingConfig config) {
boolean sameMatch = config.getOuterAdapterKey() != null && config.getOuterAdapterKey()
.equalsIgnoreCase(configuration.getKey());
boolean prefixMatch = config.getOuterAdapterKey() == null && configuration.getKey()
.startsWith(StringUtils
.join(new String[]{Util.AUTO_GENERATED_PREFIX, config.getDestination(),
config.getGroupId()}, '-'));
return sameMatch || prefixMatch;
}
}

View File

@@ -1,23 +1,18 @@
package com.alibaba.otter.canal.client.adapter.kudu.monitor;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.otter.canal.client.adapter.config.YmlConfigBinder;
import com.alibaba.otter.canal.client.adapter.kudu.KuduAdapter;
import com.alibaba.otter.canal.client.adapter.kudu.config.KuduMappingConfig;
import com.alibaba.otter.canal.client.adapter.support.MappingConfigsLoader;
import com.alibaba.otter.canal.client.adapter.support.Util;
import java.io.File;
import java.util.Properties;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author liuyadong
@@ -41,7 +36,7 @@ public class KuduConfigMonitor {
File confDir = Util.getConfDirPath(adapterName);
try {
FileAlterationObserver observer = new FileAlterationObserver(confDir,
FileFilterUtils.and(FileFilterUtils.fileFileFilter(), FileFilterUtils.suffixFileFilter("yml")));
FileFilterUtils.and(FileFilterUtils.fileFileFilter(), FileFilterUtils.suffixFileFilter("yml")));
FileListener listener = new FileListener();
observer.addListener(listener);
fileMonitor = new FileAlterationMonitor(3000, observer);
@@ -83,9 +78,11 @@ public class KuduConfigMonitor {
return;
}
config.validate();
addConfigToCache(file, config);
logger.info("Add a new kudu mapping config: {} to canal adapter", file.getName());
boolean result = kuduAdapter.addConfig(file.getName(), config);
if (result) {
logger.info("Add a new kudu mapping config: {} to canal adapter",
file.getName());
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
@@ -113,10 +110,7 @@ public class KuduConfigMonitor {
return;
}
config.validate();
if (kuduAdapter.getKuduMapping().containsKey(file.getName())) {
deleteConfigFromCache(file);
}
addConfigToCache(file, config);
kuduAdapter.updateConfig(file.getName(), config);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
@@ -129,42 +123,12 @@ public class KuduConfigMonitor {
try {
if (kuduAdapter.getKuduMapping().containsKey(file.getName())) {
deleteConfigFromCache(file);
kuduAdapter.deleteConfig(file.getName());
logger.info("Delete a hbase mapping config: {} of canal adapter", file.getName());
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
/**
* 添加配置文件信息到缓存
*
* @param file
* @param config
*/
private void addConfigToCache(File file, KuduMappingConfig config) {
kuduAdapter.getKuduMapping().put(file.getName(), config);
Map<String, KuduMappingConfig> configMap = kuduAdapter.getMappingConfigCache()
.computeIfAbsent(StringUtils.trimToEmpty(config.getDestination()) + "."
+ config.getKuduMapping().getDatabase() + "." + config.getKuduMapping().getTable(),
k1 -> new HashMap<>());
configMap.put(file.getName(), config);
}
/**
* 从缓存中删除配置
*
* @param file 文件
*/
private void deleteConfigFromCache(File file) {
kuduAdapter.getKuduMapping().remove(file.getName());
for (Map<String, KuduMappingConfig> configMap : kuduAdapter.getMappingConfigCache().values()) {
if (configMap != null) {
configMap.remove(file.getName());
}
}
}
}
}