6
6
import java .sql .SQLFeatureNotSupportedException ;
7
7
import java .util .LinkedList ;
8
8
import java .util .List ;
9
- import java .util .Queue ;
10
9
import java .util .concurrent .*;
10
+ import java .util .concurrent .atomic .AtomicBoolean ;
11
11
import java .util .concurrent .atomic .AtomicReference ;
12
- import java .util .concurrent .locks .LockSupport ;
13
12
14
13
import javax .sql .DataSource ;
14
+ import javax .sql .PooledConnection ;
15
15
16
16
import com .ctrip .platform .dal .dao .configure .DataSourceConfigureChangeEvent ;
17
17
import com .ctrip .platform .dal .dao .configure .DataSourceConfigure ;
18
18
import com .ctrip .platform .dal .dao .configure .DataSourceConfigureChangeListener ;
19
19
import com .ctrip .platform .dal .dao .helper .CustomThreadFactory ;
20
20
import com .ctrip .platform .dal .dao .helper .DalElementFactory ;
21
21
import com .ctrip .platform .dal .dao .log .ILogger ;
22
+ import com .mysql .jdbc .MySQLConnection ;
23
+ import org .apache .commons .lang .StringUtils ;
22
24
23
25
public class RefreshableDataSource implements DataSource , DataSourceConfigureChangeListener {
24
26
private static ILogger LOGGER = DalElementFactory .DEFAULT .getILogger ();
@@ -27,14 +29,16 @@ public class RefreshableDataSource implements DataSource, DataSourceConfigureCha
27
29
28
30
private AtomicReference <String > connectionIpReference = new AtomicReference <>();
29
31
30
- private List <DataSourceSwitchListener > dataSourceSwitchListeners = new LinkedList <>();
32
+ private CopyOnWriteArraySet <DataSourceSwitchListener > dataSourceSwitchListeners = new CopyOnWriteArraySet <>();
33
+
34
+ private AtomicBoolean isListenerExecuted = new AtomicBoolean (false );
31
35
32
36
private ScheduledExecutorService service =
33
37
Executors .newScheduledThreadPool (POOL_SIZE , new CustomThreadFactory (THREAD_NAME ));
34
38
35
39
private static ThreadPoolExecutor executor ;
36
40
37
- private Queue < Thread > waiters = new ConcurrentLinkedQueue <> ();
41
+ private static ExecutorService getConnExecutor = Executors . newSingleThreadExecutor ();
38
42
39
43
private static final int INIT_DELAY = 0 ;
40
44
private static final int POOL_SIZE = 1 ;
@@ -47,6 +51,7 @@ public class RefreshableDataSource implements DataSource, DataSourceConfigureCha
47
51
public RefreshableDataSource (String name , DataSourceConfigure config ) throws SQLException {
48
52
SingleDataSource dataSource = new SingleDataSource (name , config );
49
53
dataSourceReference .set (dataSource );
54
+ asyncGetConnection ();
50
55
executor = new ThreadPoolExecutor (CORE_POOL_SIZE , MAX_POOL_SIZE , KEEP_ALIVE_TIME , TimeUnit .SECONDS ,
51
56
new LinkedBlockingQueue <Runnable >(),
52
57
new CustomThreadFactory ("DataSourceSwitchListener" ));
@@ -62,14 +67,10 @@ public synchronized void configChanged(DataSourceConfigureChangeEvent event) thr
62
67
63
68
public void refreshDataSource (String name , DataSourceConfigure configure ) throws SQLException {
64
69
SingleDataSource newDataSource = createSingleDataSource (name , configure , null );
65
- SingleDataSource oldDataSource = dataSourceReference .getAndSet (newDataSource );
66
- executeDataSourceListener (name );
67
- newDataSource .setSwitching (false );
68
- for (Thread waiter : waiters ) {
69
- LockSupport .unpark (waiter );
70
- }
71
- waiters .clear ();
72
70
getExecutorService ().schedule (newDataSource .getTask (), INIT_DELAY , TimeUnit .MILLISECONDS );
71
+ isListenerExecuted .set (false );
72
+ SingleDataSource oldDataSource = dataSourceReference .getAndSet (newDataSource );
73
+ asyncGetConnection ();
73
74
close (oldDataSource );
74
75
DataSourceCreateTask oldTask = oldDataSource .getTask ();
75
76
if (oldTask != null )
@@ -83,7 +84,9 @@ public void run() {
83
84
SingleDataSource newDataSource = createSingleDataSource (name , configure , listener );
84
85
boolean isSuccess = newDataSource .createPool (name , configure );
85
86
if (isSuccess ) {
87
+ isListenerExecuted .set (false );
86
88
SingleDataSource oldDataSource = dataSourceReference .getAndSet (newDataSource );
89
+ asyncGetConnection ();
87
90
listener .onCreatePoolSuccess ();
88
91
close (oldDataSource );
89
92
DataSourceCreateTask oldTask = oldDataSource .getTask ();
@@ -115,19 +118,7 @@ public DataSource getDataSource() {
115
118
SingleDataSource singleDataSource = getSingleDataSource ();
116
119
if (singleDataSource == null )
117
120
throw new IllegalStateException ("SingleDataSource can't be null." );
118
- for (;;) {
119
- if (!singleDataSource .getSwitching ()) {
120
- break ;
121
- }
122
- else {
123
- if (singleDataSource .getSwitching ()) {
124
- waiters .add (Thread .currentThread ());
125
- }
126
- if (singleDataSource .getSwitching ()) {
127
- LockSupport .park (this );
128
- }
129
- }
130
- }
121
+
131
122
DataSource dataSource = singleDataSource .getDataSource ();
132
123
if (dataSource == null )
133
124
throw new IllegalStateException ("DataSource can't be null." );
@@ -147,35 +138,67 @@ private ScheduledExecutorService getExecutorService() {
147
138
}
148
139
149
140
private void executeDataSourceListener (final String keyName ) {
150
- if (dataSourceSwitchListeners .size () == 0 ) {
151
- return ;
152
- }
153
- final CountDownLatch latch = new CountDownLatch (dataSourceSwitchListeners .size ());
154
- for (final DataSourceSwitchListener dataSourceSwitchListener : dataSourceSwitchListeners ) {
155
- if (dataSourceSwitchListener != null ) {
156
- executor .submit (new Runnable () {
157
- @ Override
158
- public void run () {
159
- try {
160
- dataSourceSwitchListener .preHandle ();
161
- latch .countDown ();
162
- } catch (Throwable e ) {
163
- LOGGER .error (String .format ("execute datasource switch listener fail for %s" , keyName ), e );
141
+ synchronized (this ) {
142
+ if (isListenerExecuted .get ()) {
143
+ return ;
144
+ }
145
+ final CountDownLatch latch = new CountDownLatch (dataSourceSwitchListeners .size ());
146
+ for (final DataSourceSwitchListener dataSourceSwitchListener : dataSourceSwitchListeners ) {
147
+ if (dataSourceSwitchListener != null ) {
148
+ executor .submit (new Runnable () {
149
+ @ Override
150
+ public void run () {
151
+ try {
152
+ dataSourceSwitchListener .execute ();
153
+ latch .countDown ();
154
+ } catch (Throwable e ) {
155
+ LOGGER .error (String .format ("execute datasource switch listener fail for %s" , keyName ), e );
156
+ }
164
157
}
165
- }
166
- });
158
+ });
159
+ }
160
+ }
161
+ try {
162
+ latch .await (TIME_OUT , TimeUnit .MILLISECONDS );
163
+ } catch (InterruptedException e ) {
164
+ LOGGER .error (String .format ("timeout,execute datasource switch listener is interrupted for %s" , keyName ), e );
165
+ } finally {
166
+ isListenerExecuted .set (true );
167
167
}
168
- }
169
- try {
170
- latch .await (TIME_OUT , TimeUnit .MILLISECONDS );
171
- } catch (InterruptedException e ) {
172
- LOGGER .error (String .format ("timeout,execute datasource switch listener is interrupted for %s" , keyName ), e );
173
168
}
174
169
}
175
170
171
+ private void asyncGetConnection () {
172
+ getConnExecutor .submit (new Runnable () {
173
+ @ Override
174
+ public void run () {
175
+ try {
176
+ getConnection ();
177
+ } catch (SQLException e ) {
178
+ //ignore
179
+ }
180
+ }
181
+ });
182
+ }
183
+
176
184
@ Override
177
185
public Connection getConnection () throws SQLException {
178
- return getDataSource ().getConnection ();
186
+ Connection connection = getDataSource ().getConnection ();
187
+ if (dataSourceSwitchListeners .size () > 0 ) {
188
+ String currentIp = ((MySQLConnection )(((PooledConnection )connection ).getConnection ())).getIO ().mysqlConnection .getInetAddress ().getHostAddress ();
189
+ String oldIp = connectionIpReference .get ();
190
+ if (StringUtils .isEmpty (oldIp )) {
191
+ connectionIpReference .set (currentIp );
192
+ }
193
+ else {
194
+ if (!oldIp .equalsIgnoreCase (currentIp )) {
195
+ String keyName = getSingleDataSource ().getName ();
196
+ executeDataSourceListener (keyName );
197
+ connectionIpReference .set (currentIp );
198
+ }
199
+ }
200
+ }
201
+ return connection ;
179
202
}
180
203
181
204
@ Override
0 commit comments