sentry作为一个数据权限管理系统,本文不做具体介绍,详细介绍内容可以参考官方文档:http://sentry.apache.org/
在实际应用中,大家很可能会遇到一些问题,需要修改连接池的配置参数(不使用默认参数),而官方文档(包括后文提到的DataNucleus)中又缺少具体参数讲解,所以在本文主要介绍sentry底层存储在使用连接池时该如何配置。
下文以DataNucleus的JDO实现+BoneCP连接池展开,其他的连接池(DBCP、C3P0等)可根据代码剖析部分举一反三。
---------------------------------------------风流的分割线-------------------------------------------
1 代码剖析
1.1 SentryStore
sentry存储入口是org.apache.sentry.provider.db.service.persistent.SentryStore类,启动服务或启动存储时,都会调用SentryStore的构造方法,传入配置来初始化存储:
public SentryStore(Configuration conf) throws Exception {
this.conf = conf;
Properties prop = getDataNucleusProperties(conf);
boolean checkSchemaVersion = conf.get(
ServerConfig.SENTRY_VERIFY_SCHEM_VERSION,
ServerConfig.SENTRY_VERIFY_SCHEM_VERSION_DEFAULT).equalsIgnoreCase(
"true");
if (!checkSchemaVersion) {
prop.setProperty("datanucleus.schema.autoCreateAll", "true");
prop.setProperty("datanucleus.autoCreateSchema", "true");
prop.setProperty("datanucleus.fixedDatastore", "false");
}
pmf = JDOHelper.getPersistenceManagerFactory(prop);
tm = new TransactionManager(pmf, conf);
verifySentryStoreSchema(checkSchemaVersion);
long notificationTimeout = conf.getInt(ServerConfig.SENTRY_NOTIFICATION_SYNC_TIMEOUT_MS,
ServerConfig.SENTRY_NOTIFICATION_SYNC_TIMEOUT_DEFAULT);
counterWait = new CounterWait(notificationTimeout, TimeUnit.MILLISECONDS);
}
通过JDOHelper获取PersistenceManagerFactory时,根据prop中的“javax.jdo.PersistenceManagerFactoryClass”参数指定具体实现类。而sentry绝大部分prop都是在SentryStore.getDataNucleusProperties(Configuration conf)中设置的(从方法名上也可以看出来最后指定了DataNucleus实现的JDO),深入之后可以看到一系列sentry的默认配置:
"datanucleus.connectionPoolingType" = "BoneCP";
"datanucleus.validateTables" = "false";
datanucleus.validateColumns" = "false";
"datanucleus.validateConstraints" = "false";
"datanucleus.storeManagerType" = "rdbms";
"datanucleus.schema.autoCreateAll" = "true";
"datanucleus.autoCreateSchema" = "false";
"datanucleus.fixedDatastore" = "true";
"datanucleus.autoStartMechanismMode" = "checked";
DATANUCLEUS_ISOLATION_LEVEL = DATANUCLEUS_REPEATABLE_READ;
"datanucleus.cache.level2" = "false";
"datanucleus.cache.level2.type" = "none";
"datanucleus.query.sql.allowAll" = "true";
"datanucleus.identifierFactory" = "datanucleus1";
"datanucleus.rdbms.useLegacyNativeValueStrategy" = "true";
"datanucleus.plugin.pluginRegistryBundleCheck" = "LOG";
"javax.jdo.PersistenceManagerFactoryClass" = "org.datanucleus.api.jdo.JDOPersistenceManagerFactory";
"javax.jdo.option.DetachAllOnCommit" = "true";
"javax.jdo.option.NonTransactionalRead" = "false";
"javax.jdo.option.NonTransactionalWrite" = "false";
"javax.jdo.option.Multithreaded" = "true";
其中就包括"javax.jdo.PersistenceManagerFactoryClass"="org.datanucleus.api.jdo.JDOPersistenceManagerFactory",本文也就是sentry下的Datanucleus下的连接池配置进行展开。外部指定的数据库URL、userName、password也是在getDataNucleusProperties中进行的设置。
同时,我们还可以看到如何将自定义的javax或datanucleus属性传入sentry:
for (Map.Entry<String, String> entry : conf) {
String key = entry.getKey();
if (key.startsWith(ServerConfig.SENTRY_JAVAX_JDO_PROPERTY_PREFIX) ||
key.startsWith(ServerConfig.SENTRY_DATANUCLEUS_PROPERTY_PREFIX)) {
key = StringUtils.removeStart(key, ServerConfig.SENTRY_DB_PROPERTY_PREFIX);
prop.setProperty(key, entry.getValue());
}
}
只要带有“sentry.javax.jdo”或“sentry.datanucleus”前缀,就会截掉“sentry.”前缀,将剩余的部分设置到prop中。
1.2 ConnectionFactoryImpl
继续跟进代码,可以看到DataNucleus在org.datanucleus.store.rdbms.ConnectionFactoryImpl的initialiseDataSources()方法中初始化了数据源。
其根据上述参数中的"datanucleus.connectionPoolingType"来决定连接池类型:
requiredPoolingType = storeMgr.getStringProperty("datanucleus.connectionPoolingType");
我们以上述sentry默认的BoneCP为例继续进行讲解。
1.3 BoneCPConnectionPoolFactory
知道连接池的类型为BoneCP后,通过org.datanucleus.store.rdbms.connectionpool.BoneCPConnectionPoolFactory的createConnectionPool方法创建连接池。在这个工厂类里面,一切参数都一幕了然了:
// Create the actual pool of connections
com.jolbox.bonecp.BoneCPDataSource ds = new com.jolbox.bonecp.BoneCPDataSource(config);
// Apply any BoneCP properties
if (storeMgr.hasProperty(RDBMSPropertyNames.PROPERTY_CONNECTION_POOL_MAX_STATEMENTS))
{
int size = storeMgr.getIntProperty(RDBMSPropertyNames.PROPERTY_CONNECTION_POOL_MAX_STATEMENTS);
if (size >= 0)
{
ds.setStatementsCacheSize(size);
}
}
if (storeMgr.hasProperty(RDBMSPropertyNames.PROPERTY_CONNECTION_POOL_MAX_POOL_SIZE))
{
int size = storeMgr.getIntProperty(RDBMSPropertyNames.PROPERTY_CONNECTION_POOL_MAX_POOL_SIZE);
if (size >= 0)
{
ds.setMaxConnectionsPerPartition(size);
}
}
if (storeMgr.hasProperty(RDBMSPropertyNames.PROPERTY_CONNECTION_POOL_MIN_POOL_SIZE))
{
int size = storeMgr.getIntProperty(RDBMSPropertyNames.PROPERTY_CONNECTION_POOL_MIN_POOL_SIZE);
if (size >= 0)
{
ds.setMinConnectionsPerPartition(size);
}
}
if (storeMgr.hasProperty(RDBMSPropertyNames.PROPERTY_CONNECTION_POOL_MAX_IDLE))
{
int value = storeMgr.getIntProperty(RDBMSPropertyNames.PROPERTY_CONNECTION_POOL_MAX_IDLE);
if (value > 0)
{
ds.setIdleMaxAgeInMinutes(value);
}
}
可以看到,有4个自定义参数可以设置:
Constant | Content | Description |
PROPERTY_CONNECTION_POOL_MAX_STATEMENTS | datanucleus.connectionPool.maxStatements | 最大缓存语句数 |
PROPERTY_CONNECTION_POOL_MAX_POOL_SIZE | datanucleus.connectionPool.maxPoolSize | 每个partition的最大连接数 |
PROPERTY_CONNECTION_POOL_MIN_POOL_SIZE | datanucleus.connectionPool.minPoolSize | 每个partition的最小连接数 |
PROPERTY_CONNECTION_POOL_MAX_IDLE | datanucleus.connectionPool.maxIdle | 连接的最大空闲时间 |
(DataNucleus的官方文档中,只提到了前三个BoneCP配置参数:http://www.datanucleus.org/products/accessplatform_4_1/jdo/datastore_connection.html)
2 参数配置
考虑sentry截掉前缀的特性,正确配置BoneCP连接池的参数如下:
DataNucleus Parameter | Sentry Parameter |
datanucleus.connectionPool.maxStatements | sentry.datanucleus.connectionPool.maxStatements |
datanucleus.connectionPool.maxPoolSize | sentry.datanucleus.connectionPool.maxPoolSize |
datanucleus.connectionPool.minPoolSize | sentry.datanucleus.connectionPool.minPoolSize |
datanucleus.connectionPool.maxIdle | sentry.datanucleus.connectionPool.maxIdle |
(其余类型连接池的参数可以此类推。)