/* * 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.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import fm.liu.timo.config.model.Datasource; import fm.liu.timo.config.model.Datasource.Status; import fm.liu.timo.heartbeat.Heartbeat; import fm.liu.timo.mysql.connection.MySQLConnection; import fm.liu.timo.net.connection.AbstractConnection.State; import fm.liu.timo.net.connection.BackendConnection; import fm.liu.timo.net.connection.Variables; import fm.liu.timo.net.factory.BackendConnectionFactory; import fm.liu.timo.net.handler.BackendConnectHandler; import fm.liu.timo.net.handler.BackendCreateConnectionHandler; import fm.liu.timo.net.handler.InnerCreateConnectionHandler; import fm.liu.timo.server.session.handler.ResultHandler; import fm.liu.timo.server.session.handler.SessionResultHandler; import fm.liu.timo.server.session.handler.VirtualHandler; import fm.liu.timo.util.TimeUtil; /** * 数据源 * @author Liu Huanting 2015年5月9日 */ public class Source { private volatile Datasource config; private final int datanodeID; private final BackendConnectionFactory factory; private final Heartbeat heartbeat; private final ConcurrentHashMap<Long, BackendConnection> connections = new ConcurrentHashMap<Long, BackendConnection>(); private final ConcurrentLinkedQueue<BackendConnection> idle = new ConcurrentLinkedQueue<BackendConnection>(); private ArrayList<Source> backups; public Source(Datasource config, Variables variables, int heartbeatPeriod) { this.config = config; this.datanodeID = config.getDatanodeID(); this.factory = new BackendConnectionFactory(variables) {}; this.heartbeat = new Heartbeat(this, heartbeatPeriod); } public boolean init() { int size = config.getInitCon(); BackendCreateConnectionHandler handler = new BackendCreateConnectionHandler(); handler.setDB(config.getDB()); for (int i = 0; i < size; i++) { this.newConnection(handler); } long timeout = System.currentTimeMillis() + 10 * 1000; while (handler.getFinished() < size && (System.currentTimeMillis() < timeout)) { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } if (idle.size() < size) { return false; } else { return true; } } public MySQLConnection newConnection(final BackendConnectHandler handler) { int size = getSize(); if (size > config.getMaxCon()) { } MySQLConnection c = null; handler.setDB(config.getDB()); try { c = factory.newMySQLConnection(handler, config, this); } catch (IOException e) { handler.error("create connection exception:" + e.getMessage(), null); } return c; } public boolean isAvailable() { return Status.NORMAL.equals(config.getStatus()); } public int getSize() { return connections.size(); } public int getIdleSize() { return idle.size(); } public void remove(MySQLConnection con) { if (idle.contains(con)) { idle.remove(con); } connections.remove(con); } public void add(BackendConnection c) { connections.put(c.getID(), c); } public Datasource getConfig() { return config; } public int getDatanodeID() { return datanodeID; } public void release(BackendConnection con) { con.setState(State.idle); idle.offer(con); } public BackendConnection get() { BackendConnection con = idle.poll(); if (con != null) { con.setState(State.borrowed); } return con; } public BackendConnection notNullGet() { BackendConnection con = idle.poll(); if (con == null) { BackendCreateConnectionHandler handler = new BackendCreateConnectionHandler(); handler.setDB(config.getDB()); con = this.newConnection(handler); } con.setState(State.borrowed); return con; } public void idleCheck() { int idleSize = this.idle.size(); int increase = config.getMinIdle() - idleSize; if (increase > 0) { increase = Math.min(increase, config.getMaxIdle() - this.getSize()); } for (int i = 0; i < increase; i++) { BackendConnectHandler handler = new BackendCreateConnectionHandler(); handler.setDB(config.getDB()); this.newConnection(handler); } int decrease = idleSize - config.getMaxIdle(); if (decrease > 0) { get(decrease).forEach(con -> con.close("clear spare idle connection")); } long lastActiveTime = TimeUtil.currentTimeMillis() - config.getIdleCheckPeriod(); ArrayList<BackendConnection> connections = get(idleSize / 4); for (BackendConnection connection : connections) { if (connection.getVariables().getLastActiveTime() < lastActiveTime) { connection.query("SELECT 1", new VirtualHandler()); } else { release(connection); } } if (backups != null) { Iterator<Source> itor = backups.iterator(); while (itor.hasNext()) { if (!itor.next().isAvailable()) { itor.remove(); } } } } private ArrayList<BackendConnection> get(int decrease) { ArrayList<BackendConnection> connections = new ArrayList<BackendConnection>(); while (!idle.isEmpty() && connections.size() < decrease) { BackendConnection connection = idle.poll(); if (connection != null) { connections.add(connection); } } return connections; } public void query(String sql, ResultHandler handler) { BackendConnection conn = this.get(); if (conn == null) { InnerCreateConnectionHandler innerHandler = new InnerCreateConnectionHandler(sql, handler); this.newConnection(innerHandler); } else { if (handler instanceof SessionResultHandler) { ((SessionResultHandler) handler).session.offer(conn); } conn.query(sql, handler); } } public void heartbeat(Node node) { if (isAvailable() && !heartbeat.isStoped()) { heartbeat.heartbeat(node); } } public Heartbeat getHeartbeat() { return heartbeat; } public void clear(String reason) { for (BackendConnection connection : connections.values()) { connection.close(reason); } connections.clear(); heartbeat.stop(); } public void setBackups(ArrayList<Source> backups) { this.backups = backups; } public ArrayList<Source> getBackups() { return backups; } }