/* * 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; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import io.mycat.backend.PhysicalDBPool; import io.mycat.cache.CacheService; import io.mycat.net.*; import io.mycat.route.MyCATSequnceProcessor; import io.mycat.route.RouteService; import io.mycat.server.MySQLFrontConnectionFactory; import io.mycat.server.MySQLFrontConnectionHandler; import io.mycat.server.classloader.DynaClassLoader; import io.mycat.server.config.ConfigException; import io.mycat.server.config.cluster.ClusterSync; import io.mycat.server.config.loader.ConfigFactory; import io.mycat.server.config.node.MycatConfig; import io.mycat.server.config.node.SystemConfig; import io.mycat.server.interceptor.SQLInterceptor; import io.mycat.server.interceptor.impl.GlobalTableUtil; import io.mycat.util.TimeUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.nio.channels.AsynchronousChannelGroup; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; /** * @author mycat */ public class MycatServer { public static final String NAME = "MyCat"; private static final long LOG_WATCH_DELAY = 60000L; private static final long TIME_UPDATE_PERIOD = 20L; private static final MycatServer INSTANCE = new MycatServer(); private static final Logger LOGGER = LoggerFactory.getLogger("MycatServer"); private final RouteService routerService; private final CacheService cacheService; private AsynchronousChannelGroup[] asyncChannelGroups; private volatile int channelIndex = 0; private final MyCATSequnceProcessor sequnceProcessor = new MyCATSequnceProcessor(); private final DynaClassLoader catletClassLoader; private final SQLInterceptor sqlInterceptor; private final AtomicLong xaIDInc = new AtomicLong(); public static final MycatServer getInstance() { return INSTANCE; } private final MycatConfig config; private final Timer timer; private final AtomicBoolean isOnline; private final long startupTime; private NamebleScheduledExecutor timerExecutor; private ListeningExecutorService listeningExecutorService; private ClusterSync clusterSync; private MycatServer() { this.config = new MycatConfig(); this.timer = new Timer(NAME + "Timer", true); this.isOnline = new AtomicBoolean(true); cacheService = new CacheService(); routerService = new RouteService(cacheService); try { sqlInterceptor = (SQLInterceptor) Class.forName( config.getSystem().getSqlInterceptor()).newInstance(); } catch (Exception e) { throw new RuntimeException(e); } catletClassLoader = new DynaClassLoader(SystemConfig.getHomePath() + File.separator + "catlet", config.getSystem() .getCatletClassCheckSeconds()); this.startupTime = TimeUtil.currentTimeMillis(); } public DynaClassLoader getCatletClassLoader() { return catletClassLoader; } public MyCATSequnceProcessor getSequnceProcessor() { return sequnceProcessor; } public SQLInterceptor getSqlInterceptor() { return sqlInterceptor; } public String genXATXID() { long seq = this.xaIDInc.incrementAndGet(); if (seq < 0) { synchronized (xaIDInc) { if (xaIDInc.get() < 0) { xaIDInc.set(0); } seq = xaIDInc.incrementAndGet(); } } return "'Mycat." + this.getConfig().getSystem().getMycatNodeId() + "." + seq + "'"; } /** * get next AsynchronousChannel ,first is exclude if multi * AsynchronousChannelGroups * * @return */ public AsynchronousChannelGroup getNextAsyncChannelGroup() { if (asyncChannelGroups.length == 1) { return asyncChannelGroups[0]; } else { int index = (++channelIndex) % asyncChannelGroups.length; if (index == 0) { ++channelIndex; return asyncChannelGroups[1]; } else { return asyncChannelGroups[index]; } } } public MycatConfig getConfig() { return config; } public void startup() throws IOException { SystemConfig system = config.getSystem(); int processorCount = system.getProcessors(); // server startup LOGGER.info("==============================================="); LOGGER.info(NAME + " is ready to startup ..."); String inf = "Startup processors ...,total processors:" + system.getProcessors() + ",aio thread pool size:" + system.getProcessorExecutor() + " \r\n each process allocated socket buffer pool " + " bytes ,buffer chunk size:" + system.getProcessorBufferChunk() + " buffer pool's capacity(buferPool/bufferChunk) is:" + system.getProcessorBufferPool() / system.getProcessorBufferChunk(); LOGGER.info(inf); LOGGER.info("sysconfig params:" + system.toString()); int threadPoolSize = system.getProcessorExecutor(); long processBuferPool = system.getProcessorBufferPool(); int processBufferChunk = system.getProcessorBufferChunk(); int socketBufferLocalPercent = system.getProcessorBufferLocalPercent(); // server startup LOGGER.info("==============================================="); LOGGER.info(NAME + " is ready to startup ,network config:" + system); // message byte buffer pool BufferPool bufferPool = new BufferPool(processBuferPool, processBufferChunk, system.getFrontSocketSoRcvbuf(), socketBufferLocalPercent / processorCount); // Business Executor ,用来执行那些耗时的任务 NameableExecutor businessExecutor = ExecutorUtil.create( "BusinessExecutor", threadPoolSize); // 定时器Executor,用来执行定时任务 timerExecutor = ExecutorUtil.createSheduledExecute("Timer", system.getTimerExecutor()); listeningExecutorService = MoreExecutors .listeningDecorator(businessExecutor); // create netsystem to store our network related objects NetSystem netSystem = new NetSystem(bufferPool, businessExecutor, timerExecutor); netSystem.setNetConfig(system); // Reactor pool NIOReactorPool reactorPool = new NIOReactorPool( BufferPool.LOCAL_BUF_THREAD_PREX + "NIOREACTOR", processorCount); NIOConnector connector = new NIOConnector( BufferPool.LOCAL_BUF_THREAD_PREX + "NIOConnector", reactorPool); connector.start(); netSystem.setConnector(connector); MySQLFrontConnectionFactory frontFactory = new MySQLFrontConnectionFactory( new MySQLFrontConnectionHandler()); NIOAcceptor server = new NIOAcceptor(BufferPool.LOCAL_BUF_THREAD_PREX + NAME + "Server", system.getBindIp(), system.getServerPort(), frontFactory, reactorPool); server.start(); // server started LOGGER.info(server.getName() + " is started and listening on " + server.getPort()); // init datahost config.initDatasource(); long dataNodeIldeCheckPeriod = system.getDataNodeIdleCheckPeriod(); timer.schedule(updateTime(), 0L, TIME_UPDATE_PERIOD); timer.schedule(processorCheck(), 0L, system.getProcessorCheckPeriod()); timer.schedule(dataNodeConHeartBeatCheck(dataNodeIldeCheckPeriod), 0L, dataNodeIldeCheckPeriod); timer.schedule(dataNodeHeartbeat(), 0L, system.getDataNodeHeartbeatPeriod()); if(system.isGlobalTableCheckSwitchOn()) // 全局表一致性检测是否开启 timer.schedule(glableTableConsistencyCheck(), 0L, system.getGlableTableCheckPeriod()); timer.schedule(catletClassClear(), 30000); } private TimerTask catletClassClear() { return new TimerTask() { @Override public void run() { try { catletClassLoader.clearUnUsedClass(); } catch (Exception e) { LOGGER.warn("catletClassClear err " + e); } } }; } public RouteService getRouterService() { return routerService; } public CacheService getCacheService() { return cacheService; } public RouteService getRouterservice() { return routerService; } public long getStartupTime() { return startupTime; } public boolean isOnline() { return isOnline.get(); } public void offline() { isOnline.set(false); } public void online() { isOnline.set(true); } // 系统时间定时更新任务 private TimerTask updateTime() { return new TimerTask() { @Override public void run() { TimeUtil.update(); } }; } // 处理器定时检查任务 private TimerTask processorCheck() { return new TimerTask() { @Override public void run() { timerExecutor.execute(new Runnable() { @Override public void run() { try { NetSystem.getInstance().checkConnections(); } catch (Exception e) { LOGGER.warn("checkBackendCons caught err:", e); } } }); } }; } // 数据节点定时连接空闲超时检查任务 private TimerTask dataNodeConHeartBeatCheck(final long heartPeriod) { return new TimerTask() { @Override public void run() { timerExecutor.execute(new Runnable() { @Override public void run() { Map<String, PhysicalDBPool> nodes = config .getDataHosts(); for (PhysicalDBPool node : nodes.values()) { node.heartbeatCheck(heartPeriod); } Map<String, PhysicalDBPool> _nodes = config .getBackupDataHosts(); if (_nodes != null) { for (PhysicalDBPool node : _nodes.values()) { node.heartbeatCheck(heartPeriod); } } } }); } }; } // 全局表一致性检查任务 private TimerTask glableTableConsistencyCheck() { return new TimerTask() { @Override public void run() { timerExecutor.execute(new Runnable() { @Override public void run() { GlobalTableUtil.consistencyCheck(); } }); } }; } // 数据节点定时心跳任务 private TimerTask dataNodeHeartbeat() { return new TimerTask() { @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(); } } }); } }; } public ListeningExecutorService getListeningExecutorService() { return listeningExecutorService; } /** * save cur datanode index to properties file * * @param dataNode * @param curIndex */ public synchronized void saveDataHostIndex(String dataHost, int curIndex) { if(clusterSync==null){ clusterSync = ConfigFactory.instanceCluster(); } boolean isSwitch = clusterSync.switchDataSource(dataHost, curIndex); if(isSwitch){ config.setHostIndex(dataHost, curIndex); }else { LOGGER.warn("can't switch dataHost"+dataHost +" to curIndex " + curIndex); throw new ConfigException("can't switch dataHost"+dataHost +" to curIndex " + curIndex); } } }