package com.zhqc.cloud.canal;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Data
@Component
public class CanalConfig {
@Value("${canal.hostname}")
private String host;
@Value("${canal.port}")
private int port;
@Value("${canal.username}")
private String username;
@Value("${canal.passwd}")
private String passwd;
@Value("${canal.table}")
private String table;
}
package com.zhqc.cloud.canal;
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.alibaba.otter.canal.protocol.Message;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.net.InetSocketAddress;
import java.util.List;
@Slf4j
@Component
public class SimpleCanalClient implements CommandLineRunner {
private CanalConnector connector;
private Thread thread = null;
private final Thread.UncaughtExceptionHandler handler = (t, e) -> e.printStackTrace();
private volatile boolean running = false;
private final static int BATCH_SIZE = 5 * 1024;
@Resource
private CanalConfig canalConfig;
@Override
public void run(String... args) throws Exception {
String destination = "example";
this.connector = CanalConnectors.newSingleConnector(new InetSocketAddress(canalConfig.getHost(), canalConfig.getPort()),
destination,
canalConfig.getUsername(),
canalConfig.getPasswd());
start();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
log.info("## stop the canal client");
stop();
} catch (Throwable e) {
log.error("##something goes wrong when stopping canal:", e);
e.printStackTrace();
} finally {
log.info("## canal client is down.");
}
}));
}
public void start() {
if (this.connector == null) {
log.error("connector不能为空,启动失败");
return;
}
thread = new Thread(this::process);
thread.setUncaughtExceptionHandler(handler);
running = true;
thread.start();
log.info("canal client started...");
}
public void stop() {
if (!running) {
return;
}
running = false;
if (thread != null) {
try {
thread.join();
} catch (InterruptedException e) {
}
}
log.info("canal client stopped...");
}
private void process() {
while (running) {
try {
connector.connect();
connector.subscribe(canalConfig.getTable());
connector.rollback();
while (running) {
Message message = connector.getWithoutAck(BATCH_SIZE);
long batchId = message.getId();
int size = message.getEntries().size();
if (batchId == -1 || size == 0) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
printEntry(message.getEntries());
}
if (batchId != -1) {
connector.ack(batchId);
}
}
} catch (Throwable e) {
e.printStackTrace();
try {
Thread.sleep(1000L);
} catch (InterruptedException e1) {
}
connector.rollback();
} finally {
connector.disconnect();
}
}
}
private static void printEntry(List<CanalEntry.Entry> entrys) {
for (CanalEntry.Entry entry : entrys) {
if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN || entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND) {
continue;
}
CanalEntry.RowChange rowChange;
try {
rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
} catch (Exception e) {
throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(), e);
}
CanalEntry.EventType eventType = rowChange.getEventType();
log.info("binlog[{}:{}], name[{},{}], eventType: {}",
entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),
entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),
eventType);
if (rowChange.getIsDdl()) {
log.info("================》;isDDL: true,sql:" + rowChange.getSql());
}
for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
if (eventType == CanalEntry.EventType.DELETE) {
printColumn(rowData.getBeforeColumnsList());
} else if (eventType == CanalEntry.EventType.INSERT) {
printColumn(rowData.getAfterColumnsList());
} else {
log.info("------->; before");
printColumn(rowData.getBeforeColumnsList());
log.info("------->; after");
printColumn(rowData.getAfterColumnsList());
}
}
}
}
private static void printColumn(List<CanalEntry.Column> columns) {
for (CanalEntry.Column column : columns) {
log.info(column.getName() + " : " + column.getValue() + " update=" + column.getUpdated());
}
}
}