/* * Copyright 2012-2015 org.opencloudb. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.opencloudb.heartbeat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantLock; import org.apache.log4j.Logger; import org.opencloudb.MycatNode; import org.opencloudb.config.Alarms; import org.opencloudb.config.model.MycatNodeConfig; import org.opencloudb.net.mysql.OkPacket; import org.opencloudb.statistic.HeartbeatRecorder; import org.opencloudb.util.TimeUtil; /** * @author mycat */ public class MyCATHeartbeat { public static final int OK_STATUS = 1; public static final int OFF_STATUS = 2; public static final int SEND = 3; public static final int ERROR_STATUS = -1; private static final int TIMEOUT_STATUS = -2; private static final int INIT_STATUS = 0; private static final int MAX_RETRY_COUNT = 5; private static final Logger ALARM = Logger.getLogger("alarm"); private static final Logger LOGGER = Logger.getLogger(MyCATHeartbeat.class); private static final Logger HEARTBEAT = Logger.getLogger("heartbeat"); private final MycatNode node; private final AtomicBoolean isStop; private final AtomicBoolean isChecking; private final MyCATDetectorFactory factory; private final HeartbeatRecorder recorder; private final ReentrantLock lock; private final int maxRetryCount; private int errorCount; private volatile int status; private MyCATDetector detector; public final AtomicLong detectCount; public MyCATHeartbeat(MycatNode node) { this.node = node; this.isStop = new AtomicBoolean(false); this.isChecking = new AtomicBoolean(false); this.factory = new MyCATDetectorFactory(); this.recorder = new HeartbeatRecorder(); this.lock = new ReentrantLock(false); this.maxRetryCount = MAX_RETRY_COUNT; this.status = OK_STATUS; this.detectCount = new AtomicLong(0); } public MycatNode getNode() { return node; } public MyCATDetector getDetector() { return detector; } public int getStatus() { return status; } public int getErrorCount() { return errorCount; } public long getTimeout() { MyCATDetector detector = this.detector; if (detector == null) { return -1L; } return detector.getHeartbeatTimeout(); } public HeartbeatRecorder getRecorder() { return recorder; } public String lastActiveTime() { MyCATDetector detector = this.detector; if (detector == null) { return null; } long t = Math.max(detector.lastReadTime(), detector.lastWriteTime()); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(new Date(t)); } public boolean isStop() { return isStop.get(); } public boolean isChecking() { return isChecking.get(); } public void start() { final ReentrantLock lock = this.lock; lock.lock(); try { isStop.compareAndSet(true, false); } finally { lock.unlock(); } } public void stop() { final ReentrantLock lock = this.lock; lock.lock(); try { if (isStop.compareAndSet(false, true)) { if (isChecking.get()) { // nothing } else { MyCATDetector detector = this.detector; if (detector != null) { detector.quit(); isChecking.set(false); } } } } finally { lock.unlock(); } } /** * 执行心跳 */ public void heartbeat() { final ReentrantLock lock = this.lock; lock.lock(); try { if (isChecking.compareAndSet(false, true)) { MyCATDetector detector = this.detector; if (detector == null || detector.isQuit() || detector.isClosed()) { try { detector = factory.make(this); } catch (Throwable e) { LOGGER.warn(node.toString(), e); setError(null); return; } this.detector = detector; } else { detector.heartbeat(); } } else { MyCATDetector detector = this.detector; if (detector != null) { if (detector.isQuit() || detector.isClosed()) { isChecking.compareAndSet(true, false); } else if (detector.isHeartbeatTimeout()) { setTimeout(detector); } } } } finally { lock.unlock(); } } /** * 设定结果 */ public void setResult(int result, MyCATDetector detector, boolean isTransferError, byte[] message) { switch (result) { case OK_STATUS: setOk(detector); if (HEARTBEAT.isInfoEnabled()) { HEARTBEAT.info(requestMessage(OK_STATUS, message)); } break; case OFF_STATUS: setOff(detector); if (HEARTBEAT.isInfoEnabled()) { HEARTBEAT.info(requestMessage(OFF_STATUS, message)); } break; case ERROR_STATUS: if (detector.isQuit()) { isChecking.set(false); } else { if (isTransferError) { detector.close("heatbeat transferError"); } setError(detector); } if (HEARTBEAT.isInfoEnabled()) { HEARTBEAT.info(requestMessage(ERROR_STATUS, message)); } break; } } private void setOk(MyCATDetector detector) { recorder.set(detector.lastReadTime() - detector.lastWriteTime()); switch (status) { case TIMEOUT_STATUS: this.status = INIT_STATUS; this.errorCount = 0; this.isChecking.set(false); if (isStop.get()) { detector.quit(); } else { heartbeat();// 超时状态,再次执行心跳。 } break; default: this.status = OK_STATUS; this.errorCount = 0; this.isChecking.set(false); if (isStop.get()) { detector.quit(); } } } private void setOff(MyCATDetector detector) { this.status = OFF_STATUS; this.errorCount = 0; this.isChecking.set(false); if (isStop.get()) { detector.quit(); } } private void setError(MyCATDetector detector) { if (++errorCount < maxRetryCount) { this.isChecking.set(false); if (detector != null && isStop.get()) { detector.quit(); } else { heartbeat();// 未到达错误次数,再次执行心跳。 } } else { this.status = ERROR_STATUS; this.errorCount = 0; this.isChecking.set(false); try { ALARM.error(alarmMessage("ERROR")); } finally { if (detector != null && isStop.get()) { detector.quit(); } } } } private void setTimeout(MyCATDetector detector) { status = TIMEOUT_STATUS; try { ALARM.error(alarmMessage("TIMEOUT")); if (HEARTBEAT.isInfoEnabled()) { HEARTBEAT.info(requestMessage(TIMEOUT_STATUS, null)); } } finally { detector.quit(); isChecking.set(false); } } /** * 报警信息 */ private String alarmMessage(String reason) { MycatNodeConfig cnc = node.getConfig(); return new StringBuilder().append(Alarms.DEFAULT).append("[name=").append(cnc.getName()).append(",host=") .append(cnc.getHost()).append(",port=").append(cnc.getPort()).append(",reason=").append(reason) .append(']').toString(); } /** * 心跳日志信息 */ public String requestMessage(int type, byte[] message) { String action = null; String id = null; switch (type) { case OK_STATUS: action = "OK"; OkPacket ok = new OkPacket(); ok.read(message); id = String.valueOf(ok.affectedRows); break; case OFF_STATUS: action = "OFFLINE"; if (message != null) { id = new String(message); } break; case ERROR_STATUS: action = "ERROR"; if (message != null) { id = new String(message); } break; case TIMEOUT_STATUS: action = "TIMEOUT"; if (message != null) { id = new String(message); } break; case SEND: action = "SEND"; if (message != null) { id = new String(message); } break; default: action = "UNKNOWN"; if (message != null) { id = new String(message); } } // 如果取不到从服务端返回的id,则从本地取得。 if (id == null) { id = "$" + detectCount.get(); } return new StringBuilder().append("REQUEST:").append(action).append(", id=").append(id).append(", host=") .append(node.getConfig().getHost()).append(", port=").append(node.getConfig().getPort()) .append(", time=").append(TimeUtil.currentTimeMillis()).toString(); } }