/* * Copyright(C) 2010-2011 Alibaba Group Holding Limited All rights reserved. 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 com.alibaba.doris.client; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.doris.client.net.DataSource; import com.alibaba.doris.client.operation.OperationDataSourceGroup; import com.alibaba.doris.common.StoreNode; import com.alibaba.doris.common.config.ConfigManager; import com.alibaba.doris.common.configer.RouteTableConfiger; import com.alibaba.doris.common.event.RouteConfigChangeEvent; import com.alibaba.doris.common.event.RouteConfigListener; import com.alibaba.doris.common.operation.OperationEnum; import com.alibaba.doris.common.route.DorisRouterException; import com.alibaba.doris.common.route.RouteStrategy; import com.alibaba.doris.common.route.RouteTable; import com.alibaba.doris.common.route.VirtualRouter; /** * DataSourceRouterImpl * * @author Kun He (Raymond He), kun.hek@alibaba-inc.com * @since 1.0 2011-5-5 */ public class DataSourceRouterImpl implements DataSourceRouter { private static final Logger logger = LoggerFactory.getLogger(DataSourceRouterImpl.class); private Class<? extends DataSource> dataSourceClass; private Class<? extends RouteTableConfiger> nodeConfigerClass; private Class<? extends RouteStrategy> routeStrategyClass; private RouteTableConfiger routeTableConfiger; private volatile RouteStrategyHolder routeStrategyHolder; private Properties configProperties; private RouteConfigListenerWrapper routeConfigListener; private ConfigManager configManager; public VirtualRouter virtualRouter; public void setDataSourceClass(Class<? extends DataSource> dataSourceClass) { this.dataSourceClass = dataSourceClass; } public void setNodeConfigerClass(Class<? extends RouteTableConfiger> nodeConfigerClass) { this.nodeConfigerClass = nodeConfigerClass; } public void setNodeRouterClass(Class<? extends RouteStrategy> nodeRouterClass) { this.routeStrategyClass = nodeRouterClass; } /** * @see com.alibaba.doris.client.DataSourceRouter#getRouteTableConfiger() */ public RouteTableConfiger getRouteTableConfiger() { return routeTableConfiger; } /** * @see com.alibaba.doris.client.DataSourceRouter#getRouteStrategy() */ public RouteStrategy getRouteStrategy() { return routeStrategyHolder.routeStrategy; } public Map<String, List<DataSource>> getAllDataSources() { return routeStrategyHolder.allDataSources; } public void setConfigProperties(Properties properties) { this.configProperties = properties; } public void setVirtualRouter(VirtualRouter virtualRouter) { this.virtualRouter = virtualRouter; } public VirtualRouter getVirtualRouter() { return virtualRouter; } /** * Get operation datasources group by key and write/read count. * <p/> * e.g. * <li>getOperationDataSourceGroup("001", 2), return datasource 2,3 * <li>getOperationDataSourceGroup("001", 1), return datasource 2 * <li>getOperationDataSourceGroup("001", 3), return datasource 2,3,5 * * @throws DorisRouterException * @see com.alibaba.doris.client.DataSourceRouter#getOperationDataSourceGroup(java.lang.Object, int) */ public OperationDataSourceGroup getOperationDataSourceGroup(OperationEnum operationEnum, int count, String key) throws DorisRouterException { RouteStrategyHolder rHolder = routeStrategyHolder; List<StoreNode> storeNodes = rHolder.routeStrategy.findNodes(operationEnum, count, key); List<DataSource> dataSourceGroup = new ArrayList<DataSource>(); for (StoreNode storeNode : storeNodes) { String seqNo = String.valueOf(storeNode.getSequence().getValue()); List<DataSource> seqDataSources = rHolder.allDataSources.get(seqNo); DataSource dataSource = seqDataSources.get(storeNode.getLogicId()); dataSourceGroup.add(dataSource); } OperationDataSourceGroup group = new OperationDataSourceGroup(key, dataSourceGroup, storeNodes); return group; } /** * Init config after setting values. * * @throws DataSourceException */ public void refresh() throws DataSourceException { // refreshDataSource(); } /** * Recreate all the datasource according to the lastest node list. * * @throws DataSourceException */ protected void refreshDataSource(RouteConfigChangeEvent event) throws DataSourceException { RouteTable routeTable = event.getRouteTable(); List<StoreNode> nodeList = routeTable.getNodeList(); RouteStrategyHolder rHolder = generateRouteStrategyHolder(routeTable); rHolder.allDataSources = new HashMap<String, List<DataSource>>(); Map<String, List<DataSource>> tobeClosedDataSources = null; if (null != this.routeStrategyHolder) { tobeClosedDataSources = cloneDataSources(this.routeStrategyHolder.allDataSources); } for (int i = 0; i < nodeList.size(); i++) { StoreNode storeNode = nodeList.get(i); try { int seqNo = storeNode.getSequence().getValue(); int logicId = storeNode.getLogicId(); String strSeqNo = String.valueOf(seqNo); ArrayList<DataSource> seqDatasources = (ArrayList<DataSource>) rHolder.allDataSources.get(strSeqNo); if (seqDatasources == null) { seqDatasources = new ArrayList<DataSource>(); rHolder.allDataSources.put(strSeqNo, seqDatasources); } DataSource dataSource = (DataSource) dataSourceClass.newInstance(); dataSource.setSequence(seqNo); dataSource.setNo(logicId); dataSource.setIp(storeNode.getIp()); dataSource.setPort(storeNode.getPort()); dataSource.setConfigProperties(configManager.getProperties()); dataSource.initConfig(); if (null != tobeClosedDataSources) { List<DataSource> tobeClosedDataSourceList = tobeClosedDataSources.get(strSeqNo); // 扩容情况时,后加入的节点的logicId会大于tobeClosedDataSourceList的size if (tobeClosedDataSourceList.size()>logicId) { DataSource ds = tobeClosedDataSourceList.get(logicId); if (dataSource.equals(ds)) { // 设置原有连接不释放。 tobeClosedDataSourceList.set(logicId, null); // 当原有连接已经存在,则直接重用原来的连接。 dataSource = ds; } } } seqDatasources.ensureCapacity(storeNode.getLogicId() + 1); seqDatasources.add(storeNode.getLogicId(), dataSource); if (logger.isDebugEnabled()) { logger.debug("Create datasource: " + dataSource + " " + storeNode + ", " + storeNode.getPhId()); } } catch (Exception e) { throw new DataSourceException("Fail to create DataSource." + e.getMessage(), e); } } // Everything is okay, routeConfigListener.setRouteConfigListener(rHolder.routeStrategy); rHolder.routeStrategy.onConfigChange(event); this.routeStrategyHolder = rHolder; // close all connection. releaseDatasourcesThread.release(tobeClosedDataSources); } /** * 获取某个数据源对应的 StoreNode. */ public StoreNode getStoreNodeOf(DataSource dataSource) { int seqNo = dataSource.getSequence(); int logicId = dataSource.getNo(); StoreNode storeNode = routeTableConfiger.getRouteTable().getStoreNode(seqNo, logicId); return storeNode; } @SuppressWarnings("unchecked") private Map<String, List<DataSource>> cloneDataSources(Map<String, List<DataSource>> source) { Map<String, List<DataSource>> result = new HashMap<String, List<DataSource>>(); Iterator<String> keyIterator = source.keySet().iterator(); while (keyIterator.hasNext()) { String key = keyIterator.next(); ArrayList<DataSource> sourceDsList = (ArrayList<DataSource>) source.get(key); result.put(key, (ArrayList<DataSource>) sourceDsList.clone()); } return result; } /** * 获取某个StoreNode对应的DataSource. */ public DataSource getDataSourceOf(StoreNode storeNode) { int seqNo = storeNode.getSequence().getValue(); int logicId = storeNode.getLogicId(); String seqNoStr = String.valueOf(seqNo); List<DataSource> seqDataSources = routeStrategyHolder.allDataSources.get(seqNoStr); if (logicId < seqDataSources.size()) { DataSource dataSource = seqDataSources.get(logicId); return dataSource; } else { throw new IllegalArgumentException("Cant't find DataSource of storeNode " + storeNode); } } public void initConfig() { try { if (null == releaseDatasourcesThread) { releaseDatasourcesThread = new ReleaseDatasourcesThread(); releaseDatasourcesThread.start(); } routeTableConfiger = nodeConfigerClass.newInstance(); // routeStrategy = routeStrategyClass.newInstance(); routeTableConfiger.setConfigManager(configManager); // routeStrategy.setConfigManager(configManager); // routeStrategyHolder = generateRouteStrategyHolder(); routeConfigListener = new RouteConfigListenerWrapper(); routeTableConfiger.addConfigListener(this); // routeTableConfiger.addConfigListener(routeConfigListener); configManager.addConfigListener(routeTableConfiger); routeTableConfiger.initConfig(); } catch (Exception e) { logger.error("initConfig", e); } } private RouteStrategyHolder generateRouteStrategyHolder(RouteTable routeTable) { RouteStrategyHolder holder = new RouteStrategyHolder(); try { holder.routeStrategy = routeStrategyClass.newInstance(); holder.routeStrategy.setConfigManager(configManager); holder.routeStrategy.setRouteTable(routeTable); holder.routeStrategy.initConfig(); } catch (Exception e) { logger.error("generateRouteStrategyHolder", e); } return holder; } public void onConfigChange(RouteConfigChangeEvent event) { try { refreshDataSource(event); } catch (DataSourceException e) { logger.error("refreshDataSource", e); } } public void setConfigManager(ConfigManager configManager) { this.configManager = configManager; } private static class RouteStrategyHolder { RouteStrategy routeStrategy; Map<String, List<DataSource>> allDataSources; } private static class RouteConfigListenerWrapper implements RouteConfigListener { public void onConfigChange(RouteConfigChangeEvent event) { if (null != routeConfigListener) { routeConfigListener.onConfigChange(event); } } public void setRouteConfigListener(RouteConfigListener routeConfigListener) { this.routeConfigListener = routeConfigListener; } private volatile RouteConfigListener routeConfigListener; } private static class ReleaseDatasourcesThread extends Thread { public ReleaseDatasourcesThread() { setName("Release-Datasources-Thread cleanup"); setDaemon(true); } public void run() { try { while (true) { Map<String, List<DataSource>> dataSources = releaseDatasourceQueue.take(); if (null != dataSources) { Thread.sleep(10000); Collection<List<DataSource>> datasources = dataSources.values(); for (List<DataSource> datasourceList : datasources) { for (DataSource datasource : datasourceList) { if (datasource != null) { close(datasource); } } } } } } catch (InterruptedException ignore) { } } private void close(DataSource datasource) { try { datasource.close(); } catch (Exception e) { logger.error("Close datasource failed! " + datasource, e); } } public void release(Map<String, List<DataSource>> dataSources) { if (null != dataSources) { try { releaseDatasourceQueue.put(dataSources); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } private BlockingQueue<Map<String, List<DataSource>>> releaseDatasourceQueue = new ArrayBlockingQueue<Map<String, List<DataSource>>>( 100); } private ReleaseDatasourcesThread releaseDatasourcesThread; }