Merge branch 'master' into master

This commit is contained in:
agapple
2017-01-19 12:46:41 +08:00
committed by GitHub
42 changed files with 12045 additions and 4216 deletions

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<artifactId>canal.common</artifactId> <artifactId>canal.common</artifactId>

View File

@@ -1,12 +1,19 @@
package com.alibaba.otter.canal.common.utils; package com.alibaba.otter.canal.common.utils;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference; import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.serializer.JSONSerializer; import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;
import com.alibaba.fastjson.serializer.PropertyFilter; import com.alibaba.fastjson.serializer.PropertyFilter;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializeWriter; import com.alibaba.fastjson.serializer.SerializeWriter;
import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.serializer.SerializerFeature;
@@ -17,6 +24,12 @@ import com.alibaba.fastjson.serializer.SerializerFeature;
*/ */
public class JsonUtils { public class JsonUtils {
static {
SerializeConfig.getGlobalInstance().put(InetAddress.class, InetAddressSerializer.instance);
SerializeConfig.getGlobalInstance().put(Inet4Address.class, InetAddressSerializer.instance);
SerializeConfig.getGlobalInstance().put(Inet6Address.class, InetAddressSerializer.instance);
}
public static <T> T unmarshalFromByte(byte[] bytes, Class<T> targetClass) { public static <T> T unmarshalFromByte(byte[] bytes, Class<T> targetClass) {
return (T) JSON.parseObject(bytes, targetClass);// 默认为UTF-8 return (T) JSON.parseObject(bytes, targetClass);// 默认为UTF-8
} }
@@ -70,4 +83,21 @@ public class JsonUtils {
out.close(); out.close();
} }
} }
public static class InetAddressSerializer implements ObjectSerializer {
public static InetAddressSerializer instance = new InetAddressSerializer();
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType)
throws IOException {
if (object == null) {
serializer.writeNull();
return;
}
InetAddress address = (InetAddress) object;
// 优先使用name
serializer.write(address.getHostName());
}
}
} }

View File

@@ -87,6 +87,10 @@ public class ServerRunningMonitor extends AbstractCanalLifeCycle {
} }
public void init() {
processStart();
}
public void start() { public void start() {
super.start(); super.start();
processStart(); processStart();

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -140,10 +140,15 @@ public final class FormatDescriptionLogEvent extends StartLogEventV3 {
throw new IOException("Unknown binlog version: " + binlogVersion); throw new IOException("Unknown binlog version: " + binlogVersion);
} }
} }
public FormatDescriptionLogEvent(final int binlogVersion,int binlogChecksum){
this(binlogVersion);
this.header.checksumAlg = binlogChecksum;
}
public FormatDescriptionLogEvent(final int binlogVersion){ public FormatDescriptionLogEvent(final int binlogVersion){
this.binlogVersion = binlogVersion; this.binlogVersion = binlogVersion;
postHeaderLen = new short[ENUM_END_EVENT]; postHeaderLen = new short[ENUM_END_EVENT];
/* identify binlog format */ /* identify binlog format */
switch (binlogVersion) { switch (binlogVersion) {

View File

@@ -90,6 +90,7 @@ public final class RotateLogEvent extends LogEvent {
int filenameLen = buffer.limit() - filenameOffset; int filenameLen = buffer.limit() - filenameOffset;
if (filenameLen > FN_REFLEN - 1) filenameLen = FN_REFLEN - 1; if (filenameLen > FN_REFLEN - 1) filenameLen = FN_REFLEN - 1;
buffer.position(filenameOffset); buffer.position(filenameOffset);
filename = buffer.getFixString(filenameLen); filename = buffer.getFixString(filenameLen);
} }

View File

@@ -957,11 +957,10 @@ public final class RowsLogBuffer {
jsonValue.toJsonString(builder); jsonValue.toJsonString(builder);
value = builder.toString(); value = builder.toString();
buffer.position(position + len); buffer.position(position + len);
// byte[] binary = new byte[len]; // byte[] binary = new byte[len];
// buffer.fillBytes(binary, 0, len); // buffer.fillBytes(binary, 0, len);
// value = binary; // value = binary;
javaType = Types.VARBINARY; javaType = Types.VARCHAR;
length = len; length = len;
break; break;
} }

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -5,6 +5,7 @@ import java.util.Properties;
import org.I0Itec.zkclient.IZkStateListener; import org.I0Itec.zkclient.IZkStateListener;
import org.I0Itec.zkclient.exception.ZkNoNodeException; import org.I0Itec.zkclient.exception.ZkNoNodeException;
import org.I0Itec.zkclient.exception.ZkNodeExistsException;
import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.Watcher.Event.KeeperState;
@@ -172,6 +173,8 @@ public class CanalController {
if (zkclientx != null) { if (zkclientx != null) {
runningMonitor.setZkClient(zkclientx); runningMonitor.setZkClient(zkclientx);
} }
// 触发创建一下cid节点
runningMonitor.init();
return runningMonitor; return runningMonitor;
} }
})); }));
@@ -189,10 +192,10 @@ public class CanalController {
instanceConfigs.put(destination, config); instanceConfigs.put(destination, config);
} }
if (!config.getLazy() && !embededCanalServer.isStart(destination)) { if (!embededCanalServer.isStart(destination)) {
// HA机制启动 // HA机制启动
ServerRunningMonitor runningMonitor = ServerRunningMonitors.getRunningMonitor(destination); ServerRunningMonitor runningMonitor = ServerRunningMonitors.getRunningMonitor(destination);
if (!runningMonitor.isStart()) { if (!config.getLazy() && !runningMonitor.isStart()) {
runningMonitor.start(); runningMonitor.start();
} }
} }
@@ -389,10 +392,10 @@ public class CanalController {
final String destination = entry.getKey(); final String destination = entry.getKey();
InstanceConfig config = entry.getValue(); InstanceConfig config = entry.getValue();
// 创建destination的工作节点 // 创建destination的工作节点
if (!config.getLazy() && !embededCanalServer.isStart(destination)) { if (!embededCanalServer.isStart(destination)) {
// HA机制启动 // HA机制启动
ServerRunningMonitor runningMonitor = ServerRunningMonitors.getRunningMonitor(destination); ServerRunningMonitor runningMonitor = ServerRunningMonitors.getRunningMonitor(destination);
if (!runningMonitor.isStart()) { if (!config.getLazy() && !runningMonitor.isStart()) {
runningMonitor.start(); runningMonitor.start();
} }
} }
@@ -448,6 +451,9 @@ public class CanalController {
String parentDir = path.substring(0, path.lastIndexOf('/')); String parentDir = path.substring(0, path.lastIndexOf('/'));
zkclientx.createPersistent(parentDir, true); zkclientx.createPersistent(parentDir, true);
zkclientx.createEphemeral(path); zkclientx.createEphemeral(path);
} catch (ZkNodeExistsException e) {
// ignore
// 因为第一次启动时创建了cid,但在stop/start的时可能会关闭和新建,允许出现NodeExists问题s
} }
} }

View File

