mirror of
https://github.com/alibaba/canal.git
synced 2026-11-04 00:30:15 +08:00
init
This commit is contained in:
123
example/pom.xml
Normal file
123
example/pom.xml
Normal file
@@ -0,0 +1,123 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.alibaba.otter</groupId>
|
||||
<artifactId>canal</artifactId>
|
||||
<version>1.0.19-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<groupId>com.alibaba.otter</groupId>
|
||||
<artifactId>canal.example</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>canal example module for otter ${project.version}</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.otter</groupId>
|
||||
<artifactId>canal.client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.otter</groupId>
|
||||
<artifactId>canal.protocol</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- test dependency -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- deploy模块的packaging通常是jar,如果项目中没有java 源代码或资源文件,加上这一段配置使项目能通过构建 -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<addMavenDescriptor>true</addMavenDescriptor>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<!-- 这是最新版本,推荐使用这个版本 -->
|
||||
<version>2.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>assemble</id>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<attach>false</attach>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>dev</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
<property>
|
||||
<name>env</name>
|
||||
<value>!release</value>
|
||||
</property>
|
||||
</activation>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- maven assembly插件需要一个描述文件 来告诉插件包的结构以及打包所需的文件来自哪里 -->
|
||||
<descriptors>
|
||||
<descriptor>${basedir}/src/main/assembly/dev.xml</descriptor>
|
||||
</descriptors>
|
||||
<finalName>canal-example</finalName>
|
||||
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>env</name>
|
||||
<value>release</value>
|
||||
</property>
|
||||
</activation>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- 发布模式使用的maven assembly插件描述文件 -->
|
||||
<descriptors>
|
||||
<descriptor>${basedir}/src/main/assembly/release.xml</descriptor>
|
||||
</descriptors>
|
||||
<!-- 如果一个应用的包含多个deploy模块,如果使用同样的包名, 如果把它们复制的一个目录中可能会失败,所以包名加了 artifactId以示区分 -->
|
||||
<finalName>${project.artifactId}-${project.version}</finalName>
|
||||
<!-- scm 要求 release 模式打出的包放到顶级目录下的target子目录中 -->
|
||||
<outputDirectory>${project.parent.build.directory}</outputDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
54
example/src/main/assembly/dev.xml
Normal file
54
example/src/main/assembly/dev.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
|
||||
<id>dist</id>
|
||||
<formats>
|
||||
<format>dir</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>.</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>README*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>./src/main/bin</directory>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
<fileMode>0755</fileMode>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>./src/main/conf</directory>
|
||||
<outputDirectory>/conf</outputDirectory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>./src/main/resources</directory>
|
||||
<outputDirectory>/conf</outputDirectory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>target</directory>
|
||||
<outputDirectory>logs</outputDirectory>
|
||||
<excludes>
|
||||
<exclude>**/*</exclude>
|
||||
</excludes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>lib</outputDirectory>
|
||||
<excludes>
|
||||
<exclude>junit:junit</exclude>
|
||||
</excludes>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
||||
54
example/src/main/assembly/release.xml
Normal file
54
example/src/main/assembly/release.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
|
||||
<id>dist</id>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>.</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>README*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>./src/main/bin</directory>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
<fileMode>0755</fileMode>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>./src/main/conf</directory>
|
||||
<outputDirectory>/conf</outputDirectory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>./src/main/resources</directory>
|
||||
<outputDirectory>/conf</outputDirectory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>target</directory>
|
||||
<outputDirectory>logs</outputDirectory>
|
||||
<excludes>
|
||||
<exclude>**/*</exclude>
|
||||
</excludes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>lib</outputDirectory>
|
||||
<excludes>
|
||||
<exclude>junit:junit</exclude>
|
||||
</excludes>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
||||
26
example/src/main/bin/startup.bat
Executable file
26
example/src/main/bin/startup.bat
Executable file
@@ -0,0 +1,26 @@
|
||||
@echo off
|
||||
@if not "%ECHO%" == "" echo %ECHO%
|
||||
@if "%OS%" == "Windows_NT" setlocal
|
||||
|
||||
set ENV_PATH=.\
|
||||
if "%OS%" == "Windows_NT" set ENV_PATH=%~dp0%
|
||||
|
||||
set conf_dir=%ENV_PATH%\..\conf
|
||||
set logback_configurationFile=%conf_dir%\logback.xml
|
||||
set client_mode=Simple
|
||||
if "%1%" != "" set client_mode=%1%
|
||||
|
||||
set CLASSPATH=%conf_dir%
|
||||
set CLASSPATH=%conf_dir%\..\lib\*;%CLASSPATH%
|
||||
|
||||
set JAVA_MEM_OPTS= -Xms128m -Xmx512m -XX:PermSize=128m
|
||||
set JAVA_OPTS_EXT= -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dapplication.codeset=UTF-8 -Dfile.encoding=UTF-8
|
||||
set JAVA_DEBUG_OPT= -server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=9199,server=y,suspend=n
|
||||
set CANAL_OPTS= -DappName=otter-canal-example -Dlogback.configurationFile="%logback_configurationFile%"
|
||||
|
||||
set JAVA_OPTS= %JAVA_MEM_OPTS% %JAVA_OPTS_EXT% %JAVA_DEBUG_OPT% %CANAL_OPTS%
|
||||
|
||||
if "%client_mode%" == "Cluster"
|
||||
java %JAVA_OPTS% -classpath "%CLASSPATH%" com.alibaba.otter.canal.example.ClusterCanalClientTest
|
||||
else
|
||||
java %JAVA_OPTS% -classpath "%CLASSPATH%" com.alibaba.otter.canal.example.SimpleCanalClientTest
|
||||
96
example/src/main/bin/startup.sh
Normal file
96
example/src/main/bin/startup.sh
Normal file
@@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
|
||||
current_path=`pwd`
|
||||
case "`uname`" in
|
||||
Linux)
|
||||
bin_abs_path=$(readlink -f $(dirname $0))
|
||||
;;
|
||||
*)
|
||||
bin_abs_path=`cd $(dirname $0); pwd`
|
||||
;;
|
||||
esac
|
||||
base=${bin_abs_path}/..
|
||||
client_mode="Simple"
|
||||
logback_configurationFile=$base/conf/logback.xml
|
||||
export LANG=en_US.UTF-8
|
||||
export BASE=$base
|
||||
|
||||
if [ -f $base/bin/canal.pid ] ; then
|
||||
echo "found canal.pid , Please run stop.sh first ,then startup.sh" 2>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
## set java path
|
||||
if [ -z "$JAVA" ] ; then
|
||||
JAVA=$(which java)
|
||||
fi
|
||||
|
||||
ALIBABA_JAVA="/usr/alibaba/java/bin/java"
|
||||
TAOBAO_JAVA="/opt/taobao/java/bin/java"
|
||||
if [ -z "$JAVA" ]; then
|
||||
if [ -f $ALIBABA_JAVA ] ; then
|
||||
JAVA=$ALIBABA_JAVA
|
||||
elif [ -f $TAOBAO_JAVA ] ; then
|
||||
JAVA=$TAOBAO_JAVA
|
||||
else
|
||||
echo "Cannot find a Java JDK. Please set either set JAVA or put java (>=1.5) in your PATH." 2>&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
case "$#"
|
||||
in
|
||||
0 )
|
||||
;;
|
||||
1 )
|
||||
client_mode=$*
|
||||
;;
|
||||
2 )
|
||||
if [ "$1" = "debug" ]; then
|
||||
DEBUG_PORT=$2
|
||||
DEBUG_SUSPEND="y"
|
||||
JAVA_DEBUG_OPT="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=$DEBUG_PORT,server=y,suspend=$DEBUG_SUSPEND"
|
||||
else
|
||||
client_mode=$1
|
||||
fi;;
|
||||
* )
|
||||
echo "THE PARAMETERS MUST BE TWO OR LESS.PLEASE CHECK AGAIN."
|
||||
exit;;
|
||||
esac
|
||||
|
||||
str=`file $JAVA_HOME/bin/java | grep 64-bit`
|
||||
if [ -n "$str" ]; then
|
||||
JAVA_OPTS="-server -Xms2048m -Xmx3072m -Xmn1024m -XX:SurvivorRatio=2 -XX:PermSize=96m -XX:MaxPermSize=256m -Xss256k -XX:-UseAdaptiveSizePolicy -XX:MaxTenuringThreshold=15 -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:+HeapDumpOnOutOfMemoryError"
|
||||
else
|
||||
JAVA_OPTS="-server -Xms1024m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:MaxPermSize=128m "
|
||||
fi
|
||||
|
||||
JAVA_OPTS=" $JAVA_OPTS -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8"
|
||||
CANAL_OPTS="-DappName=otter-canal-example -Dlogback.configurationFile=$logback_configurationFile"
|
||||
|
||||
if [ -e $logback_configurationFile ]
|
||||
then
|
||||
|
||||
for i in $base/lib/*;
|
||||
do CLASSPATH=$i:"$CLASSPATH";
|
||||
done
|
||||
CLASSPATH="$base/conf:$CLASSPATH";
|
||||
|
||||
echo "cd to $bin_abs_path for workaround relative path"
|
||||
cd $bin_abs_path
|
||||
|
||||
echo LOG CONFIGURATION : $logback_configurationFile
|
||||
echo client mode : $client_mode
|
||||
echo CLASSPATH :$CLASSPATH
|
||||
if [ $client_mode == "Cluster" ] ; then
|
||||
$JAVA $JAVA_OPTS $JAVA_DEBUG_OPT $CANAL_OPTS -classpath .:$CLASSPATH com.alibaba.otter.canal.example.ClusterCanalClientTest 1>>$base/bin/nohup.out 2>&1 &
|
||||
else
|
||||
$JAVA $JAVA_OPTS $JAVA_DEBUG_OPT $CANAL_OPTS -classpath .:$CLASSPATH com.alibaba.otter.canal.example.SimpleCanalClientTest 1>>$base/bin/nohup.out 2>&1 &
|
||||
fi
|
||||
|
||||
echo $! > $base/bin/canal.pid
|
||||
echo "cd to $current_path for continue"
|
||||
cd $current_path
|
||||
else
|
||||
echo "client mode("$client_mode") OR log configration file($logback_configurationFile) is not exist,please create then first!"
|
||||
fi
|
||||
53
example/src/main/bin/stop.sh
Normal file
53
example/src/main/bin/stop.sh
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
|
||||
cygwin=false;
|
||||
case "`uname`" in
|
||||
CYGWIN*)
|
||||
cygwin=true
|
||||
;;
|
||||
esac
|
||||
|
||||
get_pid() {
|
||||
STR=$1
|
||||
PID=$2
|
||||
if $cygwin; then
|
||||
JAVA_CMD="$JAVA_HOME\bin\java"
|
||||
JAVA_CMD=`cygpath --path --unix $JAVA_CMD`
|
||||
JAVA_PID=`ps |grep $JAVA_CMD |awk '{print $1}'`
|
||||
else
|
||||
if [ ! -z "$PID" ]; then
|
||||
JAVA_PID=`ps -C java -f --width 1000|grep "$STR"|grep "$PID"|grep -v grep|awk '{print $2}'`
|
||||
else
|
||||
JAVA_PID=`ps -C java -f --width 1000|grep "$STR"|grep -v grep|awk '{print $2}'`
|
||||
fi
|
||||
fi
|
||||
echo $JAVA_PID;
|
||||
}
|
||||
|
||||
base=`dirname $0`/..
|
||||
pidfile=$base/bin/canal.pid
|
||||
if [ ! -f "$pidfile" ];then
|
||||
echo "canal is not running. exists"
|
||||
exit
|
||||
fi
|
||||
|
||||
pid=`cat $pidfile`
|
||||
if [ "$pid" == "" ] ; then
|
||||
pid=`get_pid "appName=otter-canal-example"`
|
||||
fi
|
||||
|
||||
echo -e "`hostname`: stopping canal $pid ... "
|
||||
kill $pid
|
||||
|
||||
LOOPS=0
|
||||
while (true);
|
||||
do
|
||||
gpid=`get_pid "appName=otter-canal-example" "$pid"`
|
||||
if [ "$gpid" == "" ] ; then
|
||||
echo "Oook! cost:$LOOPS"
|
||||
`rm $pidfile`
|
||||
break;
|
||||
fi
|
||||
let LOOPS=LOOPS+1
|
||||
sleep 1
|
||||
done
|
||||
73
example/src/main/conf/logback.xml
Normal file
73
example/src/main/conf/logback.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<configuration scan="true" scanPeriod=" 5 seconds">
|
||||
<jmxConfigurator />
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{56} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="CANAL-ROOT" class="ch.qos.logback.classic.sift.SiftingAppender">
|
||||
<discriminator>
|
||||
<Key>destination</Key>
|
||||
<DefaultValue>canal</DefaultValue>
|
||||
</discriminator>
|
||||
<sift>
|
||||
<appender name="FILE-${destination}" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<File>../logs/${destination}/${destination}.log</File>
|
||||
<rollingPolicy
|
||||
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- rollover daily -->
|
||||
<fileNamePattern>../logs/${destination}/%d{yyyy-MM-dd}/${destination}-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<!-- or whenever the file size reaches 100MB -->
|
||||
<maxFileSize>512MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>
|
||||
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{56} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
</sift>
|
||||
</appender>
|
||||
|
||||
<appender name="ENTRY_LOG" class="ch.qos.logback.classic.sift.SiftingAppender">
|
||||
<discriminator>
|
||||
<Key>destination</Key>
|
||||
<DefaultValue>canal</DefaultValue>
|
||||
</discriminator>
|
||||
<sift>
|
||||
<appender name="ENTRY_LOG_${destination}"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<File>../logs/${destination}/entry.log</File>
|
||||
<rollingPolicy
|
||||
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- rollover daily -->
|
||||
<fileNamePattern>../logs/${destination}/%d{yyyy-MM-dd}/entry-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>512MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%msg</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
</sift>
|
||||
</appender>
|
||||
|
||||
<logger name="com.alibaba.otter.canal.example.AbstractCanalClientTest" additivity="false">
|
||||
<level value="INFO" />
|
||||
<appender-ref ref="ENTRY_LOG" />
|
||||
</logger>
|
||||
|
||||
<root level="INFO">
|
||||
<!--
|
||||
<appender-ref ref="STDOUT"/>
|
||||
-->
|
||||
<appender-ref ref="CANAL-ROOT" />
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,253 @@
|
||||
package com.alibaba.otter.canal.example;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.MDC;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.alibaba.otter.canal.client.CanalConnector;
|
||||
import com.alibaba.otter.canal.protocol.CanalEntry.Column;
|
||||
import com.alibaba.otter.canal.protocol.CanalEntry.Entry;
|
||||
import com.alibaba.otter.canal.protocol.CanalEntry.EntryType;
|
||||
import com.alibaba.otter.canal.protocol.CanalEntry.EventType;
|
||||
import com.alibaba.otter.canal.protocol.CanalEntry.RowChange;
|
||||
import com.alibaba.otter.canal.protocol.CanalEntry.RowData;
|
||||
import com.alibaba.otter.canal.protocol.CanalEntry.TransactionBegin;
|
||||
import com.alibaba.otter.canal.protocol.CanalEntry.TransactionEnd;
|
||||
import com.alibaba.otter.canal.protocol.Message;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* 测试基类
|
||||
*
|
||||
* @author jianghang 2013-4-15 下午04:17:12
|
||||
* @version 1.0.4
|
||||
*/
|
||||
public class AbstractCanalClientTest {
|
||||
|
||||
protected final static Logger logger = LoggerFactory.getLogger(AbstractCanalClientTest.class);
|
||||
protected static final String SEP = SystemUtils.LINE_SEPARATOR;
|
||||
protected static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||
protected volatile boolean running = false;
|
||||
protected Thread.UncaughtExceptionHandler handler = new Thread.UncaughtExceptionHandler() {
|
||||
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
logger.error("parse events has an error", e);
|
||||
}
|
||||
};
|
||||
protected Thread thread = null;
|
||||
protected CanalConnector connector;
|
||||
protected static String context_format = null;
|
||||
protected static String row_format = null;
|
||||
protected static String transaction_format = null;
|
||||
protected String destination;
|
||||
|
||||
static {
|
||||
context_format = SEP + "****************************************************" + SEP;
|
||||
context_format += "* Batch Id: [{}] ,count : [{}] , memsize : [{}] , Time : {}" + SEP;
|
||||
context_format += "* Start : [{}] " + SEP;
|
||||
context_format += "* End : [{}] " + SEP;
|
||||
context_format += "****************************************************" + SEP;
|
||||
|
||||
row_format = SEP
|
||||
+ "----------------> binlog[{}:{}] , name[{},{}] , eventType : {} , executeTime : {} , delay : {}ms"
|
||||
+ SEP;
|
||||
|
||||
transaction_format = SEP + "================> binlog[{}:{}] , executeTime : {} , delay : {}ms" + SEP;
|
||||
|
||||
}
|
||||
|
||||
public AbstractCanalClientTest(String destination){
|
||||
this(destination, null);
|
||||
}
|
||||
|
||||
public AbstractCanalClientTest(String destination, CanalConnector connector){
|
||||
this.destination = destination;
|
||||
this.connector = connector;
|
||||
}
|
||||
|
||||
protected void start() {
|
||||
Assert.notNull(connector, "connector is null");
|
||||
thread = new Thread(new Runnable() {
|
||||
|
||||
public void run() {
|
||||
process();
|
||||
}
|
||||
});
|
||||
|
||||
thread.setUncaughtExceptionHandler(handler);
|
||||
thread.start();
|
||||
running = true;
|
||||
}
|
||||
|
||||
protected void stop() {
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
running = false;
|
||||
if (thread != null) {
|
||||
try {
|
||||
thread.join();
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
MDC.remove("destination");
|
||||
}
|
||||
|
||||
protected void process() {
|
||||
int batchSize = 5 * 1024;
|
||||
while (running) {
|
||||
try {
|
||||
MDC.put("destination", destination);
|
||||
connector.connect();
|
||||
connector.subscribe();
|
||||
while (running) {
|
||||
Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
|
||||
long batchId = message.getId();
|
||||
int size = message.getEntries().size();
|
||||
if (batchId == -1 || size == 0) {
|
||||
// try {
|
||||
// Thread.sleep(1000);
|
||||
// } catch (InterruptedException e) {
|
||||
// }
|
||||
} else {
|
||||
printSummary(message, batchId, size);
|
||||
printEntry(message.getEntries());
|
||||
}
|
||||
|
||||
connector.ack(batchId); // 提交确认
|
||||
// connector.rollback(batchId); // 处理失败, 回滚数据
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("process error!", e);
|
||||
} finally {
|
||||
connector.disconnect();
|
||||
MDC.remove("destination");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void printSummary(Message message, long batchId, int size) {
|
||||
long memsize = 0;
|
||||
for (Entry entry : message.getEntries()) {
|
||||
memsize += entry.getHeader().getEventLength();
|
||||
}
|
||||
|
||||
String startPosition = null;
|
||||
String endPosition = null;
|
||||
if (!CollectionUtils.isEmpty(message.getEntries())) {
|
||||
startPosition = buildPositionForDump(message.getEntries().get(0));
|
||||
endPosition = buildPositionForDump(message.getEntries().get(message.getEntries().size() - 1));
|
||||
}
|
||||
|
||||
SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
|
||||
logger.info(context_format, new Object[] { batchId, size, memsize, format.format(new Date()), startPosition,
|
||||
endPosition });
|
||||
}
|
||||
|
||||
protected String buildPositionForDump(Entry entry) {
|
||||
long time = entry.getHeader().getExecuteTime();
|
||||
Date date = new Date(time);
|
||||
SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
|
||||
return entry.getHeader().getLogfileName() + ":" + entry.getHeader().getLogfileOffset() + ":"
|
||||
+ entry.getHeader().getExecuteTime() + "(" + format.format(date) + ")";
|
||||
}
|
||||
|
||||
protected void printEntry(List<Entry> entrys) {
|
||||
for (Entry entry : entrys) {
|
||||
long executeTime = entry.getHeader().getExecuteTime();
|
||||
long delayTime = new Date().getTime() - executeTime;
|
||||
|
||||
if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {
|
||||
if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN) {
|
||||
TransactionBegin begin = null;
|
||||
try {
|
||||
begin = TransactionBegin.parseFrom(entry.getStoreValue());
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new RuntimeException("parse event has an error , data:" + entry.toString(), e);
|
||||
}
|
||||
// 打印事务头信息,执行的线程id,事务耗时
|
||||
logger.info(transaction_format,
|
||||
new Object[] { entry.getHeader().getLogfileName(),
|
||||
String.valueOf(entry.getHeader().getLogfileOffset()),
|
||||
String.valueOf(entry.getHeader().getExecuteTime()), String.valueOf(delayTime) });
|
||||
logger.info(" BEGIN ----> Thread id: {}", begin.getThreadId());
|
||||
} else if (entry.getEntryType() == EntryType.TRANSACTIONEND) {
|
||||
TransactionEnd end = null;
|
||||
try {
|
||||
end = TransactionEnd.parseFrom(entry.getStoreValue());
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new RuntimeException("parse event has an error , data:" + entry.toString(), e);
|
||||
}
|
||||
// 打印事务提交信息,事务id
|
||||
logger.info("----------------\n");
|
||||
logger.info(" END ----> transaction id: {}", end.getTransactionId());
|
||||
logger.info(transaction_format,
|
||||
new Object[] { entry.getHeader().getLogfileName(),
|
||||
String.valueOf(entry.getHeader().getLogfileOffset()),
|
||||
String.valueOf(entry.getHeader().getExecuteTime()), String.valueOf(delayTime) });
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.getEntryType() == EntryType.ROWDATA) {
|
||||
RowChange rowChage = null;
|
||||
try {
|
||||
rowChage = RowChange.parseFrom(entry.getStoreValue());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("parse event has an error , data:" + entry.toString(), e);
|
||||
}
|
||||
|
||||
EventType eventType = rowChage.getEventType();
|
||||
|
||||
logger.info(row_format,
|
||||
new Object[] { entry.getHeader().getLogfileName(),
|
||||
String.valueOf(entry.getHeader().getLogfileOffset()), entry.getHeader().getSchemaName(),
|
||||
entry.getHeader().getTableName(), eventType,
|
||||
String.valueOf(entry.getHeader().getExecuteTime()), String.valueOf(delayTime) });
|
||||
|
||||
if (eventType == EventType.QUERY || rowChage.getIsDdl()) {
|
||||
logger.info(" sql ----> " + rowChage.getSql() + SEP);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (RowData rowData : rowChage.getRowDatasList()) {
|
||||
if (eventType == EventType.DELETE) {
|
||||
printColumn(rowData.getBeforeColumnsList());
|
||||
} else if (eventType == EventType.INSERT) {
|
||||
printColumn(rowData.getAfterColumnsList());
|
||||
} else {
|
||||
printColumn(rowData.getAfterColumnsList());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void printColumn(List<Column> columns) {
|
||||
for (Column column : columns) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(column.getName() + " : " + column.getValue());
|
||||
builder.append(" type=" + column.getMysqlType());
|
||||
if (column.getUpdated()) {
|
||||
builder.append(" update=" + column.getUpdated());
|
||||
}
|
||||
builder.append(SEP);
|
||||
logger.info(builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void setConnector(CanalConnector connector) {
|
||||
this.connector = connector;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.alibaba.otter.canal.example;
|
||||
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
|
||||
import com.alibaba.otter.canal.client.CanalConnector;
|
||||
import com.alibaba.otter.canal.client.CanalConnectors;
|
||||
|
||||
/**
|
||||
* 集群模式的测试例子
|
||||
*
|
||||
* @author jianghang 2013-4-15 下午04:19:20
|
||||
* @version 1.0.4
|
||||
*/
|
||||
public class ClusterCanalClientTest extends AbstractCanalClientTest {
|
||||
|
||||
public ClusterCanalClientTest(String destination){
|
||||
super(destination);
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
String destination = "example";
|
||||
|
||||
// 基于固定canal server的地址,建立链接,其中一台server发生crash,可以支持failover
|
||||
// CanalConnector connector = CanalConnectors.newClusterConnector(
|
||||
// Arrays.asList(new InetSocketAddress(
|
||||
// AddressUtils.getHostIp(),
|
||||
// 11111)),
|
||||
// "stability_test", "", "");
|
||||
|
||||
// 基于zookeeper动态获取canal server的地址,建立链接,其中一台server发生crash,可以支持failover
|
||||
CanalConnector connector = CanalConnectors.newClusterConnector("127.0.0.1:2181", destination, "", "");
|
||||
|
||||
final ClusterCanalClientTest clientTest = new ClusterCanalClientTest(destination);
|
||||
clientTest.setConnector(connector);
|
||||
clientTest.start();
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
logger.info("## stop the canal client");
|
||||
clientTest.stop();
|
||||
} catch (Throwable e) {
|
||||
logger.warn("##something goes wrong when stopping canal:\n{}", ExceptionUtils.getFullStackTrace(e));
|
||||
} finally {
|
||||
logger.info("## canal client is down.");
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.alibaba.otter.canal.example;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
|
||||
import com.alibaba.otter.canal.client.CanalConnector;
|
||||
import com.alibaba.otter.canal.client.CanalConnectors;
|
||||
import com.alibaba.otter.canal.common.utils.AddressUtils;
|
||||
|
||||
/**
|
||||
* 单机模式的测试例子
|
||||
*
|
||||
* @author jianghang 2013-4-15 下午04:19:20
|
||||
* @version 1.0.4
|
||||
*/
|
||||
public class SimpleCanalClientTest extends AbstractCanalClientTest {
|
||||
|
||||
public SimpleCanalClientTest(String destination){
|
||||
super(destination);
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
// 根据ip,直接创建链接,无HA的功能
|
||||
String destination = "example";
|
||||
CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress(AddressUtils.getHostIp(),
|
||||
11111), destination, "", "");
|
||||
|
||||
final SimpleCanalClientTest clientTest = new SimpleCanalClientTest(destination);
|
||||
clientTest.setConnector(connector);
|
||||
clientTest.start();
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
logger.info("## stop the canal client");
|
||||
clientTest.stop();
|
||||
} catch (Throwable e) {
|
||||
logger.warn("##something goes wrong when stopping canal:\n{}", ExceptionUtils.getFullStackTrace(e));
|
||||
} finally {
|
||||
logger.info("## canal client is down.");
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
25
example/src/main/resources/logback.xml
Normal file
25
example/src/main/resources/logback.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<configuration scan="true" scanPeriod=" 5 seconds">
|
||||
|
||||
<jmxConfigurator />
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{56} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="ENTRY_LOG" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%msg</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="com.alibaba.otter.canal.example.AbstractCanalClientTest" additivity="false">
|
||||
<level value="INFO" />
|
||||
<appender-ref ref="ENTRY_LOG" />
|
||||
</logger>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user