/* * Copyright 2015 Liu Huanting. * * 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 fm.liu.timo.backend; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.locks.ReentrantLock; import org.pmw.tinylog.Logger; import fm.liu.timo.config.model.Datanode.Strategy; import fm.liu.timo.server.session.handler.ResultHandler; import fm.liu.timo.util.TimeUtil; /** * 数据节点 * @author Liu Huanting 2015年5月9日 */ public class Node { private final int id; private final Strategy strategy; private final List<Source> sources; private final ReentrantLock lock = new ReentrantLock(); private volatile Source source; private volatile long heartbeatRecoveryTime; public Node(int id, Strategy strategy, List<Source> sources) { this.id = id; this.strategy = strategy; this.sources = sources; } public boolean init() { boolean chosen = false; for (Source source : sources) { if (!source.isAvailable()) { continue; } if (!source.init()) { Logger.warn("source {} init failed.", source.getConfig()); return false; } if (!chosen) { this.source = source; chosen = true; } } return chosen; } public void idleCheck() { sources.parallelStream().filter(s -> s.isAvailable()).forEach(s -> s.idleCheck()); } public void heartbeat() { if (TimeUtil.currentTimeMillis() < heartbeatRecoveryTime) { sources.parallelStream().filter(s -> s.isAvailable()) .forEach(s -> s.getHeartbeat().pause()); return; } sources.parallelStream().filter(s -> s.isAvailable()).forEach(s -> s.heartbeat(this)); } public int getID() { return id; } public Source getSource() { return source; } public List<Source> getSources() { return sources; } public void setHeartbeatRecoveryTime(long heartbeatRecoveryTime) { this.heartbeatRecoveryTime = heartbeatRecoveryTime; } public boolean handover(boolean manual) { boolean success = false; ArrayList<Source> backups = source.getBackups(); if (backups == null || backups.isEmpty()) { Logger.error("can't handover datasource:{} without backup infomation", source.getConfig()); return false; } lock.lock(); try { for (Source source : backups) { if (source.isAvailable()) { if (!manual) { this.source.getConfig().ban(); this.source.clear("datanode handover by manager"); } else { Logger.info("handover datanode {} to datasource {} by manager.", this.getID(), source); } this.source = source; success = true; break; } } } finally { lock.unlock(); } return success; } public void clear(String reason) { for (Source source : sources) { source.clear(reason); } } public void query(String sql, ResultHandler handler, boolean read) { Source source = this.source; switch (strategy) { case MRW_SR: if (read) { source = randomAll(); } break; case MW_SR: if (read) { source = randomRead(); } break; default: break; } if (!source.isAvailable()) { source = this.source; } source.query(sql, handler); } private Source randomRead() { ArrayList<Source> backups = source.getBackups(); if (backups == null || backups.isEmpty()) { return this.source; } return backups.get(ThreadLocalRandom.current().nextInt(backups.size())); } private Source randomAll() { ArrayList<Source> backups = source.getBackups(); if (backups == null || backups.isEmpty()) { return this.source; } int seed = ThreadLocalRandom.current().nextInt(backups.size() + 1); switch (seed) { case 0: return this.source; default: return source.getBackups().get(seed - 1); } } public Strategy getStrategy() { return strategy; } }