Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 6204889

Browse files
author
何惠民
committed
Completed multiple and dynamic datasource and load balance
1 parent f20c348 commit 6204889

15 files changed

+238
-96
lines changed

‎src/main/java/cn/com/hellowood/dynamicdatasource/DynamicDataSourceApplication.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22

33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
5-
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
65

7-
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
6+
@SpringBootApplication
87
public class DynamicDataSourceApplication {
98

109
public static void main(String[] args) {

‎src/main/java/cn/com/hellowood/dynamicdatasource/common/CommonConstant.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,22 @@
99
*/
1010
public class CommonConstant {
1111

12-
//Request result message
12+
/**
13+
* Request result message
14+
*/
1315
public static final String DEFAULT_SUCCESS_MESSAGE = "success";
1416
public static final String DEFAULT_FAIL_MESSAGE = "fail";
1517
public static final String NO_RESULT_MESSAGE = "no result";
1618

17-
//Operation status
19+
/**
20+
* Operation status
21+
*/
1822
public static final String SUCCESS = "SUCCESS";
1923
public static final String ERROR = "ERROR";
2024

21-
//Error or exception message
25+
/**
26+
* Error or exception message
27+
*/
2228
public static final String DB_ERROR_MESSAGE = "Database Error";
2329
public static final String SERVER_ERROR_MESSAGE = "Server Error";
2430
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package cn.com.hellowood.dynamicdatasource.common;
2+
3+
/**
4+
* The enum Data source key.
5+
*
6+
* @author HelloWood
7+
* @date 2017年08月15日 14:26
8+
* @Email hellowoodes@gmail.com
9+
*/
10+
public enum DataSourceKey {
11+
/**
12+
* Master data source key.
13+
*/
14+
master,
15+
/**
16+
* Slave alpha data source key.
17+
*/
18+
slaveAlpha,
19+
/**
20+
* Slave beta data source key.
21+
*/
22+
slaveBeta,
23+
/**
24+
* Slave gamma data source key.
25+
*/
26+
slaveGamma
27+
}

‎src/main/java/cn/com/hellowood/dynamicdatasource/common/ResponseUtil.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ public static CommonResponse generateResponse(Object data) {
7575
} else {
7676
commonResponse
7777
.setCode(ResponseCode.SUCCESS)
78-
.setMessage(CommonConstant.NO_RESULT_MESSAGE)
79-
.setData(data);
78+
.setMessage(CommonConstant.NO_RESULT_MESSAGE);
8079

8180
}
8281
return commonResponse;

‎src/main/java/cn/com/hellowood/dynamicdatasource/configuration/CustomHandlerExceptionResolver.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,17 @@ public ModelAndView resolveException(HttpServletRequest request, HttpServletResp
3232
CommonResponse commonResponse = new CommonResponse();
3333
if (handler instanceof HandlerMethod) {
3434
HandlerMethod handlerMethod = (HandlerMethod) handler;
35-
36-
if (ex instanceof ServiceException) {//Service exception,handler exception from service
35+
//Service exception,handler exception from service
36+
if (ex instanceof ServiceException) {
3737
commonResponse.setCode(ResponseCode.SUCCESS).setMessage(ex.getMessage());
3838
logger.warn(ex.getMessage());
3939
} else {
40-
41-
if (ex instanceof DataAccessException) {//DB exception
40+
//DB exception
41+
if (ex instanceof DataAccessException) {
4242
commonResponse.setCode(ResponseCode.INTERNAL_SERVER_ERROR)
4343
.setMessage(CommonConstant.DB_ERROR_MESSAGE);
44-
} else {//Others exception
44+
} else {
45+
//Others exception
4546
commonResponse.setCode(ResponseCode.INTERNAL_SERVER_ERROR)
4647
.setMessage(CommonConstant.SERVER_ERROR_MESSAGE);
4748
}

‎src/main/java/cn/com/hellowood/dynamicdatasource/configuration/DataSourceConfigurer.java

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package cn.com.hellowood.dynamicdatasource.configuration;
22

3+
import cn.com.hellowood.dynamicdatasource.common.DataSourceKey;
34
import org.mybatis.spring.SqlSessionFactoryBean;
45
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
56
import org.springframework.boot.context.properties.ConfigurationProperties;
67
import org.springframework.context.annotation.Bean;
78
import org.springframework.context.annotation.Configuration;
9+
import org.springframework.context.annotation.Primary;
10+
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
11+
import org.springframework.transaction.PlatformTransactionManager;
812

913
import javax.sql.DataSource;
1014
import java.util.HashMap;
@@ -26,19 +30,42 @@ public class DataSourceConfigurer {
2630
* @return data source
2731
*/
2832
@Bean("master")
33+
@Primary
2934
@ConfigurationProperties(prefix = "application.server.db.master")
3035
public DataSource master() {
3136
return DataSourceBuilder.create().build();
3237
}
3338

3439
/**
35-
* slave DataSource
40+
* Slave alpha data source.
3641
*
37-
* @return data source
42+
* @return the data source
43+
*/
44+
@Bean("slaveAlpha")
45+
@ConfigurationProperties(prefix = "application.server.db.slave-alpha")
46+
public DataSource slaveAlpha() {
47+
return DataSourceBuilder.create().build();
48+
}
49+
50+
/**
51+
* Slave beta data source.
52+
*
53+
* @return the data source
3854
*/
39-
@Bean("slave")
40-
@ConfigurationProperties(prefix = "application.server.db.slave")
41-
public DataSource slave() {
55+
@Bean("slaveBeta")
56+
@ConfigurationProperties(prefix = "application.server.db.slave-beta")
57+
public DataSource slaveBeta() {
58+
return DataSourceBuilder.create().build();
59+
}
60+
61+
/**
62+
* Slave gamma data source.
63+
*
64+
* @return the data source
65+
*/
66+
@Bean("slaveGamma")
67+
@ConfigurationProperties(prefix = "application.server.db.slave-gamma")
68+
public DataSource slaveGamma() {
4269
return DataSourceBuilder.create().build();
4370
}
4471

@@ -50,9 +77,11 @@ public DataSource slave() {
5077
@Bean("dynamicDataSource")
5178
public DataSource dynamicDataSource() {
5279
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
53-
Map<Object, Object> dataSourceMap = new HashMap<>(2);
54-
dataSourceMap.put("master", master());
55-
dataSourceMap.put("slave", slave());
80+
Map<Object, Object> dataSourceMap = new HashMap<>(4);
81+
dataSourceMap.put(DataSourceKey.master.name(), master());
82+
dataSourceMap.put(DataSourceKey.slaveAlpha.name(), slaveAlpha());
83+
dataSourceMap.put(DataSourceKey.slaveBeta.name(), slaveBeta());
84+
dataSourceMap.put(DataSourceKey.slaveGamma.name(), slaveGamma());
5685

5786
// Set master datasource as default
5887
dynamicRoutingDataSource.setDefaultTargetDataSource(master());
@@ -61,6 +90,10 @@ public DataSource dynamicDataSource() {
6190

6291
// To put datasource keys into DataSourceContextHolder to judge if the datasource is exist
6392
DynamicDataSourceContextHolder.dataSourceKeys.addAll(dataSourceMap.keySet());
93+
94+
// To put slave datasource keys into DataSourceContextHolder to load balance
95+
DynamicDataSourceContextHolder.slaveDataSourceKeys.addAll(dataSourceMap.keySet());
96+
DynamicDataSourceContextHolder.slaveDataSourceKeys.remove(DataSourceKey.master.name());
6497
return dynamicRoutingDataSource;
6598
}
6699

@@ -83,5 +116,15 @@ public SqlSessionFactoryBean sqlSessionFactoryBean() {
83116
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
84117
return sqlSessionFactoryBean;
85118
}
119+
120+
/**
121+
* Transaction manager platform transaction manager.
122+
*
123+
* @return the platform transaction manager
124+
*/
125+
@Bean
126+
public PlatformTransactionManager transactionManager() {
127+
return new DataSourceTransactionManager(dynamicDataSource());
128+
}
86129
}
87130

‎src/main/java/cn/com/hellowood/dynamicdatasource/configuration/DynamicDataSourceAspect.java

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
import org.aspectj.lang.annotation.After;
55
import org.aspectj.lang.annotation.Aspect;
66
import org.aspectj.lang.annotation.Before;
7+
import org.aspectj.lang.annotation.Pointcut;
78
import org.slf4j.Logger;
89
import org.slf4j.LoggerFactory;
9-
import org.springframework.core.annotation.Order;
1010
import org.springframework.stereotype.Component;
1111

1212
/**
@@ -17,39 +17,60 @@
1717
* @email hellowoodes@gmail.com
1818
*/
1919
@Aspect
20-
@Order(-1) // To ensure execute before @Transactional
2120
@Component
2221
public class DynamicDataSourceAspect {
2322
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
2423

24+
private final String[] QUERY_PREFIX = {"select"};
25+
26+
/**
27+
* Dao aspect.
28+
*/
29+
@Pointcut("execution( * cn.com.hellowood.dynamicdatasource.mapper.*.*(..))")
30+
public void daoAspect() {
31+
}
32+
2533
/**
2634
* Switch DataSource
2735
*
28-
* @param point
29-
* @param targetDataSource
36+
* @param point the point
3037
*/
31-
@Before("@annotation(targetDataSource))")
32-
public void switchDataSource(JoinPoint point, TargetDataSource targetDataSource) {
33-
if (!DynamicDataSourceContextHolder.containDataSourceKey(targetDataSource.value())) {
34-
logger.error("DataSource [{}] doesn't exist, use default DataSource [{}]", targetDataSource.value());
35-
} else {
36-
DynamicDataSourceContextHolder.setDataSourceKey(targetDataSource.value());
37-
logger.info("Switch DataSource to [{}] in Method [{}]",
38+
@Before("daoAspect()")
39+
public void switchDataSource(JoinPoint point) {
40+
Boolean isQueryMethod = isQueryMethod(point.getSignature().getName());
41+
if (isQueryMethod) {
42+
DynamicDataSourceContextHolder.useSlaveDataSource();
43+
logger.debug("Switch DataSource to [{}] in Method [{}]",
3844
DynamicDataSourceContextHolder.getDataSourceKey(), point.getSignature());
3945
}
4046
}
4147

4248
/**
4349
* Restore DataSource
4450
*
45-
* @param point
46-
* @param targetDataSource
51+
* @param point the point
4752
*/
48-
@After("@annotation(targetDataSource))")
49-
public void restoreDataSource(JoinPoint point, TargetDataSourcetargetDataSource) {
53+
@After("daoAspect())")
54+
public void restoreDataSource(JoinPoint point) {
5055
DynamicDataSourceContextHolder.clearDataSourceKey();
51-
logger.info("Restore DataSource to [{}] in Method [{}]",
56+
logger.debug("Restore DataSource to [{}] in Method [{}]",
5257
DynamicDataSourceContextHolder.getDataSourceKey(), point.getSignature());
5358
}
5459

60+
61+
/**
62+
* Judge if method start with query prefix
63+
*
64+
* @param methodName
65+
* @return
66+
*/
67+
private Boolean isQueryMethod(String methodName) {
68+
for (String prefix : QUERY_PREFIX) {
69+
if (methodName.startsWith(prefix)) {
70+
return true;
71+
}
72+
}
73+
return false;
74+
}
75+
5576
}

‎src/main/java/cn/com/hellowood/dynamicdatasource/configuration/DynamicDataSourceContextHolder.java

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
package cn.com.hellowood.dynamicdatasource.configuration;
22

33

4+
import cn.com.hellowood.dynamicdatasource.common.DataSourceKey;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
48
import java.util.ArrayList;
59
import java.util.List;
10+
import java.util.concurrent.locks.Lock;
11+
import java.util.concurrent.locks.ReentrantLock;
612

713
/**
814
* Multiple DataSource Context Holder
@@ -13,30 +19,61 @@
1319
*/
1420
public class DynamicDataSourceContextHolder {
1521

16-
// private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
22+
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
23+
24+
private static Lock lock = new ReentrantLock();
25+
26+
private static int counter = 0;
1727

1828
/**
1929
* Maintain variable for every thread, to avoid effect other thread
2030
*/
21-
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
22-
@Override
23-
protected String initialValue() {
24-
return "master";
25-
}
26-
};
31+
private static final ThreadLocal<String> CONTEXT_HOLDER = ThreadLocal.withInitial(DataSourceKey.master::name);
32+
2733

2834
/**
2935
* All DataSource List
3036
*/
3137
public static List<Object> dataSourceKeys = new ArrayList<>();
3238

39+
/**
40+
* The constant slaveDataSourceKeys.
41+
*/
42+
public static List<Object> slaveDataSourceKeys = new ArrayList<>();
43+
3344
/**
3445
* To switch DataSource
3546
*
3647
* @param key the key
3748
*/
3849
public static void setDataSourceKey(String key) {
39-
contextHolder.set(key);
50+
CONTEXT_HOLDER.set(key);
51+
}
52+
53+
/**
54+
* Use master data source.
55+
*/
56+
public static void useMasterDataSource() {
57+
CONTEXT_HOLDER.set(DataSourceKey.master.name());
58+
}
59+
60+
/**
61+
* Use slave data source.
62+
*/
63+
public static void useSlaveDataSource() {
64+
lock.lock();
65+
66+
try {
67+
int datasourceKeyIndex = counter % slaveDataSourceKeys.size();
68+
CONTEXT_HOLDER.set(String.valueOf(slaveDataSourceKeys.get(datasourceKeyIndex)));
69+
counter++;
70+
} catch (Exception e) {
71+
logger.error("Switch slave datasource failed, error message is {}", e.getMessage());
72+
useMasterDataSource();
73+
e.printStackTrace();
74+
} finally {
75+
lock.unlock();
76+
}
4077
}
4178

4279
/**
@@ -45,14 +82,14 @@ public static void setDataSourceKey(String key) {
4582
* @return data source key
4683
*/
4784
public static String getDataSourceKey() {
48-
return contextHolder.get();
85+
return CONTEXT_HOLDER.get();
4986
}
5087

5188
/**
5289
* To set DataSource as default
5390
*/
5491
public static void clearDataSourceKey() {
55-
contextHolder.remove();
92+
CONTEXT_HOLDER.remove();
5693
}
5794

5895
/**

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /