我编写了此类,以在持久配置数据发生更改时重新加载整个应用程序使用的DataSource
。
如您所见,它是由CDI
管理并显示为Singleton
,“配置已更改”事件是通过configurationReload(...)
方法到达的,但现在不相关了。
参考更新受ReentrantReadWriteLock
保护,但我想知道是否完全需要它。
@Singleton
@ThreadSafe
class ReloadingDataSource implements DataSource {
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final Lock readLock = readWriteLock.readLock();
private final Lock writeLock = readWriteLock.writeLock();
@GuardedBy("readWriteLock")
private DataSource delegateDataSource;
@Inject
ReloadingDataSource(@Nonnull final Configuration configuration) {
delegateDataSource = createDataSource(configuration);
}
private DataSource createDataSource(final Configuration configuration) {
... Create a ComboPooledDataSource using properties extracted from Configuration.
}
@Override
public Connection getConnection() throws SQLException {
readLock.lock();
try {
return delegateDataSource.getConnection();
} finally {
readLock.unlock();
}
}
...
private void configurationReload(
@Observes @Reload final ConfigurationChanged configurationChanged,
@Nonnull final Configuration configuration) {
final ConfigurationEvent event = configurationChanged.getConfigurationEvent();
if (event.getType() != AbstractFileConfiguration.EVENT_RELOAD && !event.isBeforeUpdate()) {
return;
}
writeLock.lock();
try {
destroyDelegateDataSource();
delegateDataSource = createDataSource(configuration);
} finally {
writeLock.unlock();
}
}
private void destroyDelegateDataSource() {
try {
DataSources.destroy(delegateDataSource);
} catch (final SQLException ignored) {
// Do nothing.
}
}
}
如果我们忽略了创建新数据源的成本,是否可以将上述策略替换为AtomicReference<DataSource>
,如下所示?
这将导致更好的性能和更易于阅读的代码。
还有我不知道的更好的处理方法吗?
@Singleton
@ThreadSafe
class ReloadingDataSource implements DataSource {
private final AtomicReference<DataSource> delegateDataSource;
@Inject
ReloadingDataSource(@Nonnull final Configuration configuration) {
delegateDataSource = new AtomicReference<>(createDataSource(configuration));
}
private DataSource createDataSource(final Configuration configuration) {
... Create a ComboPooledDataSource using properties extracted from Configuration.
}
@Override
public Connection getConnection() throws SQLException {
return delegateDataSource.get().getConnection();
}
...
private void configurationReload(
@Observes @Reload final ConfigurationChanged configurationChanged,
@Nonnull final Configuration configuration) {
final ConfigurationEvent event = configurationChanged.getConfigurationEvent();
if (event.getType() != AbstractFileConfiguration.EVENT_RELOAD && !event.isBeforeUpdate()) {
return;
}
// Updated as per eckes tip. Is this what you meant?
final DataSource newDataSource = createDataSource(configuration);
while (true) {
final DataSource oldDataSource = delegateDataSource.get();
if (delegateDataSource.compareAndSet(oldDataSource, newDataSource)) {
destroyDelegateDataSource(oldDataSource);
break;
}
}
}
private void destroyDelegateDataSource(final DataSource oldDataSource) {
try {
DataSources.destroy(oldDataSource);
} catch (final SQLException ignored) {
// Do nothing.
}
}
}
答案 0 :(得分:2)
如果需要以有序方式处理更新,则仍需要锁定reload方法。在这种情况下,您可以放弃AtomicReference逻辑,而只需使用volatile:
public class RDS {
private volatile DataSource delegate;
public Connection getConnection() throws SQLException {
return delegate.getConnection();
}
private void reload(Configuration config) {
DataSource old = null;
synchronized(this) {
old = delegate;
delegate = createDataSource(config);
}
destroyDataSource(old);
}
}
但是请注意,关闭旧数据源时,仍然可能存在其他问题,连接仍然可以用于旧数据源(在@eckes对问题的第一条评论中提到)。为了解决这个问题,您需要一个带有“获取/释放”类型逻辑的连接池,一旦所有现有连接都被释放,它就会关闭旧的委托。