diff --git a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/GtidLogEvent.java b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/GtidLogEvent.java index 411b6a78..064f60bf 100644 --- a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/GtidLogEvent.java +++ b/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/GtidLogEvent.java @@ -1,11 +1,11 @@ package com.taobao.tddl.dbsync.binlog.event; -import com.taobao.tddl.dbsync.binlog.LogBuffer; -import com.taobao.tddl.dbsync.binlog.LogEvent; - import java.nio.ByteBuffer; import java.util.UUID; +import com.taobao.tddl.dbsync.binlog.LogBuffer; +import com.taobao.tddl.dbsync.binlog.LogEvent; + /** * @author jianghang 2013-4-8 上午12:36:29 * @version 1.0.3 diff --git a/deployer/src/main/resources/example/instance.properties b/deployer/src/main/resources/example/instance.properties index 32f4f78f..a3703b86 100644 --- a/deployer/src/main/resources/example/instance.properties +++ b/deployer/src/main/resources/example/instance.properties @@ -1,15 +1,16 @@ ################################################# ## mysql serverId canal.instance.mysql.slaveId=0 + # position info canal.instance.master.address=127.0.0.1:3306 +# enable gtid use true/false +canal.instance.gtidon=false canal.instance.master.journal.name= canal.instance.master.position= canal.instance.master.timestamp= canal.instance.master.gtid= -canal.instance.gtidon=true - # table meta tsdb info canal.instance.tsdb.enable=true canal.instance.tsdb.dir=${canal.file.data.dir:../conf}/${canal.instance.destination:} diff --git a/deployer/src/main/resources/spring/default-instance.xml b/deployer/src/main/resources/spring/default-instance.xml index 8a26c34a..93cb39af 100644 --- a/deployer/src/main/resources/spring/default-instance.xml +++ b/deployer/src/main/resources/spring/default-instance.xml @@ -166,6 +166,7 @@ + @@ -173,6 +174,7 @@ + @@ -187,5 +189,8 @@ + + + diff --git a/deployer/src/main/resources/spring/group-instance.xml b/deployer/src/main/resources/spring/group-instance.xml index 5ac91794..e8c0dd0f 100644 --- a/deployer/src/main/resources/spring/group-instance.xml +++ b/deployer/src/main/resources/spring/group-instance.xml @@ -148,6 +148,7 @@ + @@ -155,6 +156,7 @@ + @@ -239,6 +241,7 @@ + @@ -246,6 +249,7 @@ + diff --git a/deployer/src/main/resources/spring/memory-instance.xml b/deployer/src/main/resources/spring/memory-instance.xml index 7f1eff3f..ef8e4880 100644 --- a/deployer/src/main/resources/spring/memory-instance.xml +++ b/deployer/src/main/resources/spring/memory-instance.xml @@ -139,6 +139,7 @@ + @@ -146,6 +147,7 @@ + @@ -160,5 +162,8 @@ + + + diff --git a/example/src/main/java/com/alibaba/otter/canal/example/AbstractCanalClientTest.java b/example/src/main/java/com/alibaba/otter/canal/example/AbstractCanalClientTest.java index 264e1b1e..3c2cda96 100644 --- a/example/src/main/java/com/alibaba/otter/canal/example/AbstractCanalClientTest.java +++ b/example/src/main/java/com/alibaba/otter/canal/example/AbstractCanalClientTest.java @@ -4,6 +4,7 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; +import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.SystemUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,10 +59,12 @@ public class AbstractCanalClientTest { context_format += "****************************************************" + SEP; row_format = SEP - + "----------------> binlog[{}:{}] , name[{},{}] , eventType : {} , executeTime : {}({}) , delay : {} ms" + + "----------------> binlog[{}:{}] , name[{},{}] , eventType : {} , executeTime : {}({}) , gtid : ({}) , delay : {} ms" + SEP; - transaction_format = SEP + "================> binlog[{}:{}] , executeTime : {}({}) , delay : {}ms" + SEP; + transaction_format = SEP + + "================> binlog[{}:{}] , executeTime : {}({}) , gtid : ({}) , delay : {}ms" + + SEP; } @@ -166,8 +169,12 @@ public class AbstractCanalClientTest { long time = entry.getHeader().getExecuteTime(); Date date = new Date(time); SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT); - return entry.getHeader().getLogfileName() + ":" + entry.getHeader().getLogfileOffset() + ":" - + entry.getHeader().getExecuteTime() + "(" + format.format(date) + ")"; + String position = entry.getHeader().getLogfileName() + ":" + entry.getHeader().getLogfileOffset() + ":" + + entry.getHeader().getExecuteTime() + "(" + format.format(date) + ")"; + if (StringUtils.isNotEmpty(entry.getHeader().getGtid())) { + position += " gtid(" + entry.getHeader().getGtid() + ")"; + } + return position; } protected void printEntry(List entrys) { @@ -190,7 +197,7 @@ public class AbstractCanalClientTest { new Object[] { entry.getHeader().getLogfileName(), String.valueOf(entry.getHeader().getLogfileOffset()), String.valueOf(entry.getHeader().getExecuteTime()), simpleDateFormat.format(date), - String.valueOf(delayTime) }); + entry.getHeader().getGtid(), String.valueOf(delayTime) }); logger.info(" BEGIN ----> Thread id: {}", begin.getThreadId()); } else if (entry.getEntryType() == EntryType.TRANSACTIONEND) { TransactionEnd end = null; @@ -206,7 +213,7 @@ public class AbstractCanalClientTest { new Object[] { entry.getHeader().getLogfileName(), String.valueOf(entry.getHeader().getLogfileOffset()), String.valueOf(entry.getHeader().getExecuteTime()), simpleDateFormat.format(date), - String.valueOf(delayTime) }); + entry.getHeader().getGtid(), String.valueOf(delayTime) }); } continue; @@ -227,7 +234,7 @@ public class AbstractCanalClientTest { String.valueOf(entry.getHeader().getLogfileOffset()), entry.getHeader().getSchemaName(), entry.getHeader().getTableName(), eventType, String.valueOf(entry.getHeader().getExecuteTime()), simpleDateFormat.format(date), - String.valueOf(delayTime) }); + entry.getHeader().getGtid(), String.valueOf(delayTime) }); if (eventType == EventType.QUERY || rowChage.getIsDdl()) { logger.info(" sql ----> " + rowChage.getSql() + SEP); diff --git a/meta/src/main/java/com/alibaba/otter/canal/meta/FileMixedMetaManager.java b/meta/src/main/java/com/alibaba/otter/canal/meta/FileMixedMetaManager.java index 383fb351..49543939 100644 --- a/meta/src/main/java/com/alibaba/otter/canal/meta/FileMixedMetaManager.java +++ b/meta/src/main/java/com/alibaba/otter/canal/meta/FileMixedMetaManager.java @@ -109,10 +109,11 @@ public class FileMixedMetaManager extends MemoryMetaManager implements CanalMeta // 定时将内存中的最新值刷到file中,多次变更只刷一次 if (logger.isInfoEnabled()) { LogPosition cursor = (LogPosition) getCursor(clientIdentity); - logger.info("clientId:{} cursor:[{},{},{}] address[{}]", - new Object[] { clientIdentity.getClientId(), cursor.getPostion().getJournalName(), - cursor.getPostion().getPosition(), cursor.getPostion().getTimestamp(), - cursor.getIdentity().getSourceAddress().toString() }); + logger.info("clientId:{} cursor:[{},{},{},{},{}] address[{}]", new Object[] { + clientIdentity.getClientId(), cursor.getPostion().getJournalName(), + cursor.getPostion().getPosition(), cursor.getPostion().getTimestamp(), + cursor.getPostion().getServerId(), cursor.getPostion().getGtid(), + cursor.getIdentity().getSourceAddress().toString() }); } flushDataToFile(clientIdentity.getDestination()); updateCursorTasks.remove(clientIdentity); 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 c486c463..d047c6be 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 @@ -8,8 +8,6 @@ import java.util.TimerTask; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; -import com.alibaba.otter.canal.parse.driver.mysql.packets.MysqlGTIDSet; -import com.google.protobuf.InvalidProtocolBufferException; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.commons.lang.math.RandomUtils; @@ -21,6 +19,7 @@ import com.alibaba.otter.canal.common.AbstractCanalLifeCycle; import com.alibaba.otter.canal.common.alarm.CanalAlarmHandler; import com.alibaba.otter.canal.filter.CanalEventFilter; import com.alibaba.otter.canal.parse.CanalEventParser; +import com.alibaba.otter.canal.parse.driver.mysql.packets.MysqlGTIDSet; import com.alibaba.otter.canal.parse.exception.CanalParseException; import com.alibaba.otter.canal.parse.exception.TableIdNotFoundException; import com.alibaba.otter.canal.parse.inbound.EventTransactionBuffer.TransactionFlushCallback; @@ -90,7 +89,6 @@ public abstract class AbstractEventParser extends AbstractCanalLifeCycle protected Throwable exception = null; protected boolean isGTIDMode = false; // 是否是GTID模式 - protected String GTIDSetStr = null; protected abstract BinlogParser buildParser(); @@ -550,13 +548,4 @@ public abstract class AbstractEventParser extends AbstractCanalLifeCycle public void setIsGTIDMode(boolean isGTIDMode) { this.isGTIDMode = isGTIDMode; } - - public String getGTIDSetStr() { - return GTIDSetStr; - } - - public void setGTIDSetStr(String GTIDSetStr) { - this.GTIDSetStr = GTIDSetStr; - } - } 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 8d0c5288..3b0e605d 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 @@ -1,15 +1,13 @@ package com.alibaba.otter.canal.parse.inbound.mysql; -import java.io.IOException; import java.nio.charset.Charset; -import com.alibaba.otter.canal.parse.driver.mysql.packets.MysqlGTIDSet; -import com.alibaba.otter.canal.parse.inbound.mysql.dbsync.LogEventConvertGTID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.otter.canal.filter.CanalEventFilter; import com.alibaba.otter.canal.filter.aviater.AviaterRegexFilter; +import com.alibaba.otter.canal.parse.driver.mysql.packets.MysqlGTIDSet; import com.alibaba.otter.canal.parse.exception.CanalParseException; import com.alibaba.otter.canal.parse.inbound.AbstractEventParser; import com.alibaba.otter.canal.parse.inbound.BinlogParser; @@ -37,20 +35,7 @@ public abstract class AbstractMysqlEventParser extends AbstractEventParser { protected boolean useDruidDdlFilter = true; protected BinlogParser buildParser() { - LogEventConvert convert; - - if (isGTIDMode()) { - EntryPosition position; - try { - position = findStartPosition(null); - } catch (IOException e) { - throw new RuntimeException("findStartPosition failed.", e); - } - convert = new LogEventConvertGTID(MysqlGTIDSet.parse(position.getGtid())); - } else { - convert = new LogEventConvert(); - } - + LogEventConvert convert = new LogEventConvert(); if (eventFilter != null && eventFilter instanceof AviaterRegexFilter) { convert.setNameFilter((AviaterRegexFilter) eventFilter); } @@ -84,7 +69,18 @@ public abstract class AbstractMysqlEventParser extends AbstractEventParser { * @return */ protected boolean processTableMeta(EntryPosition position) { + if (isGTIDMode()) { + if (binlogParser instanceof LogEventConvert) { + // 记录gtid + ((LogEventConvert) binlogParser).setGtidSet(MysqlGTIDSet.parse(position.getGtid())); + } + } + if (tableMetaTSDB != null) { + if (position.getTimestamp() == null || position.getTimestamp() <= 0) { + throw new CanalParseException("use gtid and TableMeta TSDB should be config timestamp > 0"); + } + return tableMetaTSDB.rollback(position); } diff --git a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlEventParser.java b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlEventParser.java index 6b0c7a66..2efab0ee 100644 --- a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlEventParser.java +++ b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlEventParser.java @@ -10,10 +10,6 @@ import java.util.Map; import java.util.TimerTask; import java.util.concurrent.atomic.AtomicLong; -import com.alibaba.otter.canal.filter.aviater.AviaterRegexFilter; -import com.alibaba.otter.canal.parse.driver.mysql.packets.MysqlGTIDSet; -import com.alibaba.otter.canal.parse.inbound.BinlogParser; -import com.alibaba.otter.canal.parse.inbound.mysql.dbsync.LogEventConvertGTID; import org.apache.commons.lang.StringUtils; import org.springframework.util.CollectionUtils; @@ -354,8 +350,12 @@ public class MysqlEventParser extends AbstractMysqlEventParser implements CanalE if (logPosition != null) { return logPosition.getPostion(); } - return masterPosition; + + if (StringUtils.isNotEmpty(masterPosition.getGtid())) { + return masterPosition; + } } + EntryPosition startPosition = findStartPositionInternal(connection); if (needTransactionPosition.get()) { logger.warn("prepare to find last position : {}", startPosition.toString()); 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 dc243fd3..87ec3b59 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 @@ -17,6 +17,7 @@ import org.slf4j.LoggerFactory; import com.alibaba.otter.canal.common.AbstractCanalLifeCycle; import com.alibaba.otter.canal.filter.aviater.AviaterRegexFilter; +import com.alibaba.otter.canal.parse.driver.mysql.packets.GTIDSet; import com.alibaba.otter.canal.parse.exception.CanalParseException; import com.alibaba.otter.canal.parse.exception.TableIdNotFoundException; import com.alibaba.otter.canal.parse.inbound.BinlogParser; @@ -40,6 +41,7 @@ import com.alibaba.otter.canal.protocol.position.EntryPosition; import com.google.protobuf.ByteString; import com.taobao.tddl.dbsync.binlog.LogEvent; import com.taobao.tddl.dbsync.binlog.event.DeleteRowsLogEvent; +import com.taobao.tddl.dbsync.binlog.event.GtidLogEvent; import com.taobao.tddl.dbsync.binlog.event.IntvarLogEvent; import com.taobao.tddl.dbsync.binlog.event.LogHeader; import com.taobao.tddl.dbsync.binlog.event.QueryLogEvent; @@ -56,7 +58,6 @@ import com.taobao.tddl.dbsync.binlog.event.UserVarLogEvent; import com.taobao.tddl.dbsync.binlog.event.WriteRowsLogEvent; import com.taobao.tddl.dbsync.binlog.event.XidLogEvent; import com.taobao.tddl.dbsync.binlog.event.mariadb.AnnotateRowsEvent; -import com.taobao.tddl.dbsync.binlog.event.GtidLogEvent; /** * 基于{@linkplain LogEvent}转化为Entry对象的处理 @@ -93,6 +94,17 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar private boolean filterRows = false; private boolean useDruidDdlFilter = true; + // latest gtid + private GTIDSet gtidSet; + + public LogEventConvert(GTIDSet gtidSet){ + this.gtidSet = gtidSet; + } + + public LogEventConvert(){ + + } + @Override public Entry parse(LogEvent logEvent, boolean isSeek) throws CanalParseException { if (logEvent == null || logEvent instanceof UnknownLogEvent) { @@ -148,10 +160,15 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar private Entry parseGTIDLogEvent(GtidLogEvent logEvent) { LogHeader logHeader = logEvent.getHeader(); - Header header = createHeader("", logHeader, "", "", EventType.GTID); + String value = logEvent.getSid().toString() + ":" + logEvent.getGno(); Pair.Builder builder = Pair.newBuilder(); builder.setKey("gtid"); - builder.setValue(String.format("%s:%d", logEvent.getSid(), logEvent.getGno())); + builder.setValue(value); + if (gtidSet != null) { + gtidSet.update(value); + } + + Header header = createHeader("", logHeader, "", "", EventType.GTID); return createEntry(header, EntryType.GTIDLOG, builder.build().toByteString()); } @@ -737,6 +754,11 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar headerBuilder.setTableName(tableName); } headerBuilder.setEventLength(logHeader.getEventLen()); + // enable gtid position + if (gtidSet != null) { + String gtid = gtidSet.toString(); + headerBuilder.setGtid(gtid); + } return headerBuilder.build(); } @@ -785,7 +807,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar return "LONGTEXT".equalsIgnoreCase(columnType) || "MEDIUMTEXT".equalsIgnoreCase(columnType) || "TEXT".equalsIgnoreCase(columnType) || "TINYTEXT".equalsIgnoreCase(columnType); } - + private boolean isAliSQLHeartBeat(String schema, String table) { return "test".equalsIgnoreCase(schema) && "heartbeat".equalsIgnoreCase(table); } @@ -857,4 +879,8 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar this.filterRows = filterRows; } + public void setGtidSet(GTIDSet gtidSet) { + this.gtidSet = gtidSet; + } + } diff --git a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/dbsync/LogEventConvertGTID.java b/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/dbsync/LogEventConvertGTID.java deleted file mode 100644 index 337e6f48..00000000 --- a/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/dbsync/LogEventConvertGTID.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.alibaba.otter.canal.parse.inbound.mysql.dbsync; - -import com.alibaba.otter.canal.parse.driver.mysql.packets.GTIDSet; -import com.alibaba.otter.canal.parse.exception.CanalParseException; -import com.alibaba.otter.canal.parse.inbound.BinlogParser; -import com.alibaba.otter.canal.protocol.CanalEntry; -import com.google.protobuf.InvalidProtocolBufferException; -import com.taobao.tddl.dbsync.binlog.LogEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Created by hiwjd on 2018/5/2. - * hiwjd0@gmail.com - */ -public class LogEventConvertGTID extends LogEventConvert implements BinlogParser { - - public static final Logger logger = LoggerFactory.getLogger(LogEventConvertGTID.class); - - // latest gtid - private GTIDSet gtidSet; - - public LogEventConvertGTID(GTIDSet gtidSet) { - this.gtidSet = gtidSet; - } - - @Override - public CanalEntry.Entry parse(LogEvent event, boolean isSeek) throws CanalParseException { - CanalEntry.Entry entry = super.parse(event, isSeek); - if (entry == null) { - return null; - } - - if (entry.getEntryType() == CanalEntry.EntryType.GTIDLOG) { - try { - CanalEntry.Pair pair = CanalEntry.Pair.parseFrom(entry.getStoreValue()); - gtidSet.update(pair.getValue()); - - } catch (InvalidProtocolBufferException e) { - logger.error("retrive gtid failed.", e); - throw new CanalParseException("retrive gtid failed.", e); - } - - return null; - } - - if (gtidSet != null) { - String gtid = gtidSet.toString(); - CanalEntry.Header header = entry.getHeader().toBuilder().setGtid(gtid).build(); - entry = entry.toBuilder().setHeader(header).build(); - } - - return entry; - } -} diff --git a/parse/src/test/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlDumpTest.java b/parse/src/test/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlDumpTest.java index a0754635..5ef3278a 100644 --- a/parse/src/test/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlDumpTest.java +++ b/parse/src/test/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlDumpTest.java @@ -27,8 +27,7 @@ public class MysqlDumpTest { @Test public void testSimple() { final MysqlEventParser controller = new MysqlEventParser(); - final EntryPosition startPosition = new EntryPosition("mysql-bin.000001", 104606L); - + final EntryPosition startPosition = new EntryPosition("mysql-bin.000010", 154L); controller.setConnectionCharset(Charset.forName("UTF-8")); controller.setSlaveId(3344L); controller.setDetectingEnable(false); @@ -39,6 +38,7 @@ public class MysqlDumpTest { controller.setTsdbSpringXml("classpath:tsdb/h2-tsdb.xml"); controller.setEventFilter(new AviaterRegexFilter("test\\..*")); controller.setEventBlackFilter(new AviaterRegexFilter("canal_tsdb\\..*")); + controller.setIsGTIDMode(true); controller.setEventSink(new AbstractCanalEventSinkTest>() { public boolean sink(List entrys, InetSocketAddress remoteAddress, String destination)