fixed issue #726 , 多线程解析binlog DML事件,提升性能

This commit is contained in:
七锋
2018-07-04 23:37:20 +08:00
parent f0bed4c762
commit da8bf59d5b
24 changed files with 880 additions and 107 deletions
@@ -58,7 +58,6 @@ public final class LogContext {
public void reset() {
formatDescription = FormatDescriptionLogEvent.FORMAT_DESCRIPTION_EVENT_5_x;
mapOfTable.clear();
}
}
@@ -109,6 +109,8 @@ public final class LogDecoder {
/* Decoding binary-log to event */
event = decode(buffer, header, context);
if (event != null) {
// set logFileName
event.getHeader().setLogFileName(context.getLogPosition().getFileName());
event.setSemival(buffer.semival);
}
} catch (IOException e) {
@@ -115,6 +115,11 @@ public final class LogHeader {
*/
protected long crc; // ha_checksum
/**
* binlog fileName
*/
protected String logFileName;
/* for Start_event_v3 */
public LogHeader(final int type){
this.type = type;
@@ -270,6 +275,14 @@ public final class LogHeader {
return checksumAlg;
}
public String getLogFileName() {
return logFileName;
}
public void setLogFileName(String logFileName) {
this.logFileName = logFileName;
}
private void processCheckSum(LogBuffer buffer) {
if (checksumAlg != LogEvent.BINLOG_CHECKSUM_ALG_OFF && checksumAlg != LogEvent.BINLOG_CHECKSUM_ALG_UNDEF) {
crc = buffer.getUint32(eventLen - LogEvent.BINLOG_CHECKSUM_LEN);
@@ -194,7 +194,7 @@ public abstract class RowsLogEvent extends LogEvent {
}
public final RowsLogBuffer getRowsBuf(String charsetName) {
return new RowsLogBuffer(rowsBuf.duplicate(), columnLen, charsetName);
return new RowsLogBuffer(rowsBuf, columnLen, charsetName);
}
public final int getFlags(final int flags) {
@@ -51,6 +51,11 @@ canal.instance.binlog.image = FULL,MINIMAL,NOBLOB
# binlog ddl isolation
canal.instance.get.ddl.isolation = false
# parallel parser config
canal.instance.parser.parallel = true
canal.instance.parser.parallelThreadSize = 16
canal.instance.parser.parallelBufferSize = 256
#################################################
######### destinations #############
#################################################
@@ -192,5 +192,10 @@
<!--是否启用GTID模式-->
<property name="isGTIDMode" value="${canal.instance.gtidon:false}"/>
<!-- parallel parser -->
<property name="parallel" value="${canal.instance.parser.parallel:true}" />
<property name="parallelThreadSize" value="${canal.instance.parser.parallelThreadSize:16}" />
<property name="parallelBufferSize" value="${canal.instance.parser.parallelBufferSize:256}" />
</bean>
</beans>
@@ -177,5 +177,10 @@
<!--是否启用GTID模式-->
<property name="isGTIDMode" value="${canal.instance.gtidon:false}"/>
<!-- parallel parser -->
<property name="parallel" value="${canal.instance.parser.parallel:true}" />
<property name="parallelThreadSize" value="${canal.instance.parser.parallelThreadSize:16}" />
<property name="parallelBufferSize" value="${canal.instance.parser.parallelBufferSize:256}" />
</bean>
</beans>
@@ -166,6 +166,11 @@
<property name="filterTableError" value="${canal.instance.filter.table.error:false}" />
<property name="supportBinlogFormats" value="${canal.instance.binlog.format}" />
<property name="supportBinlogImages" value="${canal.instance.binlog.image}" />
<!-- parallel parser -->
<property name="parallel" value="${canal.instance.parser.parallel:true}" />
<property name="parallelThreadSize" value="${canal.instance.parser.parallelThreadSize:16}" />
<property name="parallelBufferSize" value="${canal.instance.parser.parallelBufferSize:256}" />
</bean>
<bean id="eventParser2" class="com.alibaba.otter.canal.parse.inbound.mysql.MysqlEventParser">
@@ -260,5 +265,10 @@
<property name="filterTableError" value="${canal.instance.filter.table.error:false}" />
<property name="supportBinlogFormats" value="${canal.instance.binlog.format}" />
<property name="supportBinlogImages" value="${canal.instance.binlog.image}" />
<!-- parallel parser -->
<property name="parallel" value="${canal.instance.parser.parallel:true}" />
<property name="parallelThreadSize" value="${canal.instance.parser.parallelThreadSize:16}" />
<property name="parallelBufferSize" value="${canal.instance.parser.parallelBufferSize:256}" />
</bean>
</beans>
@@ -137,5 +137,10 @@
<!--表结构相关-->
<property name="enableTsdb" value="${canal.instance.tsdb.enable:true}"/>
<property name="tsdbSpringXml" value="${canal.instance.tsdb.spring.xml:}"/>
<!-- parallel parser -->
<property name="parallel" value="${canal.instance.parser.parallel:true}" />
<property name="parallelThreadSize" value="${canal.instance.parser.parallelThreadSize:16}" />
<property name="parallelBufferSize" value="${canal.instance.parser.parallelBufferSize:256}" />
</bean>
</beans>
@@ -165,5 +165,10 @@
<!--是否启用GTID模式-->
<property name="isGTIDMode" value="${canal.instance.gtidon:false}"/>
<!-- parallel parser -->
<property name="parallel" value="${canal.instance.parser.parallel:true}" />
<property name="parallelThreadSize" value="${canal.instance.parser.parallelThreadSize:16}" />
<property name="parallelBufferSize" value="${canal.instance.parser.parallelBufferSize:256}" />
</bean>
</beans>
@@ -93,6 +93,37 @@ public class BioSocketChannel implements SocketChannel {
return data;
}
@Override
public void read(byte[] data, int off, int len, int timeout) throws IOException {
InputStream input = this.input;
int accTimeout = 0;
if (input == null) {
throw new SocketException("Socket already closed.");
}
int n = 0;
while (n < len && accTimeout < timeout) {
try {
int read = input.read(data, off + n, len - n);
if (read > -1) {
n += read;
} else {
throw new IOException("EOF encountered.");
}
} catch (SocketTimeoutException te) {
if (Thread.interrupted()) {
throw new ClosedByInterruptException();
}
accTimeout += SO_TIMEOUT;
}
}
if (n < len && accTimeout >= timeout) {
throw new SocketTimeoutException("Timeout occurred, failed to read " + len + " bytes in " + timeout
+ " milliseconds.");
}
}
public boolean isConnected() {
Socket socket = this.socket;
if (socket != null) {
@@ -133,4 +164,5 @@ public class BioSocketChannel implements SocketChannel {
this.socket = null;
}
}
@@ -11,6 +11,7 @@ import io.netty.util.internal.SystemPropertyUtil;
import java.io.IOException;
import java.net.SocketAddress;
import org.apache.commons.lang.NotImplementedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -202,6 +203,11 @@ public class NettySocketChannel implements SocketChannel {
} while (true);
}
@Override
public void read(byte[] data, int off, int len, int timeout) throws IOException {
throw new NotImplementedException();
}
public boolean isConnected() {
return channel != null ? true : false;
}
@@ -224,4 +230,5 @@ public class NettySocketChannel implements SocketChannel {
}
}
}
@@ -15,6 +15,8 @@ public interface SocketChannel {
public byte[] read(int readSize, int timeout) throws IOException;
public void read(byte[] data, int off, int len, int timeout) throws IOException;
public boolean isConnected();
public SocketAddress getRemoteSocketAddress();
+4
View File
@@ -73,6 +73,10 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
</dependency>
<!-- test dependency -->
<dependency>
<groupId>junit</groupId>
@@ -89,11 +89,18 @@ public abstract class AbstractEventParser<EVENT> extends AbstractCanalLifeCycle
protected Throwable exception = null;
protected boolean isGTIDMode = false; // 是否是GTID模式
protected boolean parallel = true; // 是否开启并行解析模式
protected int parallelThreadSize = Runtime.getRuntime()
.availableProcessors() * 60 / 100; // 60%的能力跑解析,剩余部分处理网络
protected int parallelBufferSize = 16 * parallelThreadSize;
protected MultiStageCoprocessor multiStageCoprocessor;
protected abstract BinlogParser buildParser();
protected abstract ErosaConnection buildErosaConnection();
protected abstract MultiStageCoprocessor buildMultiStageCoprocessor();
protected abstract EntryPosition findStartPosition(ErosaConnection connection) throws IOException;
protected void preDump(ErosaConnection connection) {
@@ -217,20 +224,39 @@ public abstract class AbstractEventParser<EVENT> extends AbstractCanalLifeCycle
};
// 4. 开始dump数据
// 判断所属instance是否启用GTID模式,是的话调用ErosaConnection中GTID对应方法dump数据
if (isGTIDMode()) {
erosaConnection.dump(MysqlGTIDSet.parse(startPosition.getGtid()), sinkHandler);
} else {
if (StringUtils.isEmpty(startPosition.getJournalName())
&& startPosition.getTimestamp() != null) {
erosaConnection.dump(startPosition.getTimestamp(), sinkHandler);
if (parallel) {
// build stage processor
multiStageCoprocessor = buildMultiStageCoprocessor();
multiStageCoprocessor.start();
if (isGTIDMode()) {
// 判断所属instance是否启用GTID模式,是的话调用ErosaConnection中GTID对应方法dump数据
erosaConnection.dump(MysqlGTIDSet.parse(startPosition.getGtid()), multiStageCoprocessor);
} else {
erosaConnection.dump(startPosition.getJournalName(),
startPosition.getPosition(),
sinkHandler);
if (StringUtils.isEmpty(startPosition.getJournalName())
&& startPosition.getTimestamp() != null) {
erosaConnection.dump(startPosition.getTimestamp(), multiStageCoprocessor);
} else {
erosaConnection.dump(startPosition.getJournalName(),
startPosition.getPosition(),
multiStageCoprocessor);
}
}
} else {
if (isGTIDMode()) {
// 判断所属instance是否启用GTID模式,是的话调用ErosaConnection中GTID对应方法dump数据
erosaConnection.dump(MysqlGTIDSet.parse(startPosition.getGtid()), sinkHandler);
} else {
if (StringUtils.isEmpty(startPosition.getJournalName())
&& startPosition.getTimestamp() != null) {
erosaConnection.dump(startPosition.getTimestamp(), sinkHandler);
} else {
erosaConnection.dump(startPosition.getJournalName(),
startPosition.getPosition(),
sinkHandler);
}
}
}
} catch (TableIdNotFoundException e) {
exception = e;
// 特殊处理TableIdNotFound异常,出现这样的异常,一种可能就是起始的position是一个事务当中,导致tablemap
@@ -276,6 +302,9 @@ public abstract class AbstractEventParser<EVENT> extends AbstractCanalLifeCycle
eventSink.interrupt();
transactionBuffer.reset();// 重置一下缓冲队列,重新记录数据
binlogParser.reset();// 重新置位
if (multiStageCoprocessor != null) {
multiStageCoprocessor.reset();
}
if (running) {
// sleep一段时间再进行重试
@@ -314,6 +343,10 @@ public abstract class AbstractEventParser<EVENT> extends AbstractCanalLifeCycle
if (transactionBuffer.isStart()) {
transactionBuffer.stop();
}
if (multiStageCoprocessor != null && multiStageCoprocessor.isStart()) {
multiStageCoprocessor.stop();
}
}
protected boolean consumeTheEventAndProfilingIfNecessary(List<CanalEntry.Entry> entrys) throws CanalSinkException,
@@ -549,4 +582,29 @@ public abstract class AbstractEventParser<EVENT> extends AbstractCanalLifeCycle
public void setIsGTIDMode(boolean isGTIDMode) {
this.isGTIDMode = isGTIDMode;
}
public boolean isParallel() {
return parallel;
}
public void setParallel(boolean parallel) {
this.parallel = parallel;
}
public int getParallelThreadSize() {
return parallelThreadSize;
}
public void setParallelThreadSize(int parallelThreadSize) {
this.parallelThreadSize = parallelThreadSize;
}
public int getParallelBufferSize() {
return parallelBufferSize;
}
public void setParallelBufferSize(int parallelBufferSize) {
this.parallelBufferSize = parallelBufferSize;
}
}
@@ -28,12 +28,16 @@ public interface ErosaConnection {
/**
* 通过GTID同步binlog
*
* @param gtidSet
* @param func
* @throws IOException
*/
public void dump(GTIDSet gtidSet, SinkFunction func) throws IOException;
// -------------
public void dump(String binlogfilename, Long binlogPosition, MultiStageCoprocessor coprocessor) throws IOException;
public void dump(long timestamp, MultiStageCoprocessor coprocessor) throws IOException;
public void dump(GTIDSet gtidSet, MultiStageCoprocessor coprocessor) throws IOException;
ErosaConnection fork();
}
@@ -0,0 +1,29 @@
package com.alibaba.otter.canal.parse.inbound;
import com.alibaba.otter.canal.common.CanalLifeCycle;
import com.taobao.tddl.dbsync.binlog.LogBuffer;
/**
* 针对解析器提供一个多阶段协同的处理
*
* <pre>
* 1. 网络接收 (单线程)
* 2. 事件基本解析 (单线程,事件类型、DDL解析构造TableMeta、维护位点信息)
* 3. 事件深度解析 (多线程, DML事件数据的完整解析)
* 4. 投递到store (单线程)
* </pre>
*
* @author agapple 2018年7月3日 下午4:54:17
* @since 1.0.26
*/
public interface MultiStageCoprocessor extends CanalLifeCycle {
/**
* 网络数据投递
*/
public void publish(LogBuffer buffer);
public void publish(LogBuffer buffer, String binlogFileName);
public void reset();
}
@@ -11,6 +11,7 @@ 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;
import com.alibaba.otter.canal.parse.inbound.MultiStageCoprocessor;
import com.alibaba.otter.canal.parse.inbound.mysql.dbsync.LogEventConvert;
import com.alibaba.otter.canal.parse.inbound.mysql.tsdb.TableMetaTSDB;
import com.alibaba.otter.canal.parse.inbound.mysql.tsdb.TableMetaTSDBBuilder;
@@ -117,6 +118,14 @@ public abstract class AbstractMysqlEventParser extends AbstractEventParser {
}
}
protected MultiStageCoprocessor buildMultiStageCoprocessor() {
return new MysqlMultiStageCoprocessor(parallelBufferSize,
parallelThreadSize,
(LogEventConvert) binlogParser,
transactionBuffer,
destination);
}
// ============================ setter / getter =========================
public void setConnectionCharsetNumber(byte connectionCharsetNumber) {
@@ -11,9 +11,11 @@ import org.slf4j.LoggerFactory;
import com.alibaba.otter.canal.parse.driver.mysql.packets.GTIDSet;
import com.alibaba.otter.canal.parse.inbound.ErosaConnection;
import com.alibaba.otter.canal.parse.inbound.MultiStageCoprocessor;
import com.alibaba.otter.canal.parse.inbound.SinkFunction;
import com.alibaba.otter.canal.parse.inbound.mysql.local.BinLogFileQueue;
import com.taobao.tddl.dbsync.binlog.FileLogFetcher;
import com.taobao.tddl.dbsync.binlog.LogBuffer;
import com.taobao.tddl.dbsync.binlog.LogContext;
import com.taobao.tddl.dbsync.binlog.LogDecoder;
import com.taobao.tddl.dbsync.binlog.LogEvent;
@@ -207,6 +209,128 @@ public class LocalBinLogConnection implements ErosaConnection {
throw new NotImplementedException();
}
@Override
public void dump(String binlogfilename, Long binlogPosition, MultiStageCoprocessor coprocessor) throws IOException {
File current = new File(directory, binlogfilename);
FileLogFetcher fetcher = new FileLogFetcher(bufferSize);
try {
fetcher.open(current, binlogPosition);
while (running) {
boolean needContinue = true;
while (fetcher.fetch()) {
LogBuffer buffer = fetcher.duplicate();
fetcher.consume(fetcher.limit());
coprocessor.publish(buffer, binlogfilename); // set filename
}
if (needContinue) {// 读取下一个
fetcher.close(); // 关闭上一个文件
File nextFile;
if (needWait) {
nextFile = binlogs.waitForNextFile(current);
} else {
nextFile = binlogs.getNextFile(current);
}
if (nextFile == null) {
break;
}
current = nextFile;
fetcher.open(current);
binlogfilename = nextFile.getName();
} else {
break;// 跳出
}
}
} catch (InterruptedException e) {
logger.warn("LocalBinLogConnection dump interrupted");
} finally {
if (fetcher != null) {
fetcher.close();
}
}
}
@Override
public void dump(long timestampMills, MultiStageCoprocessor coprocessor) throws IOException {
List<File> currentBinlogs = binlogs.currentBinlogs();
File current = currentBinlogs.get(currentBinlogs.size() - 1);
long timestampSeconds = timestampMills / 1000;
String binlogFilename = null;
long binlogFileOffset = 0;
FileLogFetcher fetcher = new FileLogFetcher(bufferSize);
LogDecoder decoder = new LogDecoder();
decoder.handle(LogEvent.FORMAT_DESCRIPTION_EVENT);
decoder.handle(LogEvent.QUERY_EVENT);
decoder.handle(LogEvent.XID_EVENT);
LogContext context = new LogContext();
try {
fetcher.open(current);
context.setLogPosition(new LogPosition(current.getName()));
while (running) {
boolean needContinue = true;
String lastXidLogFilename = current.getName();
long lastXidLogFileOffset = 0;
binlogFilename = lastXidLogFilename;
binlogFileOffset = lastXidLogFileOffset;
while (fetcher.fetch()) {
LogEvent event = decoder.decode(fetcher, context);
if (event != null) {
if (event.getWhen() > timestampSeconds) {
break;
}
needContinue = false;
if (LogEvent.QUERY_EVENT == event.getHeader().getType()) {
if (StringUtils.endsWithIgnoreCase(((QueryLogEvent) event).getQuery(), "BEGIN")) {
binlogFilename = lastXidLogFilename;
binlogFileOffset = lastXidLogFileOffset;
} else if (StringUtils.endsWithIgnoreCase(((QueryLogEvent) event).getQuery(), "COMMIT")) {
lastXidLogFilename = current.getName();
lastXidLogFileOffset = event.getLogPos();
}
} else if (LogEvent.XID_EVENT == event.getHeader().getType()) {
lastXidLogFilename = current.getName();
lastXidLogFileOffset = event.getLogPos();
}
}
}
if (needContinue) {// 读取下一个
fetcher.close(); // 关闭上一个文件
File nextFile = binlogs.getBefore(current);
if (nextFile == null) {
break;
}
current = nextFile;
fetcher.open(current);
context.setLogPosition(new LogPosition(current.getName()));
} else {
break;// 跳出
}
}
} finally {
if (fetcher != null) {
fetcher.close();
}
}
dump(binlogFilename, binlogFileOffset, coprocessor);
}
@Override
public void dump(GTIDSet gtidSet, MultiStageCoprocessor coprocessor) throws IOException {
throw new NotImplementedException();
}
public ErosaConnection fork() {
LocalBinLogConnection connection = new LocalBinLogConnection();
@@ -26,9 +26,11 @@ import com.alibaba.otter.canal.parse.driver.mysql.packets.server.ResultSetPacket
import com.alibaba.otter.canal.parse.driver.mysql.utils.PacketManager;
import com.alibaba.otter.canal.parse.exception.CanalParseException;
import com.alibaba.otter.canal.parse.inbound.ErosaConnection;
import com.alibaba.otter.canal.parse.inbound.MultiStageCoprocessor;
import com.alibaba.otter.canal.parse.inbound.SinkFunction;
import com.alibaba.otter.canal.parse.inbound.mysql.dbsync.DirectLogFetcher;
import com.alibaba.otter.canal.parse.support.AuthenticationInfo;
import com.taobao.tddl.dbsync.binlog.LogBuffer;
import com.taobao.tddl.dbsync.binlog.LogContext;
import com.taobao.tddl.dbsync.binlog.LogDecoder;
import com.taobao.tddl.dbsync.binlog.LogEvent;
@@ -156,7 +158,7 @@ public class MysqlConnection implements ErosaConnection {
}
if (event.getSemival() == 1) {
sendSemiAck(context.getLogPosition().getFileName(), binlogPosition);
sendSemiAck(context.getLogPosition().getFileName(), context.getLogPosition().getPosition());
}
}
}
@@ -167,20 +169,24 @@ public class MysqlConnection implements ErosaConnection {
sendBinlogDumpGTID(gtidSet);
DirectLogFetcher fetcher = new DirectLogFetcher(connector.getReceiveBufferSize());
fetcher.start(connector.getChannel());
LogDecoder decoder = new LogDecoder(LogEvent.UNKNOWN_EVENT, LogEvent.ENUM_END_EVENT);
LogContext context = new LogContext();
while (fetcher.fetch()) {
LogEvent event = null;
event = decoder.decode(fetcher, context);
try {
fetcher.start(connector.getChannel());
LogDecoder decoder = new LogDecoder(LogEvent.UNKNOWN_EVENT, LogEvent.ENUM_END_EVENT);
LogContext context = new LogContext();
while (fetcher.fetch()) {
LogEvent event = null;
event = decoder.decode(fetcher, context);
if (event == null) {
throw new CanalParseException("parse failed");
}
if (event == null) {
throw new CanalParseException("parse failed");
}
if (!func.sink(event)) {
break;
if (!func.sink(event)) {
break;
}
}
} finally {
fetcher.close();
}
}
@@ -188,6 +194,49 @@ public class MysqlConnection implements ErosaConnection {
throw new NullPointerException("Not implement yet");
}
@Override
public void dump(String binlogfilename, Long binlogPosition, MultiStageCoprocessor coprocessor) throws IOException {
updateSettings();
sendRegisterSlave();
sendBinlogDump(binlogfilename, binlogPosition);
((MysqlMultiStageCoprocessor) coprocessor).setConnection(this);
DirectLogFetcher fetcher = new DirectLogFetcher(connector.getReceiveBufferSize());
try {
fetcher.start(connector.getChannel());
while (fetcher.fetch()) {
LogBuffer buffer = fetcher.duplicate();
fetcher.consume(fetcher.limit());
coprocessor.publish(buffer);
}
} finally {
fetcher.close();
}
}
@Override
public void dump(long timestamp, MultiStageCoprocessor coprocessor) throws IOException {
throw new NullPointerException("Not implement yet");
}
@Override
public void dump(GTIDSet gtidSet, MultiStageCoprocessor coprocessor) throws IOException {
updateSettings();
sendBinlogDumpGTID(gtidSet);
((MysqlMultiStageCoprocessor) coprocessor).setConnection(this);
DirectLogFetcher fetcher = new DirectLogFetcher(connector.getReceiveBufferSize());
try {
fetcher.start(connector.getChannel());
while (fetcher.fetch()) {
LogBuffer buffer = fetcher.duplicate();
fetcher.consume(fetcher.limit());
coprocessor.publish(buffer);
}
} finally {
fetcher.close();
}
}
private void sendRegisterSlave() throws IOException {
RegisterSlaveCommandPacket cmd = new RegisterSlaveCommandPacket();
cmd.reportHost = authInfo.getAddress().getAddress().getHostAddress();
@@ -233,7 +282,7 @@ public class MysqlConnection implements ErosaConnection {
connector.setDumping(true);
}
private void sendSemiAck(String binlogfilename, Long binlogPosition) throws IOException {
public void sendSemiAck(String binlogfilename, Long binlogPosition) throws IOException {
SemiAckCommandPacket semiAckCmd = new SemiAckCommandPacket();
semiAckCmd.binlogFileName = binlogfilename;
semiAckCmd.binlogPosition = binlogPosition;
@@ -538,4 +587,5 @@ public class MysqlConnection implements ErosaConnection {
public void setAuthInfo(AuthenticationInfo authInfo) {
this.authInfo = authInfo;
}
}
@@ -0,0 +1,391 @@
package com.alibaba.otter.canal.parse.inbound.mysql;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.commons.lang.StringUtils;
import com.alibaba.otter.canal.common.AbstractCanalLifeCycle;
import com.alibaba.otter.canal.common.utils.NamedThreadFactory;
import com.alibaba.otter.canal.parse.exception.CanalParseException;
import com.alibaba.otter.canal.parse.inbound.ErosaConnection;
import com.alibaba.otter.canal.parse.inbound.EventTransactionBuffer;
import com.alibaba.otter.canal.parse.inbound.MultiStageCoprocessor;
import com.alibaba.otter.canal.parse.inbound.TableMeta;
import com.alibaba.otter.canal.parse.inbound.mysql.dbsync.LogEventConvert;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.lmax.disruptor.BatchEventProcessor;
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.EventHandler;
import com.lmax.disruptor.FatalExceptionHandler;
import com.lmax.disruptor.LifecycleAware;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.Sequence;
import com.lmax.disruptor.SequenceBarrier;
import com.lmax.disruptor.WorkHandler;
import com.lmax.disruptor.WorkerPool;
import com.taobao.tddl.dbsync.binlog.LogBuffer;
import com.taobao.tddl.dbsync.binlog.LogContext;
import com.taobao.tddl.dbsync.binlog.LogDecoder;
import com.taobao.tddl.dbsync.binlog.LogEvent;
import com.taobao.tddl.dbsync.binlog.LogPosition;
import com.taobao.tddl.dbsync.binlog.event.DeleteRowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.RowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.UpdateRowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.WriteRowsLogEvent;
/**
* 针对解析器提供一个多阶段协同的处理
*
* <pre>
* 1. 网络接收 (单线程)
* 2. 事件基本解析 (单线程,事件类型、DDL解析构造TableMeta、维护位点信息)
* 3. 事件深度解析 (多线程, DML事件数据的完整解析)
* 4. 投递到store (单线程)
* </pre>
*
* @author agapple 2018年7月3日 下午4:54:17
* @since 1.0.26
*/
public class MysqlMultiStageCoprocessor extends AbstractCanalLifeCycle implements MultiStageCoprocessor {
private LogEventConvert logEventConvert;
private EventTransactionBuffer transactionBuffer;
private ErosaConnection connection;
private int parserThreadCount;
private int ringBufferSize;
private RingBuffer<MessageEvent> disruptorMsgBuffer;
private ExecutorService parserExecutor;
private ExecutorService stageExecutor;
private String destination;
private CanalParseException exception;
public MysqlMultiStageCoprocessor(int ringBufferSize, int parserThreadCount, LogEventConvert logEventConvert,
EventTransactionBuffer transactionBuffer, String destination){
this.ringBufferSize = ringBufferSize;
this.parserThreadCount = parserThreadCount;
this.logEventConvert = logEventConvert;
this.transactionBuffer = transactionBuffer;
this.destination = destination;
}
@Override
public void start() {
super.start();
this.exception = null;
this.disruptorMsgBuffer = RingBuffer.createSingleProducer(new MessageEventFactory(),
ringBufferSize,
new BlockingWaitStrategy());
this.parserExecutor = Executors.newFixedThreadPool(parserThreadCount,
new NamedThreadFactory("MultiStageCoprocessor-Parser-" + destination));
this.stageExecutor = Executors.newFixedThreadPool(2, new NamedThreadFactory("MultiStageCoprocessor-other-"
+ destination));
SequenceBarrier sequenceBarrier = disruptorMsgBuffer.newBarrier();
// stage 2
BatchEventProcessor<MessageEvent> simpleParserStage = new BatchEventProcessor<MessageEvent>(disruptorMsgBuffer,
sequenceBarrier,
new SimpleParserStage());
disruptorMsgBuffer.addGatingSequences(simpleParserStage.getSequence());
// stage 3
SequenceBarrier dmlParserSequenceBarrier = disruptorMsgBuffer.newBarrier(simpleParserStage.getSequence());
WorkHandler<MessageEvent>[] workHandlers = new DmlParserStage[parserThreadCount];
for (int i = 0; i < parserThreadCount; i++) {
workHandlers[i] = new DmlParserStage();
}
WorkerPool<MessageEvent> workerPool = new WorkerPool<MessageEvent>(disruptorMsgBuffer,
dmlParserSequenceBarrier,
new FatalExceptionHandler(),
workHandlers);
Sequence[] sequence = workerPool.getWorkerSequences();
disruptorMsgBuffer.addGatingSequences(sequence);
// stage 4
SequenceBarrier sinkSequenceBarrier = disruptorMsgBuffer.newBarrier(sequence);
BatchEventProcessor<MessageEvent> sinkStoreStage = new BatchEventProcessor<MessageEvent>(disruptorMsgBuffer,
sinkSequenceBarrier,
new SinkStoreStage());
disruptorMsgBuffer.addGatingSequences(sinkStoreStage.getSequence());
// start work
stageExecutor.submit(simpleParserStage);
stageExecutor.submit(sinkStoreStage);
workerPool.start(parserExecutor);
}
@Override
public void stop() {
try {
parserExecutor.shutdownNow();
} catch (Throwable e) {
// ignore
}
try {
stageExecutor.shutdownNow();
} catch (Throwable e) {
// ignore
}
disruptorMsgBuffer = null;
super.stop();
}
/**
* 网络数据投递
*/
public void publish(LogBuffer buffer) {
if (!isStart() && exception != null) {
throw exception;
}
long next = disruptorMsgBuffer.next();
MessageEvent event = disruptorMsgBuffer.get(next);
event.setBuffer(buffer);
disruptorMsgBuffer.publish(next);
}
public void publish(LogBuffer buffer, String binlogFileName) {
if (!isStart() && exception != null) {
throw exception;
}
long next = disruptorMsgBuffer.next();
MessageEvent event = disruptorMsgBuffer.get(next);
event.setBuffer(buffer);
event.setBinlogFileName(binlogFileName);
disruptorMsgBuffer.publish(next);
}
@Override
public void reset() {
stop();
start();
}
private class SimpleParserStage implements EventHandler<MessageEvent>, LifecycleAware {
private LogDecoder decoder;
private LogContext context;
public SimpleParserStage(){
decoder = new LogDecoder(LogEvent.UNKNOWN_EVENT, LogEvent.ENUM_END_EVENT);
context = new LogContext();
}
public void onEvent(MessageEvent event, long sequence, boolean endOfBatch) throws Exception {
try {
LogBuffer buffer = event.getBuffer();
if (StringUtils.isNotEmpty(event.getBinlogFileName())
&& !context.getLogPosition().getFileName().equals(event.getBinlogFileName())) {
// set roate binlog file name
context.setLogPosition(new LogPosition(event.getBinlogFileName(), context.getLogPosition()
.getPosition()));
}
LogEvent logEvent = decoder.decode(buffer, context);
event.setEvent(logEvent);
int eventType = logEvent.getHeader().getType();
TableMeta tableMeta = null;
boolean needDmlParse = false;
switch (eventType) {
case LogEvent.WRITE_ROWS_EVENT_V1:
case LogEvent.WRITE_ROWS_EVENT:
tableMeta = logEventConvert.parseRowsEventForTableMeta((WriteRowsLogEvent) logEvent);
needDmlParse = true;
break;
case LogEvent.UPDATE_ROWS_EVENT_V1:
case LogEvent.UPDATE_ROWS_EVENT:
tableMeta = logEventConvert.parseRowsEventForTableMeta((UpdateRowsLogEvent) logEvent);
needDmlParse = true;
break;
case LogEvent.DELETE_ROWS_EVENT_V1:
case LogEvent.DELETE_ROWS_EVENT:
tableMeta = logEventConvert.parseRowsEventForTableMeta((DeleteRowsLogEvent) logEvent);
needDmlParse = true;
break;
case LogEvent.ROWS_QUERY_LOG_EVENT:
needDmlParse = true;
break;
default:
CanalEntry.Entry entry = logEventConvert.parse(event.getEvent(), false);
event.setEntry(entry);
}
// 记录一下DML的表结构
event.setNeedDmlParse(needDmlParse);
event.setTable(tableMeta);
} catch (Throwable e) {
exception = new CanalParseException(e);
throw exception;
}
}
@Override
public void onStart() {
}
@Override
public void onShutdown() {
stop();
}
}
private class DmlParserStage implements WorkHandler<MessageEvent>, LifecycleAware {
@Override
public void onEvent(MessageEvent event) throws Exception {
try {
if (event.isNeedDmlParse()) {
int eventType = event.getEvent().getHeader().getType();
CanalEntry.Entry entry = null;
switch (eventType) {
case LogEvent.ROWS_QUERY_LOG_EVENT:
entry = logEventConvert.parse(event.getEvent(), false);
break;
default:
// 单独解析dml事件
entry = logEventConvert.parseRowsEvent((RowsLogEvent) event.getEvent(), event.getTable());
}
event.setEntry(entry);
}
} catch (Throwable e) {
exception = new CanalParseException(e);
throw exception;
}
}
@Override
public void onStart() {
}
@Override
public void onShutdown() {
stop();
}
}
private class SinkStoreStage implements EventHandler<MessageEvent>, LifecycleAware {
public void onEvent(MessageEvent event, long sequence, boolean endOfBatch) throws Exception {
try {
if (event.getEntry() != null) {
transactionBuffer.add(event.getEntry());
}
LogEvent logEvent = event.getEvent();
if (connection instanceof MysqlConnection && logEvent.getSemival() == 1) {
// semi ack回报
((MysqlConnection) connection).sendSemiAck(logEvent.getHeader().getLogFileName(),
logEvent.getHeader().getLogPos());
}
// clear for gc
event.setBuffer(null);
event.setBinlogFileName(null);
event.setEvent(null);
event.setTable(null);
event.setEntry(null);
event.setNeedDmlParse(false);
} catch (Throwable e) {
exception = new CanalParseException(e);
throw exception;
}
}
@Override
public void onStart() {
}
@Override
public void onShutdown() {
stop();
}
}
class MessageEvent {
private String binlogFileName; // for local binlog parse
private LogBuffer buffer;
private CanalEntry.Entry entry;
private boolean needDmlParse = false;
private TableMeta table;
private LogEvent event;
public String getBinlogFileName() {
return binlogFileName;
}
public void setBinlogFileName(String binlogFileName) {
this.binlogFileName = binlogFileName;
}
public LogBuffer getBuffer() {
return buffer;
}
public void setBuffer(LogBuffer buffer) {
this.buffer = buffer;
}
public LogEvent getEvent() {
return event;
}
public void setEvent(LogEvent event) {
this.event = event;
}
public CanalEntry.Entry getEntry() {
return entry;
}
public void setEntry(CanalEntry.Entry entry) {
this.entry = entry;
}
public boolean isNeedDmlParse() {
return needDmlParse;
}
public void setNeedDmlParse(boolean needDmlParse) {
this.needDmlParse = needDmlParse;
}
public TableMeta getTable() {
return table;
}
public void setTable(TableMeta table) {
this.table = table;
}
}
class MessageEventFactory implements EventFactory<MessageEvent> {
public MessageEvent newInstance() {
return new MessageEvent();
}
}
public void setLogEventConvert(LogEventConvert logEventConvert) {
this.logEventConvert = logEventConvert;
}
public void setTransactionBuffer(EventTransactionBuffer transactionBuffer) {
this.transactionBuffer = transactionBuffer;
}
public void setConnection(ErosaConnection connection) {
this.connection = connection;
}
}
@@ -171,10 +171,13 @@ public class DirectLogFetcher extends LogFetcher {
private final boolean fetch0(final int off, final int len) throws IOException {
ensureCapacity(off + len);
byte[] read = channel.read(len, READ_TIMEOUT_MILLISECONDS);
System.arraycopy(read, 0, this.buffer, off, len);
// byte[] read = channel.read(len, READ_TIMEOUT_MILLISECONDS);
// System.arraycopy(read, 0, this.buffer, off, len);
if (limit < off + len) limit = off + len;
channel.read(buffer, off, len, READ_TIMEOUT_MILLISECONDS);
if (limit < off + len) {
limit = off + len;
}
return true;
}
@@ -46,7 +46,6 @@ import com.taobao.tddl.dbsync.binlog.event.IntvarLogEvent;
import com.taobao.tddl.dbsync.binlog.event.LogHeader;
import com.taobao.tddl.dbsync.binlog.event.QueryLogEvent;
import com.taobao.tddl.dbsync.binlog.event.RandLogEvent;
import com.taobao.tddl.dbsync.binlog.event.RotateLogEvent;
import com.taobao.tddl.dbsync.binlog.event.RowsLogBuffer;
import com.taobao.tddl.dbsync.binlog.event.RowsLogEvent;
import com.taobao.tddl.dbsync.binlog.event.RowsQueryLogEvent;
@@ -89,7 +88,6 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
private volatile AviaterRegexFilter nameBlackFilter;
private TableMetaCache tableMetaCache;
private String binlogFileName = "mysql-bin.000001";
private Charset charset = Charset.defaultCharset();
private boolean filterQueryDcl = false;
private boolean filterQueryDml = false;
@@ -119,9 +117,6 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
int eventType = logEvent.getHeader().getType();
switch (eventType) {
case LogEvent.ROTATE_EVENT:
binlogFileName = ((RotateLogEvent) logEvent).getFilename();
break;
case LogEvent.QUERY_EVENT:
return parseQueryEvent((QueryLogEvent) logEvent, isSeek);
case LogEvent.XID_EVENT:
@@ -158,7 +153,6 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
public void reset() {
// do nothing
binlogFileName = "mysql-bin.000001";
if (tableMetaCache != null) {
tableMetaCache.clearTableMeta();
}
@@ -174,7 +168,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
gtidSet.update(value);
}
Header header = createHeader("", logHeader, "", "", EventType.GTID);
Header header = createHeader(logHeader, "", "", EventType.GTID);
return createEntry(header, EntryType.GTIDLOG, builder.build().toByteString());
}
@@ -187,7 +181,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
beginBuilder.addProps(createSpecialPair(XA_TYPE, XA_START));
beginBuilder.addProps(createSpecialPair(XA_XID, getXaXid(queryString, XA_START)));
TransactionBegin transactionBegin = beginBuilder.build();
Header header = createHeader(binlogFileName, event.getHeader(), "", "", null);
Header header = createHeader(event.getHeader(), "", "", null);
return createEntry(header, EntryType.TRANSACTIONBEGIN, transactionBegin.toByteString());
} else if (StringUtils.startsWithIgnoreCase(queryString, XA_END)) {
// xa start use TransactionEnd
@@ -196,11 +190,11 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
endBuilder.addProps(createSpecialPair(XA_TYPE, XA_END));
endBuilder.addProps(createSpecialPair(XA_XID, getXaXid(queryString, XA_END)));
TransactionEnd transactionEnd = endBuilder.build();
Header header = createHeader(binlogFileName, event.getHeader(), "", "", null);
Header header = createHeader(event.getHeader(), "", "", null);
return createEntry(header, EntryType.TRANSACTIONEND, transactionEnd.toByteString());
} else if (StringUtils.startsWithIgnoreCase(queryString, XA_COMMIT)) {
// xa commit
Header header = createHeader(binlogFileName, event.getHeader(), "", "", EventType.XACOMMIT);
Header header = createHeader(event.getHeader(), "", "", EventType.XACOMMIT);
RowChange.Builder rowChangeBuider = RowChange.newBuilder();
rowChangeBuider.setSql(queryString);
rowChangeBuider.addProps(createSpecialPair(XA_TYPE, XA_COMMIT));
@@ -209,7 +203,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
return createEntry(header, EntryType.ROWDATA, rowChangeBuider.build().toByteString());
} else if (StringUtils.startsWithIgnoreCase(queryString, XA_ROLLBACK)) {
// xa rollback
Header header = createHeader(binlogFileName, event.getHeader(), "", "", EventType.XAROLLBACK);
Header header = createHeader(event.getHeader(), "", "", EventType.XAROLLBACK);
RowChange.Builder rowChangeBuider = RowChange.newBuilder();
rowChangeBuider.setSql(queryString);
rowChangeBuider.addProps(createSpecialPair(XA_TYPE, XA_ROLLBACK));
@@ -218,11 +212,11 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
return createEntry(header, EntryType.ROWDATA, rowChangeBuider.build().toByteString());
} else if (StringUtils.endsWithIgnoreCase(queryString, BEGIN)) {
TransactionBegin transactionBegin = createTransactionBegin(event.getSessionId());
Header header = createHeader(binlogFileName, event.getHeader(), "", "", null);
Header header = createHeader(event.getHeader(), "", "", null);
return createEntry(header, EntryType.TRANSACTIONBEGIN, transactionBegin.toByteString());
} else if (StringUtils.endsWithIgnoreCase(queryString, COMMIT)) {
TransactionEnd transactionEnd = createTransactionEnd(0L); // MyISAM可能不会有xid事件
Header header = createHeader(binlogFileName, event.getHeader(), "", "", null);
Header header = createHeader(event.getHeader(), "", "", null);
return createEntry(header, EntryType.TRANSACTIONEND, transactionEnd.toByteString());
} else {
boolean notFilter = false;
@@ -265,7 +259,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
tableMetaCache.apply(position, event.getDbName(), queryString, null);
}
Header header = createHeader(binlogFileName, event.getHeader(), schemaName, tableName, type);
Header header = createHeader(event.getHeader(), schemaName, tableName, type);
RowChange.Builder rowChangeBuider = RowChange.newBuilder();
if (type != EventType.QUERY && type != EventType.INSERT && type != EventType.UPDATE
&& type != EventType.DELETE) {
@@ -425,52 +419,78 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
private Entry parseXidEvent(XidLogEvent event) {
TransactionEnd transactionEnd = createTransactionEnd(event.getXid());
Header header = createHeader(binlogFileName, event.getHeader(), "", "", null);
Header header = createHeader(event.getHeader(), "", "", null);
return createEntry(header, EntryType.TRANSACTIONEND, transactionEnd.toByteString());
}
private Entry parseRowsEvent(RowsLogEvent event) {
public TableMeta parseRowsEventForTableMeta(RowsLogEvent event) {
TableMapLogEvent table = event.getTable();
if (table == null) {
// tableId对应的记录不存在
throw new TableIdNotFoundException("not found tableId:" + event.getTableId());
}
boolean isHeartBeat = isAliSQLHeartBeat(table.getDbName(), table.getTableName());
boolean isRDSHeartBeat = tableMetaCache.isOnRDS() && isRDSHeartBeat(table.getDbName(), table.getTableName());
String fullname = table.getDbName() + "." + table.getTableName();
// check name filter
if (nameFilter != null && !nameFilter.filter(fullname)) {
return null;
}
if (nameBlackFilter != null && nameBlackFilter.filter(fullname)) {
return null;
}
// if (isHeartBeat || isRDSHeartBeat) {
// // 忽略rds模式的mysql.ha_health_check心跳数据
// return null;
// }
TableMeta tableMeta = null;
if (isRDSHeartBeat) {
// 处理rds模式的mysql.ha_health_check心跳数据
// 主要RDS的心跳表基本无权限,需要mock一个tableMeta
FieldMeta idMeta = new FieldMeta("id", "bigint(20)", true, false, "0");
FieldMeta typeMeta = new FieldMeta("type", "char(1)", false, true, "0");
tableMeta = new TableMeta(table.getDbName(), table.getTableName(), Arrays.asList(idMeta, typeMeta));
} else if (isHeartBeat) {
// 处理alisql模式的test.heartbeat心跳数据
// 心跳表基本无权限,需要mock一个tableMeta
FieldMeta idMeta = new FieldMeta("id", "smallint(6)", false, true, null);
FieldMeta typeMeta = new FieldMeta("ts", "int(11)", true, false, null);
tableMeta = new TableMeta(table.getDbName(), table.getTableName(), Arrays.asList(idMeta, typeMeta));
}
EntryPosition position = createPosition(event.getHeader());
if (tableMetaCache != null && tableMeta == null) {// 入错存在table meta
tableMeta = getTableMeta(table.getDbName(), table.getTableName(), true, position);
if (tableMeta == null) {
if (!filterTableError) {
throw new CanalParseException("not found [" + fullname + "] in db , pls check!");
}
}
}
return tableMeta;
}
public Entry parseRowsEvent(RowsLogEvent event) {
return parseRowsEvent(event, null);
}
public Entry parseRowsEvent(RowsLogEvent event, TableMeta tableMeta) {
if (filterRows) {
return null;
}
try {
TableMapLogEvent table = event.getTable();
if (table == null) {
// tableId对应的记录不存在
throw new TableIdNotFoundException("not found tableId:" + event.getTableId());
if (tableMeta == null) { // 如果没有外部指定
tableMeta = parseRowsEventForTableMeta(event);
}
boolean isHeartBeat = isAliSQLHeartBeat(table.getDbName(), table.getTableName());
boolean isRDSHeartBeat = tableMetaCache.isOnRDS()
&& isRDSHeartBeat(table.getDbName(), table.getTableName());
String fullname = table.getDbName() + "." + table.getTableName();
// check name filter
if (nameFilter != null && !nameFilter.filter(fullname)) {
if (tableMeta == null) {
// 拿不到表结构,执行忽略
return null;
}
if (nameBlackFilter != null && nameBlackFilter.filter(fullname)) {
return null;
}
// if (isHeartBeat || isRDSHeartBeat) {
// // 忽略rds模式的mysql.ha_health_check心跳数据
// return null;
// }
TableMeta tableMeta = null;
if (isRDSHeartBeat) {
// 处理rds模式的mysql.ha_health_check心跳数据
// 主要RDS的心跳表基本无权限,需要mock一个tableMeta
FieldMeta idMeta = new FieldMeta("id", "bigint(20)", true, false, "0");
FieldMeta typeMeta = new FieldMeta("type", "char(1)", false, true, "0");
tableMeta = new TableMeta(table.getDbName(), table.getTableName(), Arrays.asList(idMeta, typeMeta));
} else if (isHeartBeat) {
// 处理alisql模式的test.heartbeat心跳数据
// 心跳表基本无权限,需要mock一个tableMeta
FieldMeta idMeta = new FieldMeta("id", "smallint(6)", false, true, null);
FieldMeta typeMeta = new FieldMeta("ts", "int(11)", true, false, null);
tableMeta = new TableMeta(table.getDbName(), table.getTableName(), Arrays.asList(idMeta, typeMeta));
}
EventType eventType = null;
int type = event.getHeader().getType();
@@ -484,12 +504,9 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
throw new CanalParseException("unsupport event type :" + event.getHeader().getType());
}
Header header = createHeader(binlogFileName,
event.getHeader(),
table.getDbName(),
table.getTableName(),
eventType);
EntryPosition position = createPosition(event.getHeader());
TableMapLogEvent table = event.getTable();
Header header = createHeader(event.getHeader(), table.getDbName(), table.getTableName(), eventType);
RowChange.Builder rowChangeBuider = RowChange.newBuilder();
rowChangeBuider.setTableId(event.getTableId());
rowChangeBuider.setIsDdl(false);
@@ -498,18 +515,8 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
RowsLogBuffer buffer = event.getRowsBuf(charset.name());
BitSet columns = event.getColumns();
BitSet changeColumns = event.getChangeColumns();
boolean tableError = false;
if (tableMetaCache != null && tableMeta == null) {// 入错存在table meta
// cache
tableMeta = getTableMeta(table.getDbName(), table.getTableName(), true, position);
if (tableMeta == null) {
tableError = true;
if (!filterTableError) {
throw new CanalParseException("not found [" + fullname + "] in db , pls check!");
}
}
}
boolean tableError = false;
while (buffer.nextOneRow(columns)) {
// 处理row记录
RowData.Builder rowDataBuilder = RowData.newBuilder();
@@ -539,7 +546,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
logger.warn("table parser error : {}storeValue: {}", entry.toString(), rowChange.toString());
return null;
} else {
Entry entry = createEntry(header, EntryType.ROWDATA, rowChangeBuider.build().toByteString());
Entry entry = createEntry(header, EntryType.ROWDATA, rowChange.toByteString());
return entry;
}
} catch (Exception e) {
@@ -548,7 +555,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
}
private EntryPosition createPosition(LogHeader logHeader) {
return new EntryPosition(binlogFileName,
return new EntryPosition(logHeader.getLogFileName(),
logHeader.getLogPos(),
logHeader.getWhen() * 1000L,
logHeader.getServerId()); // 记录到秒
@@ -641,7 +648,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
isBinary = true;
} else if (StringUtils.containsIgnoreCase(fieldMeta.getColumnType(), "BINARY")) {
isBinary = true;
} else if (StringUtils.containsIgnoreCase(fieldMeta.getColumnType(), "tinyint(1)")) {
} else if (StringUtils.containsIgnoreCase(fieldMeta.getColumnType(), "TINYINT(1)")) {
isSingleBit = true;
}
}
@@ -771,7 +778,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
}
private Entry buildQueryEntry(String queryString, LogHeader logHeader, String tableName) {
Header header = createHeader(binlogFileName, logHeader, "", tableName, EventType.QUERY);
Header header = createHeader(logHeader, "", tableName, EventType.QUERY);
RowChange.Builder rowChangeBuider = RowChange.newBuilder();
rowChangeBuider.setSql(queryString);
rowChangeBuider.setEventType(EventType.QUERY);
@@ -779,19 +786,18 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
}
private Entry buildQueryEntry(String queryString, LogHeader logHeader) {
Header header = createHeader(binlogFileName, logHeader, "", "", EventType.QUERY);
Header header = createHeader(logHeader, "", "", EventType.QUERY);
RowChange.Builder rowChangeBuider = RowChange.newBuilder();
rowChangeBuider.setSql(queryString);
rowChangeBuider.setEventType(EventType.QUERY);
return createEntry(header, EntryType.ROWDATA, rowChangeBuider.build().toByteString());
}
private Header createHeader(String binlogFile, LogHeader logHeader, String schemaName, String tableName,
EventType eventType) {
private Header createHeader(LogHeader logHeader, String schemaName, String tableName, EventType eventType) {
// header会做信息冗余,方便以后做检索或者过滤
Header.Builder headerBuilder = Header.newBuilder();
headerBuilder.setVersion(version);
headerBuilder.setLogfileName(binlogFile);
headerBuilder.setLogfileName(logHeader.getLogFileName());
headerBuilder.setLogfileOffset(logHeader.getLogPos() - logHeader.getEventLen());
headerBuilder.setServerId(logHeader.getServerId());
headerBuilder.setServerenCode(UTF_8);// 经过java输出后所有的编码为unicode
+5
View File
@@ -263,6 +263,11 @@
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
<!-- log -->
<dependency>
<groupId>ch.qos.logback</groupId>