parent
b9ff53b49c
commit
1b55e68fd4
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.nacos.config.server.service.datasource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
/**
|
||||
* Properties of external DataSource
|
||||
*
|
||||
* @author Nacos
|
||||
*/
|
||||
public class ExternalDataSourceProperties {
|
||||
|
||||
private final static String JDBC_DRIVER_NAME = "com.mysql.cj.jdbc.Driver";
|
||||
public static final long CONNECTION_TIMEOUT_MS = 3000L;
|
||||
public static final long VALIDATION_TIMEOUT = 10L;
|
||||
public static final String TEST_QUERY = "SELECT 1 FROM dual";
|
||||
public static final int DEFAULT_MAX_POOL_SIZE = 20;
|
||||
public static final int DEFAULT_MINIMUM_IDLE = 50;
|
||||
|
||||
private Integer num;
|
||||
private List<String> url = new ArrayList<>();
|
||||
private List<String> user = new ArrayList<>();
|
||||
private List<String> password = new ArrayList<>();
|
||||
private List<Integer> maxPoolSize = new ArrayList<>();
|
||||
private List<Integer> minIdle = new ArrayList<>();
|
||||
|
||||
public void setNum(Integer num) {
|
||||
this.num = num;
|
||||
}
|
||||
|
||||
public void setUrl(List<String> url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public void setUser(List<String> user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public void setPassword(List<String> password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setMaxPoolSize(List<Integer> maxPoolSize) {
|
||||
this.maxPoolSize = maxPoolSize;
|
||||
}
|
||||
|
||||
public void setMinIdle(List<Integer> minIdle) {
|
||||
this.minIdle = minIdle;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param environment
|
||||
* {@link Environment}
|
||||
* @param callback
|
||||
* Callback function when constructing data source
|
||||
* @return List of {@link HikariDataSource}
|
||||
*/
|
||||
List<HikariDataSource> build(Environment environment, Callback<HikariDataSource> callback) {
|
||||
List<HikariDataSource> dataSources = new ArrayList<>();
|
||||
Binder.get(environment).bind("db", Bindable.ofInstance(this));
|
||||
Preconditions.checkArgument(Objects.nonNull(num), "db.num is null");
|
||||
Preconditions.checkArgument(CollectionUtils.isNotEmpty(user), "db.user or db.user.[index] is null");
|
||||
Preconditions.checkArgument(CollectionUtils.isNotEmpty(password), "db.password or db.password.[index] is null");
|
||||
for (int index = 0; index < num; index++) {
|
||||
int currentSize = index + 1;
|
||||
Preconditions.checkArgument(url.size() >= currentSize, "db.url.%s is null", index);
|
||||
HikariDataSource ds = new HikariDataSource();
|
||||
ds.setDriverClassName(JDBC_DRIVER_NAME);
|
||||
ds.setJdbcUrl(url.get(index).trim());
|
||||
ds.setUsername(defaultIfNull(user, index, user.get(0)).trim());
|
||||
ds.setPassword(defaultIfNull(password, index, password.get(0)).trim());
|
||||
ds.setConnectionTimeout(CONNECTION_TIMEOUT_MS);
|
||||
ds.setMaximumPoolSize(defaultIfNull(maxPoolSize, index, DEFAULT_MAX_POOL_SIZE));
|
||||
ds.setMinimumIdle(defaultIfNull(minIdle, index, DEFAULT_MINIMUM_IDLE));
|
||||
// Check the connection pool every 10 minutes
|
||||
ds.setValidationTimeout(TimeUnit.MINUTES.toMillis(VALIDATION_TIMEOUT));
|
||||
ds.setConnectionTestQuery(TEST_QUERY);
|
||||
dataSources.add(ds);
|
||||
callback.accept(ds);
|
||||
}
|
||||
Preconditions.checkArgument(CollectionUtils.isNotEmpty(dataSources), "no datasource available");
|
||||
return dataSources;
|
||||
}
|
||||
|
||||
static <T> T defaultIfNull(List<T> collection, int index, T defaultValue) {
|
||||
try {
|
||||
return collection.get(index);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
interface Callback<T> {
|
||||
|
||||
/**
|
||||
* Perform custom logic
|
||||
* @param t
|
||||
*/
|
||||
void accept(T t);
|
||||
}
|
||||
|
||||
}
|
@ -15,13 +15,19 @@
|
||||
*/
|
||||
package com.alibaba.nacos.config.server.service.datasource;
|
||||
|
||||
import com.alibaba.nacos.common.utils.ConvertUtils;
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
import com.alibaba.nacos.config.server.monitor.MetricsMonitor;
|
||||
import com.alibaba.nacos.config.server.utils.ConfigExecutor;
|
||||
import com.alibaba.nacos.config.server.utils.PropertyUtil;
|
||||
import com.alibaba.nacos.core.utils.ApplicationUtils;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import static com.alibaba.nacos.config.server.service.repository.RowMapperManager.CONFIG_INFO4BETA_ROW_MAPPER;
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog;
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
@ -30,18 +36,13 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.sql.DataSource;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.alibaba.nacos.config.server.service.repository.RowMapperManager.CONFIG_INFO4BETA_ROW_MAPPER;
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog;
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog;
|
||||
import com.alibaba.nacos.common.utils.ConvertUtils;
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
import com.alibaba.nacos.config.server.monitor.MetricsMonitor;
|
||||
import com.alibaba.nacos.config.server.utils.ConfigExecutor;
|
||||
import com.alibaba.nacos.config.server.utils.PropertyUtil;
|
||||
import com.alibaba.nacos.core.utils.ApplicationUtils;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
/**
|
||||
* Base data source
|
||||
@ -52,9 +53,7 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(
|
||||
ExternalDataSourceServiceImpl.class);
|
||||
private static final String DEFAULT_MYSQL_DRIVER = "com.mysql.jdbc.Driver";
|
||||
private static final String MYSQL_HIGH_LEVEL_DRIVER = "com.mysql.cj.jdbc.Driver";
|
||||
private static String JDBC_DRIVER_NAME;
|
||||
private final static String JDBC_DRIVER_NAME="com.mysql.cj.jdbc.Driver";
|
||||
|
||||
/**
|
||||
* JDBC执行超时时间, 单位秒
|
||||
@ -78,24 +77,13 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
|
||||
private volatile int masterIndex;
|
||||
private static Pattern ipPattern = Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName(MYSQL_HIGH_LEVEL_DRIVER);
|
||||
JDBC_DRIVER_NAME = MYSQL_HIGH_LEVEL_DRIVER;
|
||||
log.info("Use Mysql 8 as the driver");
|
||||
} catch (ClassNotFoundException e) {
|
||||
log.info("Use Mysql as the driver");
|
||||
JDBC_DRIVER_NAME = DEFAULT_MYSQL_DRIVER;
|
||||
}
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
queryTimeout = ConvertUtils.toInt(System.getProperty("QUERYTIMEOUT"), 3);
|
||||
jt = new JdbcTemplate();
|
||||
/**
|
||||
* 设置最大记录数,防止内存膨胀
|
||||
*/
|
||||
// Set the maximum number of records to prevent memory expansion
|
||||
jt.setMaxRows(50000);
|
||||
jt.setQueryTimeout(queryTimeout);
|
||||
|
||||
@ -103,20 +91,18 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
|
||||
testMasterJT.setQueryTimeout(queryTimeout);
|
||||
|
||||
testMasterWritableJT = new JdbcTemplate();
|
||||
/**
|
||||
* 防止login接口因为主库不可用而rt太长
|
||||
*/
|
||||
// Prevent the login interface from being too long because the main library is not available
|
||||
testMasterWritableJT.setQueryTimeout(1);
|
||||
/**
|
||||
* 数据库健康检测
|
||||
*/
|
||||
|
||||
// Database health check
|
||||
|
||||
testJTList = new ArrayList<JdbcTemplate>();
|
||||
isHealthList = new ArrayList<Boolean>();
|
||||
|
||||
tm = new DataSourceTransactionManager();
|
||||
tjt = new TransactionTemplate(tm);
|
||||
/**
|
||||
* 事务的超时时间需要与普通操作区分开
|
||||
* Transaction timeout needs to be distinguished from ordinary operations
|
||||
*/
|
||||
tjt.setTimeout(TRANSACTION_QUERY_TIMEOUT);
|
||||
if (PropertyUtil.isUseExternalDB()) {
|
||||
@ -136,73 +122,20 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
|
||||
|
||||
@Override
|
||||
public synchronized void reload() throws IOException {
|
||||
List<HikariDataSource> dblist = new ArrayList<>();
|
||||
try {
|
||||
String val = null;
|
||||
val = ApplicationUtils.getProperty("db.num");
|
||||
if (null == val) {
|
||||
throw new IllegalArgumentException("db.num is null");
|
||||
}
|
||||
int dbNum = Integer.parseInt(val.trim());
|
||||
|
||||
for (int i = 0; i < dbNum; i++) {
|
||||
HikariDataSource ds = new HikariDataSource();
|
||||
ds.setDriverClassName(JDBC_DRIVER_NAME);
|
||||
|
||||
val = ApplicationUtils.getProperty("db.url." + i);
|
||||
if (null == val) {
|
||||
fatalLog.error("db.url." + i + " is null");
|
||||
throw new IllegalArgumentException("db.url." + i + " is null");
|
||||
}
|
||||
ds.setJdbcUrl(val.trim());
|
||||
|
||||
val = ApplicationUtils.getProperty("db.user." + i, ApplicationUtils.getProperty("db.user"));
|
||||
if (null == val) {
|
||||
fatalLog.error("db.user." + i + " is null");
|
||||
throw new IllegalArgumentException("db.user." + i + " is null");
|
||||
}
|
||||
ds.setUsername(val.trim());
|
||||
|
||||
val = ApplicationUtils.getProperty("db.password." + i, ApplicationUtils.getProperty("db.password"));
|
||||
if (null == val) {
|
||||
fatalLog.error("db.password." + i + " is null");
|
||||
throw new IllegalArgumentException("db.password." + i + " is null");
|
||||
}
|
||||
ds.setPassword(val.trim());
|
||||
|
||||
val = ApplicationUtils.getProperty("db.maxPoolSize." + i, ApplicationUtils.getProperty("db.maxPoolSize"));
|
||||
ds.setMaximumPoolSize(Integer.parseInt(defaultIfNull(val, "20")));
|
||||
|
||||
val = ApplicationUtils.getProperty("db.minIdle." + i, ApplicationUtils.getProperty("db.minIdle"));
|
||||
ds.setMinimumIdle(Integer.parseInt(defaultIfNull(val, "50")));
|
||||
|
||||
ds.setConnectionTimeout(3000L);
|
||||
|
||||
// 每10分钟检查一遍连接池
|
||||
ds.setValidationTimeout(TimeUnit.MINUTES.toMillis(10L));
|
||||
ds.setConnectionTestQuery("SELECT 1 FROM dual");
|
||||
|
||||
dblist.add(ds);
|
||||
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate();
|
||||
jdbcTemplate.setQueryTimeout(queryTimeout);
|
||||
jdbcTemplate.setDataSource(ds);
|
||||
|
||||
testJTList.add(jdbcTemplate);
|
||||
isHealthList.add(Boolean.TRUE);
|
||||
}
|
||||
|
||||
if (dblist == null || dblist.size() == 0) {
|
||||
throw new RuntimeException("no datasource available");
|
||||
}
|
||||
|
||||
dataSourceList = dblist;
|
||||
dataSourceList = new ExternalDataSourceProperties()
|
||||
.build(ApplicationUtils.getEnvironment(), (dataSource) -> {
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate();
|
||||
jdbcTemplate.setQueryTimeout(queryTimeout);
|
||||
jdbcTemplate.setDataSource(dataSource);
|
||||
testJTList.add(jdbcTemplate);
|
||||
isHealthList.add(Boolean.TRUE);
|
||||
});
|
||||
new SelectMasterTask().run();
|
||||
new CheckDBHealthTask().run();
|
||||
} catch (RuntimeException e) {
|
||||
fatalLog.error(DB_LOAD_ERROR_MSG, e);
|
||||
throw new IOException(e);
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,9 +143,7 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
|
||||
public boolean checkMasterWritable() {
|
||||
|
||||
testMasterWritableJT.setDataSource(jt.getDataSource());
|
||||
/**
|
||||
* 防止login接口因为主库不可用而rt太长
|
||||
*/
|
||||
// Prevent the login interface from being too long because the main library is not available
|
||||
testMasterWritableJT.setQueryTimeout(1);
|
||||
String sql = " SELECT @@read_only ";
|
||||
|
||||
@ -255,13 +186,11 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
|
||||
for (int i = 0; i < isHealthList.size(); i++) {
|
||||
if (!isHealthList.get(i)) {
|
||||
if (i == masterIndex) {
|
||||
/**
|
||||
* 主库不健康
|
||||
*/
|
||||
// The master is unhealthy
|
||||
return "DOWN:" + getIpFromUrl(dataSourceList.get(i).getJdbcUrl());
|
||||
} else {
|
||||
/**
|
||||
* 从库不健康
|
||||
* The slave is unhealthy
|
||||
*/
|
||||
return "WARN:" + getIpFromUrl(dataSourceList.get(i).getJdbcUrl());
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ if %FUNCTION_MODE% == "naming" (
|
||||
set "JAVA_OPT=%JAVA_OPT% -Dnacos.functionMode=naming"
|
||||
)
|
||||
|
||||
set "JAVA_OPT=%JAVA_OPT% -Dloader.path=%BASE_DIR%/plugins/health,%BASE_DIR%/plugins/cmdb,%BASE_DIR%/plugins/mysql"
|
||||
set "JAVA_OPT=%JAVA_OPT% -Dloader.path=%BASE_DIR%/plugins/health,%BASE_DIR%/plugins/cmdb"
|
||||
|
||||
set "JAVA_OPT=%JAVA_OPT% -Dnacos.home=%BASE_DIR%"
|
||||
set "JAVA_OPT=%JAVA_OPT% -jar %BASE_DIR%\target\%SERVER%.jar"
|
||||
|
@ -113,7 +113,7 @@ else
|
||||
JAVA_OPT="${JAVA_OPT} -Xloggc:${BASE_DIR}/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M"
|
||||
fi
|
||||
|
||||
JAVA_OPT="${JAVA_OPT} -Dloader.path=${BASE_DIR}/plugins/health,${BASE_DIR}/plugins/cmdb,${BASE_DIR}/plugins/mysql"
|
||||
JAVA_OPT="${JAVA_OPT} -Dloader.path=${BASE_DIR}/plugins/health,${BASE_DIR}/plugins/cmdb"
|
||||
JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}"
|
||||
JAVA_OPT="${JAVA_OPT} -jar ${BASE_DIR}/target/${SERVER}.jar"
|
||||
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
|
||||
|
2
pom.xml
2
pom.xml
@ -141,7 +141,7 @@
|
||||
<httpclient.version>4.5</httpclient.version>
|
||||
<httpasyncclient.version>4.1.3</httpasyncclient.version>
|
||||
<async-http-client.version>1.7.17</async-http-client.version>
|
||||
<mysql-connector-java.version>5.1.34</mysql-connector-java.version>
|
||||
<mysql-connector-java.version>8.0.13</mysql-connector-java.version>
|
||||
<derby.version>10.14.2.0</derby.version>
|
||||
<cglib-nodep.version>2.1</cglib-nodep.version>
|
||||
<jcip-annotations.version>1.0</jcip-annotations.version>
|
||||
|
Loading…
Reference in New Issue
Block a user