@@ -175,6 +175,7 @@ public class SpringInstanceConfigMonitor extends AbstractCanalLifeCycle implemen
InstanceAction action = actions.remove(destination); InstanceAction action = actions.remove(destination);
try { try {
action.stop(destination); action.stop(destination);
lastFiles.remove(destination);
logger.info("auto notify stop {} successful.", destination); logger.info("auto notify stop {} successful.", destination);
} catch (Throwable e) { } catch (Throwable e) {
logger.error("scan delete found[{}] but stop failed", destination, ExceptionUtils.getFullStackTrace(e)); logger.error("scan delete found[{}] but stop failed", destination, ExceptionUtils.getFullStackTrace(e));

View File

@@ -0,0 +1,163 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
default-autowire="byName">
<!-- properties -->
<bean class="com.alibaba.otter.canal.instance.spring.support.PropertyPlaceholderConfigurer" lazy-init="false">
<property name="ignoreResourceNotFound" value="true" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/><!-- 允许system覆盖 -->
<property name="locationNames">
<list>
<value>classpath:canal.properties</value>
<value>classpath:${canal.instance.destination:}/instance.properties</value>
</list>
</property>
</bean>
<bean id="socketAddressEditor" class="com.alibaba.otter.canal.instance.spring.support.SocketAddressEditor" />
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<ref bean="socketAddressEditor" />
</list>
</property>
</bean>
<bean id="instance" class="com.alibaba.otter.canal.instance.spring.CanalInstanceWithSpring">
<property name="destination" value="${canal.instance.destination}" />
<property name="eventParser">
<ref local="eventParser" />
</property>
<property name="eventSink">
<ref local="eventSink" />
</property>
<property name="eventStore">
<ref local="eventStore" />
</property>
<property name="metaManager">
<ref local="metaManager" />
</property>
<property name="alarmHandler">
<ref local="alarmHandler" />
</property>
<property name="eventFetcher">
<ref local="eventFetcher"/>
</property>
</bean>
<bean id="eventFetcher" class="com.alibaba.otter.canal.mq.fetch.CanalEventFetcherWithMq">
<property name="enable" value="${canal.instance.mq.enable}"/>
<property name="sendMode" value="${canal.instance.mq.sendMode}"/>
</bean>
<!-- 报警处理类 -->
<bean id="alarmHandler" class="com.alibaba.otter.canal.common.alarm.MsgAlarmHandler">
<property name="enabled" value="${canal.alarm.enable}" />
<property name="environment" value="${canal.alarm.environment}" />
<property name="intercenterApi" value="${canal.alarm.intercenter.api}" />
<property name="administrators" value="${canal.alarm.administrators}" />
</bean>
<bean id="zkClientx" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" >
<property name="targetClass" value="com.alibaba.otter.canal.common.zookeeper.ZkClientx" />
<property name="targetMethod" value="getZkClient" />
<property name="arguments">
<list>
<value>${canal.zkServers:127.0.0.1:2181}</value>
</list>
</property>
</bean>
<bean id="metaManager" class="com.alibaba.otter.canal.meta.PeriodMixedMetaManager">
<property name="zooKeeperMetaManager">
<bean class="com.alibaba.otter.canal.meta.ZooKeeperMetaManager">
<property name="zkClientx" ref="zkClientx" />
</bean>
</property>
<property name="period" value="${canal.zookeeper.flush.period:1000}" />
</bean>
<bean id="eventStore" class="com.alibaba.otter.canal.store.memory.MemoryEventStoreWithBuffer">
<property name="bufferSize" value="${canal.instance.memory.buffer.size:16384}" />
<property name="bufferMemUnit" value="${canal.instance.memory.buffer.memunit:1024}" />
<property name="batchMode" value="${canal.instance.memory.batch.mode:MEMSIZE}" />
<property name="ddlIsolation" value="${canal.instance.get.ddl.isolation:false}" />
</bean>
<bean id="eventSink" class="com.alibaba.otter.canal.sink.entry.EntryEventSink">
<property name="eventStore" ref="eventStore" />
</bean>
<bean id="eventParser" class="com.alibaba.otter.canal.parse.inbound.mysql.LocalBinlogEventParser">
<property name="destination" value="${canal.instance.destination}" />
<property name="alarmHandler" ref="alarmHandler" />
<!-- 解析过滤处理 -->
<property name="eventFilter">
<bean class="com.alibaba.otter.canal.filter.aviater.AviaterRegexFilter" >
<constructor-arg index="0" value="${canal.instance.filter.regex:.*\..*}" />
</bean>
</property>
<property name="eventBlackFilter">
<bean class="com.alibaba.otter.canal.filter.aviater.AviaterRegexFilter" >
<constructor-arg index="0" value="${canal.instance.filter.black.regex:}" />
<constructor-arg index="1" value="false" />
</bean>
</property>
<!-- 最大事务解析大小,超过该大小后事务将被切分为多个事务投递 -->
<property name="transactionSize" value="${canal.instance.transaction.size:1024}" />
<!-- 解析编码 -->
<property name="connectionCharset" value="${canal.instance.connectionCharset:UTF-8}" />
<!-- 解析位点记录 -->
<property name="logPositionManager">
<bean class="com.alibaba.otter.canal.parse.index.FailbackLogPositionManager">
<property name="primary">
<bean class="com.alibaba.otter.canal.parse.index.MemoryLogPositionManager" />
</property>
<property name="failback">
<bean class="com.alibaba.otter.canal.parse.index.MetaLogPositionManager">
<property name="metaManager" ref="metaManager" />
</bean>
</property>
</bean>
</property>
<!-- 解析数据库信息 -->
<property name="masterInfo">
<bean class="com.alibaba.otter.canal.parse.support.AuthenticationInfo">
<property name="address" value="${canal.instance.master.address}" />
<property name="username" value="${canal.instance.dbUsername:retl}" />
<property name="password" value="${canal.instance.dbPassword:retl}" />
<property name="defaultDatabaseName" value="${canal.instance.defaultDatabaseName:retl}" />
</bean>
</property>
<!-- 解析起始位点 -->
<property name="masterPosition">
<bean class="com.alibaba.otter.canal.protocol.position.EntryPosition">
<property name="journalName" value="${canal.instance.master.journal.name}" />
<property name="position" value="${canal.instance.master.position}" />
<property name="timestamp" value="${canal.instance.master.timestamp}" />
</bean>
</property>
<property name="filterQueryDml" value="${canal.instance.filter.query.dml:false}" />
<property name="filterQueryDcl" value="${canal.instance.filter.query.dcl:false}" />
<property name="filterQueryDdl" value="${canal.instance.filter.query.ddl:false}" />
<property name="filterRows" value="${canal.instance.filter.rows:false}" />
<property name="filterTableError" value="${canal.instance.filter.table.error:false}" />
<property name="needWait" value="${canal.instance.parser.needWait:true}"/>
<property name="directory" value="${canal.instance.parser.directory}"/>
</bean>
</beans>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -101,7 +101,8 @@ public class MysqlConnector {
MysqlUpdateExecutor executor = new MysqlUpdateExecutor(connector); MysqlUpdateExecutor executor = new MysqlUpdateExecutor(connector);
executor.update("KILL CONNECTION " + connectionId); executor.update("KILL CONNECTION " + connectionId);
} catch (Exception e) { } catch (Exception e) {
throw new IOException("KILL DUMP " + connectionId + " failure:" + ExceptionUtils.getStackTrace(e)); // 忽略具体异常
logger.info("KILL DUMP " + connectionId + " failure:" + ExceptionUtils.getStackTrace(e));
} finally { } finally {
if (connector != null) { if (connector != null) {
connector.disconnect(); connector.disconnect();

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<artifactId>canal.instance.core</artifactId> <artifactId>canal.instance.core</artifactId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -142,8 +142,8 @@ public class PeriodMixedMetaManager extends MemoryMetaManager implements CanalMe
} }
public void updateCursor(ClientIdentity clientIdentity, Position position) throws CanalMetaManagerException { public void updateCursor(ClientIdentity clientIdentity, Position position) throws CanalMetaManagerException {
updateCursorTasks.add(clientIdentity);// 添加到任务队列中进行触发
super.updateCursor(clientIdentity, position); super.updateCursor(clientIdentity, position);
updateCursorTasks.add(clientIdentity);// 添加到任务队列中进行触发
} }
public Position getCursor(ClientIdentity clientIdentity) throws CanalMetaManagerException { public Position getCursor(ClientIdentity clientIdentity) throws CanalMetaManagerException {

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<artifactId>canal.parse</artifactId> <artifactId>canal.parse</artifactId>

View File

@@ -22,7 +22,6 @@ import com.alibaba.otter.canal.parse.CanalEventParser;
import com.alibaba.otter.canal.parse.exception.CanalParseException; import com.alibaba.otter.canal.parse.exception.CanalParseException;
import com.alibaba.otter.canal.parse.exception.TableIdNotFoundException; import com.alibaba.otter.canal.parse.exception.TableIdNotFoundException;
import com.alibaba.otter.canal.parse.inbound.EventTransactionBuffer.TransactionFlushCallback; import com.alibaba.otter.canal.parse.inbound.EventTransactionBuffer.TransactionFlushCallback;
import com.alibaba.otter.canal.parse.inbound.mysql.MysqlEventParser;
import com.alibaba.otter.canal.parse.index.CanalLogPositionManager; import com.alibaba.otter.canal.parse.index.CanalLogPositionManager;
import com.alibaba.otter.canal.parse.support.AuthenticationInfo; import com.alibaba.otter.canal.parse.support.AuthenticationInfo;
import com.alibaba.otter.canal.protocol.CanalEntry; import com.alibaba.otter.canal.protocol.CanalEntry;
@@ -325,7 +324,6 @@ public abstract class AbstractEventParser<EVENT> extends AbstractCanalLifeCycle
startTs = System.currentTimeMillis(); startTs = System.currentTimeMillis();
} }
CanalEntry.Entry event = binlogParser.parse(bod); CanalEntry.Entry event = binlogParser.parse(bod);
if (enabled) { if (enabled) {
this.parsingInterval = System.currentTimeMillis() - startTs; this.parsingInterval = System.currentTimeMillis() - startTs;
} }
@@ -388,7 +386,11 @@ public abstract class AbstractEventParser<EVENT> extends AbstractCanalLifeCycle
String name = String.format("destination = %s , address = %s , HeartBeatTimeTask", String name = String.format("destination = %s , address = %s , HeartBeatTimeTask",
destination, destination,
runningInfo == null ? null : runningInfo.getAddress().toString()); runningInfo == null ? null : runningInfo.getAddress().toString());
synchronized (MysqlEventParser.class) { synchronized (AbstractEventParser.class) {
// synchronized (MysqlEventParser.class) {
// why use MysqlEventParser.class, u know, MysqlEventParser is
// the child class 4 AbstractEventParser,
// do this is ...
if (timer == null) { if (timer == null) {
timer = new Timer(name, true); timer = new Timer(name, true);
} }

View File

@@ -1,5 +1,6 @@
package com.alibaba.otter.canal.parse.inbound; package com.alibaba.otter.canal.parse.inbound;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
@@ -44,6 +45,17 @@ public class TableMeta {
this.fileds = fileds; this.fileds = fileds;
} }
public List<FieldMeta> getPrimaryFields() {
List<FieldMeta> primarys = new ArrayList<TableMeta.FieldMeta>();
for (FieldMeta meta : fileds) {
if (meta.isKey()) {
primarys.add(meta);
}
}
return primarys;
}
public static class FieldMeta { public static class FieldMeta {
private String columnName; private String columnName;

View File

@@ -8,7 +8,6 @@ import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.alibaba.otter.canal.parse.exception.CanalParseException;
import com.alibaba.otter.canal.parse.inbound.ErosaConnection; import com.alibaba.otter.canal.parse.inbound.ErosaConnection;
import com.alibaba.otter.canal.parse.inbound.SinkFunction; import com.alibaba.otter.canal.parse.inbound.SinkFunction;
import com.alibaba.otter.canal.parse.inbound.mysql.local.BinLogFileQueue; import com.alibaba.otter.canal.parse.inbound.mysql.local.BinLogFileQueue;
@@ -18,6 +17,7 @@ import com.taobao.tddl.dbsync.binlog.LogDecoder;
import com.taobao.tddl.dbsync.binlog.LogEvent; import com.taobao.tddl.dbsync.binlog.LogEvent;
import com.taobao.tddl.dbsync.binlog.LogPosition; import com.taobao.tddl.dbsync.binlog.LogPosition;
import com.taobao.tddl.dbsync.binlog.event.QueryLogEvent; import com.taobao.tddl.dbsync.binlog.event.QueryLogEvent;
import com.taobao.tddl.dbsync.binlog.event.RotateLogEvent;
/** /**
* local bin log connection (not real connection) * local bin log connection (not real connection)
@@ -83,9 +83,9 @@ public class LocalBinLogConnection implements ErosaConnection {
context.setLogPosition(new LogPosition(binlogfilename, binlogPosition)); context.setLogPosition(new LogPosition(binlogfilename, binlogPosition));
while (running) { while (running) {
boolean needContinue = true; boolean needContinue = true;
LogEvent event; LogEvent event = null;
while (fetcher.fetch()) { L:while (fetcher.fetch()) {
event = decoder.decode(fetcher, context); /*event = decoder.decode(fetcher, context);
if (event == null) { if (event == null) {
throw new CanalParseException("parse failed"); throw new CanalParseException("parse failed");
} }
@@ -93,15 +93,20 @@ public class LocalBinLogConnection implements ErosaConnection {
if (!func.sink(event)) { if (!func.sink(event)) {
needContinue = false; needContinue = false;
break; break;
} }*/
// do { do {
// event = decoder.decode(fetcher, context); if (event == null) {
// if (event != null && !func.sink(event)) { event = new RotateLogEvent(context.getLogPosition().getFileName(), context.getLogPosition().getPosition());
// needContinue = false; } else {
// break; event = decoder.decode(fetcher, context);
// } }
// } while (event != null);
if (event != null && !func.sink(event)) {
needContinue = false;
break L;
}
} while (event != null);
} }
if (needContinue) {// 读取下一个 if (needContinue) {// 读取下一个
@@ -145,6 +150,7 @@ public class LocalBinLogConnection implements ErosaConnection {
FileLogFetcher fetcher = new FileLogFetcher(bufferSize); FileLogFetcher fetcher = new FileLogFetcher(bufferSize);
LogDecoder decoder = new LogDecoder(); LogDecoder decoder = new LogDecoder();
decoder.handle(LogEvent.FORMAT_DESCRIPTION_EVENT);
decoder.handle(LogEvent.QUERY_EVENT); decoder.handle(LogEvent.QUERY_EVENT);
decoder.handle(LogEvent.XID_EVENT); decoder.handle(LogEvent.XID_EVENT);
LogContext context = new LogContext(); LogContext context = new LogContext();

View File

@@ -1,10 +1,14 @@
package com.alibaba.otter.canal.parse.inbound.mysql; package com.alibaba.otter.canal.parse.inbound.mysql;
import java.io.IOException;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import com.alibaba.otter.canal.parse.CanalEventParser; import com.alibaba.otter.canal.parse.CanalEventParser;
import com.alibaba.otter.canal.parse.exception.CanalParseException; import com.alibaba.otter.canal.parse.exception.CanalParseException;
import com.alibaba.otter.canal.parse.inbound.ErosaConnection; import com.alibaba.otter.canal.parse.inbound.ErosaConnection;
import com.alibaba.otter.canal.parse.inbound.mysql.dbsync.LogEventConvert;
import com.alibaba.otter.canal.parse.inbound.mysql.dbsync.TableMetaCache;
import com.alibaba.otter.canal.parse.index.CanalLogPositionManager; import com.alibaba.otter.canal.parse.index.CanalLogPositionManager;
import com.alibaba.otter.canal.parse.support.AuthenticationInfo; import com.alibaba.otter.canal.parse.support.AuthenticationInfo;
import com.alibaba.otter.canal.protocol.position.EntryPosition; import com.alibaba.otter.canal.protocol.position.EntryPosition;
@@ -21,7 +25,9 @@ public class LocalBinlogEventParser extends AbstractMysqlEventParser implements
// 数据库信息 // 数据库信息
private AuthenticationInfo masterInfo; private AuthenticationInfo masterInfo;
private EntryPosition masterPosition; // binlog信息 private EntryPosition masterPosition; // binlog信息
private MysqlConnection metaConnection; // 查询meta信息的链接
private TableMetaCache tableMetaCache; // 对应meta
private String directory; private String directory;
private boolean needWait = false; private boolean needWait = false;
private int bufferSize = 16 * 1024; private int bufferSize = 16 * 1024;
@@ -33,17 +39,60 @@ public class LocalBinlogEventParser extends AbstractMysqlEventParser implements
@Override @Override
protected ErosaConnection buildErosaConnection() { protected ErosaConnection buildErosaConnection() {
return buildLocalBinLogConnection(); return buildLocalBinLogConnection();
} }
@Override
protected void preDump(ErosaConnection connection) {
metaConnection = buildMysqlConnection();
try {
metaConnection.connect();
} catch (IOException e) {
throw new CanalParseException(e);
}
tableMetaCache = new TableMetaCache(metaConnection);
((LogEventConvert) binlogParser).setTableMetaCache(tableMetaCache);
}
public void start() throws CanalParseException { @Override
protected void afterDump(ErosaConnection connection) {
if (metaConnection != null) {
try {
metaConnection.disconnect();
} catch (IOException e) {
logger.error("ERROR # disconnect meta connection for address:{}", metaConnection.getConnector()
.getAddress(), e);
}
}
}
public void start() throws CanalParseException {
if (runningInfo == null) { // 第一次链接主库 if (runningInfo == null) { // 第一次链接主库
runningInfo = masterInfo; runningInfo = masterInfo;
} }
super.start(); super.start();
} }
@Override
public void stop() {
if (metaConnection != null) {
try {
metaConnection.disconnect();
} catch (IOException e) {
logger.error("ERROR # disconnect meta connection for address:{}", metaConnection.getConnector()
.getAddress(), e);
}
}
private ErosaConnection buildLocalBinLogConnection() { if (tableMetaCache != null) {
tableMetaCache.clearTableMeta();
}
super.stop();
}
private ErosaConnection buildLocalBinLogConnection() {
LocalBinLogConnection connection = new LocalBinLogConnection(); LocalBinLogConnection connection = new LocalBinLogConnection();
connection.setBufferSize(this.bufferSize); connection.setBufferSize(this.bufferSize);
@@ -52,6 +101,19 @@ public class LocalBinlogEventParser extends AbstractMysqlEventParser implements
return connection; return connection;
} }
private MysqlConnection buildMysqlConnection() {
MysqlConnection connection = new MysqlConnection(runningInfo.getAddress(),
runningInfo.getUsername(),
runningInfo.getPassword(),
connectionCharsetNumber,
runningInfo.getDefaultDatabaseName());
connection.getConnector().setReceiveBufferSize(64 * 1024);
connection.getConnector().setSendBufferSize(64 * 1024);
connection.getConnector().setSoTimeout(30 * 1000);
connection.setCharset(connectionCharset);
return connection;
}
@Override @Override
protected EntryPosition findStartPosition(ErosaConnection connection) { protected EntryPosition findStartPosition(ErosaConnection connection) {

View File

@@ -6,6 +6,7 @@ import java.nio.ByteBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.List; import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -24,6 +25,8 @@ import com.alibaba.otter.canal.parse.inbound.mysql.dbsync.DirectLogFetcher;
import com.taobao.tddl.dbsync.binlog.LogContext; import com.taobao.tddl.dbsync.binlog.LogContext;
import com.taobao.tddl.dbsync.binlog.LogDecoder; import com.taobao.tddl.dbsync.binlog.LogDecoder;
import com.taobao.tddl.dbsync.binlog.LogEvent; import com.taobao.tddl.dbsync.binlog.LogEvent;
import com.taobao.tddl.dbsync.binlog.LogPosition;
import com.taobao.tddl.dbsync.binlog.event.FormatDescriptionLogEvent;
public class MysqlConnection implements ErosaConnection { public class MysqlConnection implements ErosaConnection {
@@ -34,6 +37,7 @@ public class MysqlConnection implements ErosaConnection {
private Charset charset = Charset.forName("UTF-8"); private Charset charset = Charset.forName("UTF-8");
private BinlogFormat binlogFormat; private BinlogFormat binlogFormat;
private BinlogImage binlogImage; private BinlogImage binlogImage;
private int binlogChecksum;
public MysqlConnection(){ public MysqlConnection(){
} }
@@ -79,7 +83,7 @@ public class MysqlConnection implements ErosaConnection {
*/ */
public void seek(String binlogfilename, Long binlogPosition, SinkFunction func) throws IOException { public void seek(String binlogfilename, Long binlogPosition, SinkFunction func) throws IOException {
updateSettings(); updateSettings();
loadBinlogChecksum();
sendBinlogDump(binlogfilename, binlogPosition); sendBinlogDump(binlogfilename, binlogPosition);
DirectLogFetcher fetcher = new DirectLogFetcher(connector.getReceiveBufferSize()); DirectLogFetcher fetcher = new DirectLogFetcher(connector.getReceiveBufferSize());
fetcher.start(connector.getChannel()); fetcher.start(connector.getChannel());
@@ -89,6 +93,8 @@ public class MysqlConnection implements ErosaConnection {
decoder.handle(LogEvent.QUERY_EVENT); decoder.handle(LogEvent.QUERY_EVENT);
decoder.handle(LogEvent.XID_EVENT); decoder.handle(LogEvent.XID_EVENT);
LogContext context = new LogContext(); LogContext context = new LogContext();
context.setLogPosition(new LogPosition(binlogfilename));
context.setFormatDescription(new FormatDescriptionLogEvent(4, binlogChecksum));
while (fetcher.fetch()) { while (fetcher.fetch()) {
LogEvent event = null; LogEvent event = null;
event = decoder.decode(fetcher, context); event = decoder.decode(fetcher, context);
@@ -105,11 +111,14 @@ public class MysqlConnection implements ErosaConnection {
public void dump(String binlogfilename, Long binlogPosition, SinkFunction func) throws IOException { public void dump(String binlogfilename, Long binlogPosition, SinkFunction func) throws IOException {
updateSettings(); updateSettings();
loadBinlogChecksum();
sendBinlogDump(binlogfilename, binlogPosition); sendBinlogDump(binlogfilename, binlogPosition);
DirectLogFetcher fetcher = new DirectLogFetcher(connector.getReceiveBufferSize()); DirectLogFetcher fetcher = new DirectLogFetcher(connector.getReceiveBufferSize());
fetcher.start(connector.getChannel()); fetcher.start(connector.getChannel());
LogDecoder decoder = new LogDecoder(LogEvent.UNKNOWN_EVENT, LogEvent.ENUM_END_EVENT); LogDecoder decoder = new LogDecoder(LogEvent.UNKNOWN_EVENT, LogEvent.ENUM_END_EVENT);
LogContext context = new LogContext(); LogContext context = new LogContext();
context.setLogPosition(new LogPosition(binlogfilename));
context.setFormatDescription(new FormatDescriptionLogEvent(4, binlogChecksum));
while (fetcher.fetch()) { while (fetcher.fetch()) {
LogEvent event = null; LogEvent event = null;
event = decoder.decode(fetcher, context); event = decoder.decode(fetcher, context);
@@ -196,9 +205,12 @@ public class MysqlConnection implements ErosaConnection {
// 如果不设置会出现错误: Slave can not handle replication events with the // 如果不设置会出现错误: Slave can not handle replication events with the
// checksum that master is configured to log // checksum that master is configured to log
// 但也不能乱设置,需要和mysql server的checksum配置一致,不然RotateLogEvent会出现乱码 // 但也不能乱设置,需要和mysql server的checksum配置一致,不然RotateLogEvent会出现乱码
update("set @master_binlog_checksum= '@@global.binlog_checksum'"); // '@@global.binlog_checksum'需要去掉单引号,在mysql 5.6.29下导致master退出
update("set @master_binlog_checksum= @@global.binlog_checksum");
} catch (Exception e) { } catch (Exception e) {
logger.warn(ExceptionUtils.getFullStackTrace(e)); if (!StringUtils.contains(e.getMessage(), "Unknown system variable")) {
logger.warn(ExceptionUtils.getFullStackTrace(e));
}
} }
try { try {
@@ -256,6 +268,28 @@ public class MysqlConnection implements ErosaConnection {
} }
} }
/**
* 获取主库checksum信息
* https://dev.mysql.com/doc/refman/5.6/en/replication-options
* -binary-log.html#option_mysqld_binlog-checksum
*/
private void loadBinlogChecksum() {
ResultSetPacket rs = null;
try {
rs = query("select @master_binlog_checksum");
} catch (IOException e) {
throw new CanalParseException(e);
}
List<String> columnValues = rs.getFieldValues();
if (columnValues != null && columnValues.size() >= 1 && columnValues.get(0) != null
&& columnValues.get(0).toUpperCase().equals("CRC32")) {
binlogChecksum = LogEvent.BINLOG_CHECKSUM_ALG_CRC32;
} else {
binlogChecksum = LogEvent.BINLOG_CHECKSUM_ALG_OFF;
}
}
public static enum BinlogFormat { public static enum BinlogFormat {
STATEMENT("STATEMENT"), ROW("ROW"), MIXED("MIXED"); STATEMENT("STATEMENT"), ROW("ROW"), MIXED("MIXED");

View File

@@ -13,6 +13,7 @@ import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.commons.lang.exception.ExceptionUtils;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import com.alibaba.otter.canal.common.utils.JsonUtils;
import com.alibaba.otter.canal.parse.CanalEventParser; import com.alibaba.otter.canal.parse.CanalEventParser;
import com.alibaba.otter.canal.parse.CanalHASwitchable; import com.alibaba.otter.canal.parse.CanalHASwitchable;
import com.alibaba.otter.canal.parse.driver.mysql.packets.server.FieldPacket; import com.alibaba.otter.canal.parse.driver.mysql.packets.server.FieldPacket;
@@ -93,6 +94,7 @@ public class MysqlEventParser extends AbstractMysqlEventParser implements CanalE
for (BinlogFormat supportFormat : supportBinlogFormats) { for (BinlogFormat supportFormat : supportBinlogFormats) {
if (supportFormat != null && format == supportFormat) { if (supportFormat != null && format == supportFormat) {
found = true; found = true;
break;
} }
} }
@@ -107,6 +109,7 @@ public class MysqlEventParser extends AbstractMysqlEventParser implements CanalE
for (BinlogImage supportImage : supportBinlogImages) { for (BinlogImage supportImage : supportBinlogImages) {
if (supportImage != null && image == supportImage) { if (supportImage != null && image == supportImage) {
found = true; found = true;
break;
} }
} }
@@ -177,10 +180,9 @@ public class MysqlEventParser extends AbstractMysqlEventParser implements CanalE
} }
protected void stopHeartBeat() { protected void stopHeartBeat() {
TimerTask heartBeatTimerTask = this.heartBeatTimerTask;
super.stopHeartBeat(); super.stopHeartBeat();
if (heartBeatTimerTask != null && heartBeatTimerTask instanceof MysqlDetectingTimeTask) {
if (heartBeatTimerTask != null) {
MysqlConnection mysqlConnection = ((MysqlDetectingTimeTask) heartBeatTimerTask).getMysqlConnection(); MysqlConnection mysqlConnection = ((MysqlDetectingTimeTask) heartBeatTimerTask).getMysqlConnection();
try { try {
mysqlConnection.disconnect(); mysqlConnection.disconnect();
@@ -395,7 +397,7 @@ public class MysqlEventParser extends AbstractMysqlEventParser implements CanalE
// binlog定位位点失败,可能有两个原因: // binlog定位位点失败,可能有两个原因:
// 1. binlog位点被删除 // 1. binlog位点被删除
// 2.vip模式的mysql,发生了主备切换,判断一下serverId是否变化,针对这种模式可以发起一次基于时间戳查找合适的binlog位点 // 2.vip模式的mysql,发生了主备切换,判断一下serverId是否变化,针对这种模式可以发起一次基于时间戳查找合适的binlog位点
boolean case2 = (standbyInfo.getAddress() == null) boolean case2 = (standbyInfo == null || standbyInfo.getAddress() == null)
&& logPosition.getPostion().getServerId() != null && logPosition.getPostion().getServerId() != null
&& !logPosition.getPostion().getServerId().equals(findServerId(mysqlConnection)); && !logPosition.getPostion().getServerId().equals(findServerId(mysqlConnection));
if (case2) { if (case2) {
@@ -410,7 +412,8 @@ public class MysqlEventParser extends AbstractMysqlEventParser implements CanalE
} }
} }
// 其余情况 // 其余情况
logger.warn("prepare to find start position just last position"); logger.warn("prepare to find start position just last position\n {}",
JsonUtils.marshalToString(logPosition));
return logPosition.getPostion(); return logPosition.getPostion();
} else { } else {
// 针对切换的情况,考虑回退时间 // 针对切换的情况,考虑回退时间

View File

@@ -85,7 +85,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
// 是否跳过table相关的解析异常,比如表不存在或者列数量不匹配,issue 92 // 是否跳过table相关的解析异常,比如表不存在或者列数量不匹配,issue 92
private boolean filterTableError = false; private boolean filterTableError = false;
// 新增rows过滤,用于仅订阅除rows以外的数据 // 新增rows过滤,用于仅订阅除rows以外的数据
private boolean filterRows = false; private boolean filterRows = false;
public Entry parse(LogEvent logEvent) throws CanalParseException { public Entry parse(LogEvent logEvent) throws CanalParseException {
if (logEvent == null || logEvent instanceof UnknownLogEvent) { if (logEvent == null || logEvent instanceof UnknownLogEvent) {
@@ -326,6 +326,11 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
return null; return null;
} }
if (tableMetaCache.isOnRDS() && "mysql.ha_health_check".equals(fullname)) {
// 忽略rds模式的mysql.ha_health_check心跳数据
return null;
}
EventType eventType = null; EventType eventType = null;
int type = event.getHeader().getType(); int type = event.getHeader().getType();
if (LogEvent.WRITE_ROWS_EVENT_V1 == type || LogEvent.WRITE_ROWS_EVENT == type) { if (LogEvent.WRITE_ROWS_EVENT_V1 == type || LogEvent.WRITE_ROWS_EVENT == type) {
@@ -402,34 +407,51 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
private boolean parseOneRow(RowData.Builder rowDataBuilder, RowsLogEvent event, RowsLogBuffer buffer, BitSet cols, private boolean parseOneRow(RowData.Builder rowDataBuilder, RowsLogEvent event, RowsLogBuffer buffer, BitSet cols,
boolean isAfter, TableMeta tableMeta) throws UnsupportedEncodingException { boolean isAfter, TableMeta tableMeta) throws UnsupportedEncodingException {
final int columnCnt = event.getTable().getColumnCnt(); int columnCnt = event.getTable().getColumnCnt();
final ColumnInfo[] columnInfo = event.getTable().getColumnInfo(); ColumnInfo[] columnInfo = event.getTable().getColumnInfo();
boolean tableError = false; boolean tableError = false;
// check table fileds count,只能处理加字段 // check table fileds count,只能处理加字段
boolean existRDSNoPrimaryKey = false;
if (tableMeta != null && columnInfo.length > tableMeta.getFileds().size()) { if (tableMeta != null && columnInfo.length > tableMeta.getFileds().size()) {
// online ddl增加字段操作步骤: if (tableMetaCache.isOnRDS()) {
// 1. 新增一张临时表,将需要做ddl表的数据全量导入 // 特殊处理下RDS的场景
// 2. 在老表上建立I/U/D的trigger,增量的将数据插入到临时表 List<FieldMeta> primaryKeys = tableMeta.getPrimaryFields();
// 3. 锁住应用请求,将临时表rename为老表的名字,完成增加字段的操作 if (primaryKeys == null || primaryKeys.isEmpty()) {
// 尝试做一次reload,可能因为ddl没有正确解析,或者使用了类似online ddl的操作 if (columnInfo.length == tableMeta.getFileds().size() + 1
// 因为online ddl没有对应表名的alter语法,所以不会有clear cache的操作 && columnInfo[columnInfo.length - 1].type == LogEvent.MYSQL_TYPE_LONGLONG) {
tableMeta = getTableMeta(event.getTable().getDbName(), event.getTable().getTableName(), false);// 强制重新获取一次 existRDSNoPrimaryKey = true;
if (tableMeta == null) { }
tableError = true;
if (!filterTableError) {
throw new CanalParseException("not found [" + event.getTable().getDbName() + "."
+ event.getTable().getTableName() + "] in db , pls check!");
} }
} }
// 在做一次判断 if (!existRDSNoPrimaryKey) {
if (tableMeta != null && columnInfo.length > tableMeta.getFileds().size()) { // online ddl增加字段操作步骤:
tableError = true; // 1. 新增一张临时表,将需要做ddl表的数据全量导入
if (!filterTableError) { // 2. 在老表上建立I/U/D的trigger,增量的将数据插入到临时表
throw new CanalParseException("column size is not match for table:" + tableMeta.getFullName() + "," // 3. 锁住应用请求,将临时表rename为老表的名字,完成增加字段的操作
+ columnInfo.length + " vs " + tableMeta.getFileds().size()); // 尝试做一次reload,可能因为ddl没有正确解析,或者使用了类似online ddl的操作
// 因为online ddl没有对应表名的alter语法,所以不会有clear cache的操作
tableMeta = getTableMeta(event.getTable().getDbName(), event.getTable().getTableName(), false);// 强制重新获取一次
if (tableMeta == null) {
tableError = true;
if (!filterTableError) {
throw new CanalParseException("not found [" + event.getTable().getDbName() + "."
+ event.getTable().getTableName() + "] in db , pls check!");
}
} }
// 在做一次判断
if (tableMeta != null && columnInfo.length > tableMeta.getFileds().size()) {
tableError = true;
if (!filterTableError) {
throw new CanalParseException("column size is not match for table:" + tableMeta.getFullName()
+ "," + columnInfo.length + " vs " + tableMeta.getFileds().size());
}
}
} else {
logger.warn("[" + event.getTable().getDbName() + "." + event.getTable().getTableName()
+ "] is no primary key , skip alibaba_rds_row_id column");
} }
} }
@@ -440,6 +462,12 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
continue; continue;
} }
if (existRDSNoPrimaryKey && i == columnCnt - 1 && info.type == LogEvent.MYSQL_TYPE_LONGLONG) {
// 不解析最后一列
buffer.nextValue(info.type, info.meta, false);
continue;
}
Column.Builder columnBuilder = Column.newBuilder(); Column.Builder columnBuilder = Column.newBuilder();
FieldMeta fieldMeta = null; FieldMeta fieldMeta = null;
@@ -465,6 +493,10 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
} }
} }
buffer.nextValue(info.type, info.meta, isBinary); buffer.nextValue(info.type, info.meta, isBinary);
if (existRDSNoPrimaryKey && i == columnCnt - 1 && info.type == LogEvent.MYSQL_TYPE_LONGLONG) {
// 不解析最后一列
continue;
}
int javaType = buffer.getJavaType(); int javaType = buffer.getJavaType();
if (buffer.isNull()) { if (buffer.isNull()) {
@@ -718,7 +750,7 @@ public class LogEventConvert extends AbstractCanalLifeCycle implements BinlogPar
public void setFilterTableError(boolean filterTableError) { public void setFilterTableError(boolean filterTableError) {
this.filterTableError = filterTableError; this.filterTableError = filterTableError;
} }
public void setFilterRows(boolean filterRows) { public void setFilterRows(boolean filterRows) {
this.filterRows = filterRows; this.filterRows = filterRows;
} }

View File

@@ -32,6 +32,7 @@ public class TableMetaCache {
public static final String COLUMN_DEFAULT = "COLUMN_DEFAULT"; public static final String COLUMN_DEFAULT = "COLUMN_DEFAULT";
public static final String EXTRA = "EXTRA"; public static final String EXTRA = "EXTRA";
private MysqlConnection connection; private MysqlConnection connection;
private boolean isOnRDS = false;
// 第一层tableId,第二层schema.table,解决tableId重复,对应多张表 // 第一层tableId,第二层schema.table,解决tableId重复,对应多张表
private Map<String, TableMeta> tableMetaCache; private Map<String, TableMeta> tableMetaCache;
@@ -56,6 +57,13 @@ public class TableMetaCache {
}); });
try {
ResultSetPacket packet = connection.query("show global variables like 'rds\\_%'");
if (packet.getFieldValues().size() > 0) {
isOnRDS = true;
}
} catch (IOException e) {
}
} }
public TableMeta getTableMeta(String schema, String table) { public TableMeta getTableMeta(String schema, String table) {
@@ -136,4 +144,13 @@ public class TableMetaCache {
.append('`') .append('`')
.toString(); .toString();
} }
public boolean isOnRDS() {
return isOnRDS;
}
public void setOnRDS(boolean isOnRDS) {
this.isOnRDS = isOnRDS;
}
} }

View File

@@ -9,6 +9,8 @@ import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.IOFileFilter; import org.apache.commons.io.filefilter.IOFileFilter;
@@ -188,7 +190,9 @@ public class BinLogFileQueue {
files.addAll(FileUtils.listFiles(directory, new IOFileFilter() { files.addAll(FileUtils.listFiles(directory, new IOFileFilter() {
public boolean accept(File file) { public boolean accept(File file) {
return file.getName().startsWith(baseName); Pattern pattern = Pattern.compile("\\d+$");
Matcher matcher = pattern.matcher(file.getName());
return file.getName().startsWith(baseName) && matcher.find();
} }
public boolean accept(File dir, String name) { public boolean accept(File dir, String name) {

View File

@@ -13,6 +13,7 @@ import com.alibaba.otter.canal.parse.stub.AbstractCanalEventSinkTest;
import com.alibaba.otter.canal.parse.stub.AbstractCanalLogPositionManager; import com.alibaba.otter.canal.parse.stub.AbstractCanalLogPositionManager;
import com.alibaba.otter.canal.parse.support.AuthenticationInfo; import com.alibaba.otter.canal.parse.support.AuthenticationInfo;
import com.alibaba.otter.canal.protocol.CanalEntry.Entry; import com.alibaba.otter.canal.protocol.CanalEntry.Entry;
import com.alibaba.otter.canal.protocol.CanalEntry.EntryType;
import com.alibaba.otter.canal.protocol.position.EntryPosition; import com.alibaba.otter.canal.protocol.position.EntryPosition;
import com.alibaba.otter.canal.protocol.position.LogIdentity; import com.alibaba.otter.canal.protocol.position.LogIdentity;
import com.alibaba.otter.canal.protocol.position.LogPosition; import com.alibaba.otter.canal.protocol.position.LogPosition;
@@ -22,8 +23,8 @@ public class MysqlEventParserTest {
private static final String DETECTING_SQL = "insert into retl.xdual values(1,now()) on duplicate key update x=now()"; private static final String DETECTING_SQL = "insert into retl.xdual values(1,now()) on duplicate key update x=now()";
private static final String MYSQL_ADDRESS = "127.0.0.1"; private static final String MYSQL_ADDRESS = "127.0.0.1";
private static final String USERNAME = "xxxxx"; private static final String USERNAME = "root";
private static final String PASSWORD = "xxxxx"; private static final String PASSWORD = "xxxxxx";
@Test @Test
public void test_position() throws InterruptedException { public void test_position() throws InterruptedException {
@@ -45,20 +46,23 @@ public class MysqlEventParserTest {
public boolean sink(List<Entry> entrys, InetSocketAddress remoteAddress, String destination) public boolean sink(List<Entry> entrys, InetSocketAddress remoteAddress, String destination)
throws CanalSinkException { throws CanalSinkException {
for (Entry entry : entrys) { for (Entry entry : entrys) {
entryCount.incrementAndGet(); if (entry.getEntryType() != EntryType.HEARTBEAT) {
String logfilename = entry.getHeader().getLogfileName(); entryCount.incrementAndGet();
long logfileoffset = entry.getHeader().getLogfileOffset(); String logfilename = entry.getHeader().getLogfileName();
long executeTime = entry.getHeader().getExecuteTime(); long logfileoffset = entry.getHeader().getLogfileOffset();
entryPosition.setJournalName(logfilename); long executeTime = entry.getHeader().getExecuteTime();
entryPosition.setPosition(logfileoffset); entryPosition.setJournalName(logfilename);
entryPosition.setTimestamp(executeTime); entryPosition.setPosition(logfileoffset);
entryPosition.setTimestamp(executeTime);
break; break;
}
} }
controller.stop(); if (entryCount.get() > 0) {
timeoutChecker.stop(); controller.stop();
timeoutChecker.touch(); timeoutChecker.stop();
timeoutChecker.touch();
}
return true; return true;
} }
}); });
@@ -92,14 +96,14 @@ public class MysqlEventParserTest {
@Test @Test
public void test_timestamp() throws InterruptedException { public void test_timestamp() throws InterruptedException {
final TimeoutChecker timeoutChecker = new TimeoutChecker(30 * 1000); final TimeoutChecker timeoutChecker = new TimeoutChecker(3000 * 1000);
final AtomicLong entryCount = new AtomicLong(0); final AtomicLong entryCount = new AtomicLong(0);
final EntryPosition entryPosition = new EntryPosition(); final EntryPosition entryPosition = new EntryPosition();
final MysqlEventParser controller = new MysqlEventParser(); final MysqlEventParser controller = new MysqlEventParser();
final EntryPosition defaultPosition = buildPosition(null, null, 1322803601000L); final EntryPosition defaultPosition = buildPosition(null, null, 1475116855000L);
controller.setSlaveId(3344L); controller.setSlaveId(3344L);
controller.setDetectingEnable(true); controller.setDetectingEnable(false);
controller.setDetectingSQL(DETECTING_SQL); controller.setDetectingSQL(DETECTING_SQL);
controller.setMasterInfo(buildAuthentication()); controller.setMasterInfo(buildAuthentication());
controller.setMasterPosition(defaultPosition); controller.setMasterPosition(defaultPosition);
@@ -109,22 +113,25 @@ public class MysqlEventParserTest {
public boolean sink(List<Entry> entrys, InetSocketAddress remoteAddress, String destination) public boolean sink(List<Entry> entrys, InetSocketAddress remoteAddress, String destination)
throws CanalSinkException { throws CanalSinkException {
for (Entry entry : entrys) { for (Entry entry : entrys) {
entryCount.incrementAndGet(); if (entry.getEntryType() != EntryType.HEARTBEAT) {
entryCount.incrementAndGet();
String logfilename = entry.getHeader().getLogfileName(); String logfilename = entry.getHeader().getLogfileName();
long logfileoffset = entry.getHeader().getLogfileOffset(); long logfileoffset = entry.getHeader().getLogfileOffset();
long executeTime = entry.getHeader().getExecuteTime(); long executeTime = entry.getHeader().getExecuteTime();
entryPosition.setJournalName(logfilename); entryPosition.setJournalName(logfilename);
entryPosition.setPosition(logfileoffset); entryPosition.setPosition(logfileoffset);
entryPosition.setTimestamp(executeTime); entryPosition.setTimestamp(executeTime);
break;
break; }
} }
controller.stop(); if (entryCount.get() > 0) {
timeoutChecker.stop(); controller.stop();
timeoutChecker.touch(); timeoutChecker.stop();
timeoutChecker.touch();
}
return true; return true;
} }
}); });
@@ -174,21 +181,26 @@ public class MysqlEventParserTest {
public boolean sink(List<Entry> entrys, InetSocketAddress remoteAddress, String destination) public boolean sink(List<Entry> entrys, InetSocketAddress remoteAddress, String destination)
throws CanalSinkException { throws CanalSinkException {
for (Entry entry : entrys) { for (Entry entry : entrys) {
entryCount.incrementAndGet(); if (entry.getEntryType() != EntryType.HEARTBEAT) {
String logfilename = entry.getHeader().getLogfileName(); entryCount.incrementAndGet();
long logfileoffset = entry.getHeader().getLogfileOffset();
long executeTime = entry.getHeader().getExecuteTime();
entryPosition.setJournalName(logfilename); String logfilename = entry.getHeader().getLogfileName();
entryPosition.setPosition(logfileoffset); long logfileoffset = entry.getHeader().getLogfileOffset();
entryPosition.setTimestamp(executeTime); long executeTime = entry.getHeader().getExecuteTime();
break;
entryPosition.setJournalName(logfilename);
entryPosition.setPosition(logfileoffset);
entryPosition.setTimestamp(executeTime);
break;
}
} }
controller.stop(); if (entryCount.get() > 0) {
timeoutChecker.stop(); controller.stop();
timeoutChecker.touch(); timeoutChecker.stop();
timeoutChecker.touch();
}
return true; return true;
} }
}); });
@@ -244,22 +256,27 @@ public class MysqlEventParserTest {
public boolean sink(List<Entry> entrys, InetSocketAddress remoteAddress, String destination) public boolean sink(List<Entry> entrys, InetSocketAddress remoteAddress, String destination)
throws CanalSinkException { throws CanalSinkException {
for (Entry entry : entrys) { for (Entry entry : entrys) {
entryCount.incrementAndGet(); if (entry.getEntryType() != EntryType.HEARTBEAT) {
entryCount.incrementAndGet();
// String logfilename = entry.getHeader().getLogfileName(); // String logfilename =
// long logfileoffset = // entry.getHeader().getLogfileName();
// entry.getHeader().getLogfileOffset(); // long logfileoffset =
long executeTime = entry.getHeader().getExecuteTime(); // entry.getHeader().getLogfileOffset();
long executeTime = entry.getHeader().getExecuteTime();
// entryPosition.setJournalName(logfilename); // entryPosition.setJournalName(logfilename);
// entryPosition.setPosition(logfileoffset); // entryPosition.setPosition(logfileoffset);
entryPosition.setTimestamp(executeTime); entryPosition.setTimestamp(executeTime);
break; break;
}
} }
controller.stop(); if (entryCount.get() > 0) {
timeoutChecker.stop(); controller.stop();
timeoutChecker.touch(); timeoutChecker.stop();
timeoutChecker.touch();
}
return true; return true;
} }
}); });

View File

@@ -4,7 +4,7 @@
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>canal module for otter ${project.version}</name> <name>canal module for otter ${project.version}</name>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<url>https://github.com/alibaba/canal</url> <url>https://github.com/alibaba/canal</url>
<parent> <parent>
<groupId>org.sonatype.oss</groupId> <groupId>org.sonatype.oss</groupId>
@@ -202,7 +202,7 @@
<dependency> <dependency>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId> <artifactId>protobuf-java</artifactId>
<version>2.4.1</version> <version>2.6.1</version>
</dependency> </dependency>
<!-- log --> <!-- log -->
<dependency> <dependency>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<artifactId>canal.server</artifactId> <artifactId>canal.server</artifactId>

View File

@@ -131,6 +131,8 @@ public class CanalServerWithEmbedded extends AbstractCanalLifeCycle implements C
*/ */
@Override @Override
public void subscribe(ClientIdentity clientIdentity) throws CanalServerException { public void subscribe(ClientIdentity clientIdentity) throws CanalServerException {
checkStart(clientIdentity.getDestination());
CanalInstance canalInstance = canalInstances.get(clientIdentity.getDestination()); CanalInstance canalInstance = canalInstances.get(clientIdentity.getDestination());
if (!canalInstance.getMetaManager().isStart()) { if (!canalInstance.getMetaManager().isStart()) {
canalInstance.getMetaManager().start(); canalInstance.getMetaManager().start();

View File

@@ -66,7 +66,6 @@ public class SessionHandler extends SimpleChannelHandler {
Short.valueOf(sub.getClientId()), Short.valueOf(sub.getClientId()),
sub.getFilter()); sub.getFilter());
MDC.put("destination", clientIdentity.getDestination()); MDC.put("destination", clientIdentity.getDestination());
embeddedServer.subscribe(clientIdentity);
// 尝试启动,如果已经启动,忽略 // 尝试启动,如果已经启动,忽略
if (!embeddedServer.isStart(clientIdentity.getDestination())) { if (!embeddedServer.isStart(clientIdentity.getDestination())) {
@@ -76,6 +75,7 @@ public class SessionHandler extends SimpleChannelHandler {
} }
} }
embeddedServer.subscribe(clientIdentity);
ctx.setAttachment(clientIdentity);// 设置状态数据 ctx.setAttachment(clientIdentity);// 设置状态数据
NettyUtils.ack(ctx.getChannel(), null); NettyUtils.ack(ctx.getChannel(), null);
} else { } else {

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>
<artifactId>canal</artifactId> <artifactId>canal</artifactId>
<version>1.0.23-SNAPSHOT</version> <version>1.0.24-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>com.alibaba.otter</groupId> <groupId>com.alibaba.otter</groupId>