MyCat的服务端在启动时会执行node.startHeartbeat()去设置每个node的heartbeat的状态为DBHeartbeat.OK_STATUS,然后针对每个node节点启动一个线程去对对应的node发送heartbeat sql(比如select user())进行心跳检测。
private Runnable dataNodeHeartbeat() {
return new Runnable() {
@Override
public void run() {
timerExecutor.execute(new Runnable() {
@Override
public void run() {
Map<String, PhysicalDBPool> nodes = config
.getDataHosts();
for (PhysicalDBPool node : nodes.values()) {
node.doHeartbeat();
}
}
});
}
};
}
heatbeat类会调用heartbeat方法。
public void heartbeat() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//改变状态
if (isChecking.compareAndSet(false, true)) {
MySQLDetector detector = this.detector;
if (detector == null || detector.isQuit()) {
try {
detector = new MySQLDetector(this);
detector.heartbeat();
} catch (Exception e) {
LOGGER.warn(source.getConfig().toString(), e);
setResult(ERROR_STATUS, detector, null);
return;
}
this.detector = detector;
} else {
detector.heartbeat();
}
} else {
MySQLDetector detector = this.detector;
if (detector != null) {
if (detector.isQuit()) {
isChecking.compareAndSet(true, false);
} else if (detector.isHeartbeatTimeout()) {
setResult(TIMEOUT_STATUS, detector, null);
}
}
}
} finally {
lock.unlock();
}
}
它会调用detector属性的heartbeat方法。
public void heartbeat() {
lastSendQryTime = System.currentTimeMillis();
MySQLDataSource ds = heartbeat.getSource();
String databaseName = ds.getDbPool().getSchemas()[0];
String[] fetchColms={};
if (heartbeat.getSource().getHostConfig().isShowSlaveSql() ) {
fetchColms=MYSQL_SLAVE_STAUTS_COLMS;
}
if (heartbeat.getSource().getHostConfig().isShowClusterSql() ) {
fetchColms=MYSQL_CLUSTER_STAUTS_COLMS;
}
OneRawSQLQueryResultHandler resultHandler = new OneRawSQLQueryResultHandler( fetchColms, this);
sqlJob = new SQLJob(heartbeat.getHeartbeatSQL(), databaseName, resultHandler, ds);
sqlJob.run();
}
它会获取数据源和数据库名,然后创建并注册一个OneRawSQLQueryResultHandler到sqlJob,再由这个job发送sql到数据库将结果取回交给注册的handler处理。
public boolean onRowData(String dataNode, byte[] rowData) {
RowDataPacket rowDataPkg = new RowDataPacket(fieldCount);
rowDataPkg.read(rowData);
String variableName = "";
String variableValue = "";
//fieldcount为2可能是select x也可能是show create table命令
if(fieldCount==2 && (fetchColPosMap.get("Variable_name")!=null || fetchColPosMap.get("Value")!=null)){
Integer ind = fetchColPosMap.get("Variable_name");
if (ind != null) {
byte[] columnData = rowDataPkg.fieldValues.get(ind);
String columnVal = columnData!=null?new String(columnData):null;
variableName = columnVal;
}
ind = fetchColPosMap.get("Value");
if (ind != null) {
byte[] columnData = rowDataPkg.fieldValues.get(ind);
String columnVal = columnData!=null?new String(columnData):null;
variableValue = columnVal;
}
result.put(variableName, variableValue);
}else{
for (String fetchCol : fetchCols) {
Integer ind = fetchColPosMap.get(fetchCol);
if (ind != null) {
byte[] columnData = rowDataPkg.fieldValues.get(ind);
String columnVal = columnData!=null?new String(columnData):null;
result.put(fetchCol, columnVal);
} else {
LOGGER.warn("cant't find column in sql query result " + fetchCol);
}
}
}
return false;
}
如果客户端发送reload指令或stop指令到服务端就会修改hearbeat的状态为stop,停止进行心跳检测。