order
This paper focuses on my SQL detecting time task of canal
MysqlDetectingTimeTask
canal-1.1.4/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlEventParser.java
class MysqlDetectingTimeTask extends TimerTask { private boolean reconnect = false; private MysqlConnection mysqlConnection; public MysqlDetectingTimeTask(MysqlConnection mysqlConnection){ this.mysqlConnection = mysqlConnection; } public void run() { try { if (reconnect) { reconnect = false; mysqlConnection.reconnect(); } else if (!mysqlConnection.isConnected()) { mysqlConnection.connect(); } Long startTime = System.currentTimeMillis(); // Maybe the heartbeat sql is select 1 if (StringUtils.startsWithIgnoreCase(detectingSQL.trim(), "select") || StringUtils.startsWithIgnoreCase(detectingSQL.trim(), "show") || StringUtils.startsWithIgnoreCase(detectingSQL.trim(), "explain") || StringUtils.startsWithIgnoreCase(detectingSQL.trim(), "desc")) { mysqlConnection.query(detectingSQL); } else { mysqlConnection.update(detectingSQL); } Long costTime = System.currentTimeMillis() - startTime; if (haController != null && haController instanceof HeartBeatCallback) { ((HeartBeatCallback) haController).onSuccess(costTime); } } catch (SocketTimeoutException e) { if (haController != null && haController instanceof HeartBeatCallback) { ((HeartBeatCallback) haController).onFailed(e); } reconnect = true; logger.warn("connect failed by ", e); } catch (IOException e) { if (haController != null && haController instanceof HeartBeatCallback) { ((HeartBeatCallback) haController).onFailed(e); } reconnect = true; logger.warn("connect failed by ", e); } catch (Throwable e) { if (haController != null && haController instanceof HeartBeatCallback) { ((HeartBeatCallback) haController).onFailed(e); } reconnect = true; logger.warn("connect failed by ", e); } } public MysqlConnection getMysqlConnection() { return mysqlConnection; } }
- MysqlDetectingTimeTask inherits TimerTask. When reconnect is true, its run method updates reconnect to false and executes mysqlConnection.reconnect(); if reconnect is false and mysqlConnection.isConnected() is false, execute mysqlConnection.connect(); then execute detectingSQL statement, record costTime, and finally execute ((heartbeatcallback) Hacontroller. Onsuccess (costTime); capture socketimeoutexception, IOException, Throwable in turn, then execute ((HeartBeatCallback) haController).onFailed(e), update connect to true
HeartBeatCallback
canal-1.1.4/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/HeartBeatCallback.java
public interface HeartBeatCallback { /** * Heartbeat sent successfully */ public void onSuccess(long costTime); /** * Heartbeat sending failed */ public void onFailed(Throwable e); }
- The HeartBeatCallback interface defines the onSuccess and onFailed methods
HeartBeatHAController
canal-1.1.4/parse/src/main/java/com/alibaba/otter/canal/parse/ha/HeartBeatHAController.java
public class HeartBeatHAController extends AbstractCanalLifeCycle implements CanalHAController, HeartBeatCallback { private static final Logger logger = LoggerFactory.getLogger(HeartBeatHAController.class); // default 3 times private int detectingRetryTimes = 3; private int failedTimes = 0; private boolean switchEnable = false; private CanalHASwitchable eventParser; public HeartBeatHAController(){ } public void onSuccess(long costTime) { failedTimes = 0; } public void onFailed(Throwable e) { failedTimes++; // Check if the number of failures is exceeded synchronized (this) { if (failedTimes > detectingRetryTimes) { if (switchEnable) { eventParser.doSwitch();// Notice to perform a switch failedTimes = 0; } else { logger.warn("HeartBeat failed Times:{} , should auto switch ?", failedTimes); } } } } // ============================= setter / getter // ============================ public void setCanalHASwitchable(CanalHASwitchable canalHASwitchable) { this.eventParser = canalHASwitchable; } public void setDetectingRetryTimes(int detectingRetryTimes) { this.detectingRetryTimes = detectingRetryTimes; } public void setSwitchEnable(boolean switchEnable) { this.switchEnable = switchEnable; } }
- The HeartBeatHAController implements the HeartBeatCallback interface, and its onSuccess method updates the failedTimes to 0; its onFailed method increments the failedTimes, and then determines whether the failedTimes is greater than the detectingRetryTimes, and if switchEnable is true, eventParser.doSwitch() is executed, and then the failedTimes is updated to 0
doSwitch
canal-1.1.4/parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlEventParser.java
public class MysqlEventParser extends AbstractMysqlEventParser implements CanalEventParser, CanalHASwitchable { //...... // Logic for handling active standby switching public void doSwitch() { AuthenticationInfo newRunningInfo = (runningInfo.equals(masterInfo) ? standbyInfo : masterInfo); this.doSwitch(newRunningInfo); } public void doSwitch(AuthenticationInfo newRunningInfo) { // 1. Need to stop the process currently being copied // 2. Find a new position point // 3. Re link and start copying data // Switch ip String alarmMessage = null; if (this.runningInfo.equals(newRunningInfo)) { alarmMessage = "same runingInfo switch again : " + runningInfo.getAddress().toString(); logger.warn(alarmMessage); return; } if (newRunningInfo == null) { alarmMessage = "no standby config, just do nothing, will continue try:" + runningInfo.getAddress().toString(); logger.warn(alarmMessage); sendAlarm(destination, alarmMessage); return; } else { stop(); alarmMessage = "try to ha switch, old:" + runningInfo.getAddress().toString() + ", new:" + newRunningInfo.getAddress().toString(); logger.warn(alarmMessage); sendAlarm(destination, alarmMessage); runningInfo = newRunningInfo; start(); } } //...... }
- doSwitch method of MysqlEventParser executes stop method first, then updates runningInfo, and finally executes start method
Summary
MysqlDetectingTimeTask inherits TimerTask. When reconnect is true, its run method updates reconnect to false and executes mysqlConnection.reconnect(); if reconnect is false and mysqlConnection.isConnected() is false, execute mysqlConnection.connect(); then execute detectingSQL statement, record costTime, and finally execute ((heartbeatcallback) Hacontroller. Onsuccess (costTime); capture socketimeoutexception, IOException, Throwable in turn, then execute ((HeartBeatCallback) haController).onFailed(e), update connect to true