diff --git a/deployer/src/main/resources/example/instance.properties b/deployer/src/main/resources/example/instance.properties index fda81838..5ceccab6 100644 --- a/deployer/src/main/resources/example/instance.properties +++ b/deployer/src/main/resources/example/instance.properties @@ -41,6 +41,10 @@ canal.instance.enableDruid=false canal.instance.filter.regex=.*\\..* # table black regex canal.instance.filter.black.regex= +# table field filter(format: schema1.tableName1:field1/field2,schema2.tableName2:field1/field2) +#canal.instance.filter.field=test1.t_product:id/subject/keywords,test2.t_company:id/name/contact/ch +# table field black filter(format: schema1.tableName1:field1/field2,schema2.tableName2:field1/field2) +#canal.instance.filter.black.field=test1.t_product:subject/product_image,test2.t_company:id/name/contact/ch # mq config canal.mq.topic=example diff --git a/deployer/src/main/resources/spring/default-instance.xml b/deployer/src/main/resources/spring/default-instance.xml index 164546d9..effb9cac 100644 --- a/deployer/src/main/resources/spring/default-instance.xml +++ b/deployer/src/main/resources/spring/default-instance.xml @@ -98,6 +98,9 @@ + + + diff --git a/deployer/src/main/resources/spring/file-instance.xml b/deployer/src/main/resources/spring/file-instance.xml index 87fdc49f..05c1d04d 100644 --- a/deployer/src/main/resources/spring/file-instance.xml +++ b/deployer/src/main/resources/spring/file-instance.xml @@ -84,6 +84,10 @@ + + + + diff --git a/deployer/src/main/resources/spring/group-instance.xml b/deployer/src/main/resources/spring/group-instance.xml index 9f96801e..a792d90d 100644 --- a/deployer/src/main/resources/spring/group-instance.xml +++ b/deployer/src/main/resources/spring/group-instance.xml @@ -90,6 +90,10 @@ + + + + diff --git a/deployer/src/main/resources/spring/memory-instance.xml b/deployer/src/main/resources/spring/memory-instance.xml index d7289aa4..6d65ad44 100644 --- a/deployer/src/main/resources/spring/memory-instance.xml +++ b/deployer/src/main/resources/spring/memory-instance.xml @@ -81,6 +81,10 @@ + + + + diff --git a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/AbstractEventParser.java b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/AbstractEventParser.java index 409c15d2..74ffee02 100644 --- a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/AbstractEventParser.java +++ b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/AbstractEventParser.java @@ -2,7 +2,9 @@ package com.alibaba.otter.canal.parse.inbound; import java.io.IOException; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.atomic.AtomicBoolean; @@ -53,6 +55,12 @@ public abstract class AbstractEventParser extends AbstractCanalLifeCycle protected CanalEventFilter eventFilter = null; protected CanalEventFilter eventBlackFilter = null; + // 字段过滤 + protected String fieldFilter; + protected Map> fieldFilterMap; + protected String fieldBlackFilter; + protected Map> fieldBlackFilterMap; + private CanalAlarmHandler alarmHandler = null; // 统计参数 @@ -528,6 +536,31 @@ public abstract class AbstractEventParser extends AbstractCanalLifeCycle } heartBeatTimerTask = null; } + + /** + * 解析字段过滤规则 + */ + private Map> parseFieldFilterMap(String config) { + + Map> map = new HashMap>(); + + if (StringUtils.isNotBlank(config)) { + for (String filter : config.split(",")) { + if (StringUtils.isBlank(filter)) { + continue; + } + + String[] filterConfig = filter.split(":"); + if (filterConfig.length != 2) { + continue; + } + + map.put(filterConfig[0].trim().toUpperCase(), Arrays.asList(filterConfig[1].trim().toUpperCase().split("/"))); + } + } + + return map; + } public void setEventFilter(CanalEventFilter eventFilter) { this.eventFilter = eventFilter; @@ -655,4 +688,43 @@ public abstract class AbstractEventParser extends AbstractCanalLifeCycle this.serverId = serverId; } + public String getFieldFilter() { + return fieldFilter; + } + + public void setFieldFilter(String fieldFilter) { + this.fieldFilter = fieldFilter.trim(); + this.fieldFilterMap = parseFieldFilterMap(fieldFilter); + } + + public String getFieldBlackFilter() { + return fieldBlackFilter; + } + + public void setFieldBlackFilter(String fieldBlackFilter) { + this.fieldBlackFilter = fieldBlackFilter; + this.fieldBlackFilterMap = parseFieldFilterMap(fieldBlackFilter); + } + + /** + * 获取表字段过滤规则 + * @return + * key: schema.tableName + * value: 字段列表 + */ + public Map> getFieldFilterMap() { + return fieldFilterMap; + } + + /** + * 获取表字段过滤规则黑名单 + * @return + * key: schema.tableName + * value: 字段列表 + */ + public Map> getFieldBlackFilterMap() { + return fieldBlackFilterMap; + } + + } diff --git a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/AbstractMysqlEventParser.java b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/AbstractMysqlEventParser.java index 29a66fd2..fbde7790 100644 --- a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/AbstractMysqlEventParser.java +++ b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/AbstractMysqlEventParser.java @@ -50,6 +50,9 @@ public abstract class AbstractMysqlEventParser extends AbstractEventParser { if (eventBlackFilter != null && eventBlackFilter instanceof AviaterRegexFilter) { convert.setNameBlackFilter((AviaterRegexFilter) eventBlackFilter); } + + convert.setFieldFilterMap(getFieldFilterMap()); + convert.setFieldBlackFilterMap(getFieldBlackFilterMap()); convert.setCharset(connectionCharset); convert.setFilterQueryDcl(filterQueryDcl); @@ -90,6 +93,34 @@ public abstract class AbstractMysqlEventParser extends AbstractEventParser { } } } + + @Override + public void setFieldFilter(String fieldFilter) { + super.setFieldFilter(fieldFilter); + + // 触发一下filter变更 + if (binlogParser instanceof LogEventConvert) { + ((LogEventConvert) binlogParser).setFieldFilterMap(getFieldFilterMap()); + } + + if (tableMetaTSDB != null && tableMetaTSDB instanceof DatabaseTableMeta) { + ((DatabaseTableMeta) tableMetaTSDB).setFieldFilterMap(getFieldFilterMap()); + } + } + + @Override + public void setFieldBlackFilter(String fieldBlackFilter) { + super.setFieldBlackFilter(fieldBlackFilter); + + // 触发一下filter变更 + if (binlogParser instanceof LogEventConvert) { + ((LogEventConvert) binlogParser).setFieldBlackFilterMap(getFieldBlackFilterMap()); + } + + if (tableMetaTSDB != null && tableMetaTSDB instanceof DatabaseTableMeta) { + ((DatabaseTableMeta) tableMetaTSDB).setFieldBlackFilterMap(getFieldBlackFilterMap()); + } + } /** * 回滚到指定位点 diff --git a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/dbsync/LogEventConvert.java b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/dbsync/LogEventConvert.java index d022a255..a0c86fcf 100644 --- a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/dbsync/LogEventConvert.java +++ b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/dbsync/LogEventConvert.java @@ -8,7 +8,9 @@ import java.nio.charset.Charset; import java.sql.Types; import java.util.Arrays; import java.util.BitSet; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; @@ -86,6 +88,8 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar private volatile AviaterRegexFilter nameFilter; // 运行时引用可能会有变化,比如规则发生变化时 private volatile AviaterRegexFilter nameBlackFilter; + private Map> fieldFilterMap = new HashMap>(); + private Map> fieldBlackFilterMap = new HashMap>(); private TableMetaCache tableMetaCache; private Charset charset = Charset.defaultCharset(); @@ -584,6 +588,15 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar boolean tableError = false; // check table fileds count,只能处理加字段 boolean existRDSNoPrimaryKey = false; + //获取字段过滤条件 + List fieldList = null; + List blackFieldList = null; + + if (tableMeta != null) { + fieldList = fieldFilterMap.get(tableMeta.getFullName().toUpperCase()); + blackFieldList = fieldBlackFilterMap.get(tableMeta.getFullName().toUpperCase()); + } + if (tableMeta != null && columnInfo.length > tableMeta.getFields().size()) { if (tableMetaCache.isOnRDS()) { // 特殊处理下RDS的场景 @@ -650,10 +663,12 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar columnBuilder.setSqlType(Types.BIGINT); columnBuilder.setUpdated(false); - if (isAfter) { - rowDataBuilder.addAfterColumns(columnBuilder.build()); - } else { - rowDataBuilder.addBeforeColumns(columnBuilder.build()); + if (needField(fieldList, blackFieldList, columnBuilder.getName())) { + if (isAfter) { + rowDataBuilder.addAfterColumns(columnBuilder.build()); + } else { + rowDataBuilder.addBeforeColumns(columnBuilder.build()); + } } continue; } @@ -820,10 +835,12 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar && isUpdate(rowDataBuilder.getBeforeColumnsList(), columnBuilder.getIsNull() ? null : columnBuilder.getValue(), i)); - if (isAfter) { - rowDataBuilder.addAfterColumns(columnBuilder.build()); - } else { - rowDataBuilder.addBeforeColumns(columnBuilder.build()); + if (needField(fieldList, blackFieldList, columnBuilder.getName())) { + if (isAfter) { + rowDataBuilder.addAfterColumns(columnBuilder.build()); + } else { + rowDataBuilder.addBeforeColumns(columnBuilder.build()); + } } } @@ -958,6 +975,17 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar private boolean isRDSHeartBeat(String schema, String table) { return "mysql".equalsIgnoreCase(schema) && "ha_health_check".equalsIgnoreCase(table); } + + /** + * 字段过滤判断 + */ + private boolean needField(List fieldList, List blackFieldList, String columnName) { + if (fieldList == null || fieldList.isEmpty()) { + return blackFieldList == null || blackFieldList.isEmpty() || !blackFieldList.contains(columnName.toUpperCase()); + } else { + return fieldList.contains(columnName.toUpperCase()); + } + } public static TransactionBegin createTransactionBegin(long threadId) { TransactionBegin.Builder beginBuilder = TransactionBegin.newBuilder(); @@ -999,6 +1027,31 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar this.nameBlackFilter = nameBlackFilter; logger.warn("--> init table black filter : " + nameBlackFilter.toString()); } + + public void setFieldFilterMap(Map> fieldFilterMap) { + if (fieldFilterMap != null) { + this.fieldFilterMap = fieldFilterMap; + } else { + this.fieldFilterMap = new HashMap>(); + } + + + for (Map.Entry> entry : this.fieldFilterMap.entrySet()) { + logger.warn("--> init field filter : " + entry.getKey() + "->" + entry.getValue()); + } + } + + public void setFieldBlackFilterMap(Map> fieldBlackFilterMap) { + if (fieldBlackFilterMap != null) { + this.fieldBlackFilterMap = fieldBlackFilterMap; + } else { + this.fieldBlackFilterMap = new HashMap>(); + } + + for (Map.Entry> entry : this.fieldBlackFilterMap.entrySet()) { + logger.warn("--> init field black filter : " + entry.getKey() + "->" + entry.getValue()); + } + } public void setTableMetaCache(TableMetaCache tableMetaCache) { this.tableMetaCache = tableMetaCache; diff --git a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/tsdb/DatabaseTableMeta.java b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/tsdb/DatabaseTableMeta.java index 953cca5d..f743a34f 100644 --- a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/tsdb/DatabaseTableMeta.java +++ b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/tsdb/DatabaseTableMeta.java @@ -69,6 +69,8 @@ public class DatabaseTableMeta implements TableMetaTSDB { private volatile MysqlConnection connection; // 查询meta信息的链接 private CanalEventFilter filter; private CanalEventFilter blackFilter; + private Map> fieldFilterMap = new HashMap>(); + private Map> fieldBlackFilterMap = new HashMap>(); private EntryPosition lastPosition; private boolean hasNewDdl; private MetaHistoryDAO metaHistoryDAO; @@ -580,6 +582,14 @@ public class DatabaseTableMeta implements TableMetaTSDB { public void setBlackFilter(CanalEventFilter blackFilter) { this.blackFilter = blackFilter; } + + public void setFieldFilterMap(Map> fieldFilterMap) { + this.fieldFilterMap = fieldFilterMap; + } + + public void setFieldBlackFilterMap(Map> fieldBlackFilterMap) { + this.fieldBlackFilterMap = fieldBlackFilterMap; + } public int getSnapshotInterval() { return snapshotInterval; diff --git a/pom.xml b/pom.xml index 5f875209..a9e26ffe 100644 --- a/pom.xml +++ b/pom.xml @@ -253,7 +253,7 @@ com.alibaba.fastsql fastsql - 2.0.0_preview_914 + 2.0.0_preview_896 com.alibaba