/* * Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software;Designed and Developed mainly by many Chinese * opensource volunteers. you can redistribute it and/or modify it under the * terms of the GNU General Public License version 2 only, as published by the * Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Any questions about this component can be directed to it's project Web address * https://code.google.com/p/opencloudb/. * */ package io.mycat.backend.heartbeat; import io.mycat.backend.MySQLDataSource; import io.mycat.backend.PhysicalDBPool; import io.mycat.backend.PhysicalDatasource; import io.mycat.server.config.node.DataHostConfig; import io.mycat.sqlengine.OneRawSQLQueryResultHandler; import io.mycat.sqlengine.SQLJob; import io.mycat.sqlengine.SQLQueryResult; import io.mycat.sqlengine.SQLQueryResultListener; import io.mycat.util.TimeUtil; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; /** * @author mycat */ public class MySQLDetector implements SQLQueryResultListener<SQLQueryResult<Map<String, String>>> { private MySQLHeartbeat heartbeat; private long heartbeatTimeout; private final AtomicBoolean isQuit; private volatile long lastSendQryTime; private volatile long lasstReveivedQryTime; private volatile SQLJob sqlJob; private static final String[] MYSQL_SLAVE_STAUTS_COLMS = new String[] { "Seconds_Behind_Master", "Slave_IO_Running", "Slave_SQL_Running" }; public MySQLDetector(MySQLHeartbeat heartbeat) { this.heartbeat = heartbeat; this.isQuit = new AtomicBoolean(false); } public MySQLHeartbeat getHeartbeat() { return heartbeat; } public long getHeartbeatTimeout() { return heartbeatTimeout; } public void setHeartbeatTimeout(long heartbeatTimeout) { this.heartbeatTimeout = heartbeatTimeout; } public boolean isHeartbeatTimeout() { return TimeUtil.currentTimeMillis() > Math.max(lastSendQryTime, lasstReveivedQryTime) + heartbeatTimeout; } public long getLastSendQryTime() { return lastSendQryTime; } public long getLasstReveivedQryTime() { return lasstReveivedQryTime; } 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; } OneRawSQLQueryResultHandler resultHandler = new OneRawSQLQueryResultHandler( fetchColms, this); sqlJob = new SQLJob(heartbeat.getHeartbeatSQL(), databaseName, resultHandler, ds); sqlJob.run(); } public void quit() { if (isQuit.compareAndSet(false, true)) { close("heart beat quit"); } } public boolean isQuit() { return isQuit.get(); } @Override public void onResult(SQLQueryResult<Map<String, String>> result) { if (result.isSuccess()) { int balance = heartbeat.getSource().getDbPool().getBalance(); PhysicalDatasource source = heartbeat.getSource(); Map<String, String> resultResult = result.getResult(); if (source.getHostConfig().isShowSlaveSql() &&(source.getHostConfig().getSwitchType() == DataHostConfig.SYN_STATUS_SWITCH_DS || PhysicalDBPool.BALANCE_NONE!=balance ) ) { String Slave_IO_Running =resultResult!=null? resultResult.get( "Slave_IO_Running"):null; String Slave_SQL_Running = resultResult!=null?resultResult.get( "Slave_SQL_Running"):null; if (Slave_IO_Running != null && Slave_IO_Running.equals(Slave_SQL_Running) && Slave_SQL_Running.equals("Yes")) { heartbeat.setDbSynStatus(DBHeartbeat.DB_SYN_NORMAL); String Seconds_Behind_Master = resultResult.get( "Seconds_Behind_Master"); if (null != Seconds_Behind_Master && !"".equals(Seconds_Behind_Master)) { heartbeat.setSlaveBehindMaster(Integer .valueOf(Seconds_Behind_Master)); } } else if(source.isSalveOrRead()) { MySQLHeartbeat.LOGGER .warn("found MySQL master/slave Replication err !!! " + heartbeat.getSource().getConfig()); heartbeat.setDbSynStatus(DBHeartbeat.DB_SYN_ERROR); } } heartbeat.setResult(MySQLHeartbeat.OK_STATUS, this, null); } else { heartbeat.setResult(MySQLHeartbeat.ERROR_STATUS, this, null); } lasstReveivedQryTime = System.currentTimeMillis(); } public void close(String msg) { SQLJob curJob = sqlJob; // 这里应该是写反了?? // node 的 heartbeat失败,会调用到SQLJob.doFinished, MySQLHeartBeat.setError // 如果超过重试次数,则调用detector.quit(); 然后到达此函数,但是 SQLJob.doFinished // 已经设置 finished = true,所以导致 curJob.teminate(msg) 始终无法执行 // 无法关闭连接 // if (curJob != null && !curJob.isFinished()) { if (curJob != null && curJob.isFinished()) { curJob.teminate(msg); sqlJob = null; } } }