Skip to content

Commit 233a112

Browse files
committed
ApiBoot DataSource Switch 使用示例
1 parent 59038f6 commit 233a112

File tree

8 files changed

+573
-0
lines changed

8 files changed

+573
-0
lines changed
Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
## ApiBoot DataSource Switch
2+
3+
顾名思义,`DataSource Switch`是用于数据源选择切换的框架,这是一款基于`Spring AOP`切面指定注解实现的,通过简单的数据源注解配置就可以完成访问时的自动切换,`DataSource Switch`切换过程中是线程安全的。
4+
5+
### 添加依赖
6+
7+
使用`DataSource Switch`很简单,在`pom.xml`配置文件内添加如下依赖:
8+
9+
```xml
10+
<!--ApiBoot DataSource Switch-->
11+
<dependency>
12+
<groupId>org.minbox.framework</groupId>
13+
<artifactId>api-boot-starter-datasource-switch</artifactId>
14+
</dependency>
15+
```
16+
`ApiBoot`所提供的依赖都不需要添加版本号,具体查看[ApiBoot版本依赖](https://github.com/hengboy/api-boot/blob/1.x/README.md#%E6%B7%BB%E5%8A%A0%E7%89%88%E6%9C%AC%E4%BE%9D%E8%B5%96)
17+
18+
### 集成数据源实现
19+
20+
目前`ApiBoot DataSource Switch`集成了`Druid``HikariCP`两种数据源实现依赖,在使用方面也有一定的差异,因为每一个数据源的内置参数不一致。
21+
22+
- `Druid`:参数配置前缀为`api.boot.datasource.druid`
23+
- `HikariCP`:参数配置前缀为`api.boot.datasource.hikari`
24+
25+
**具体使用请查看下面功能配置介绍。**
26+
27+
28+
29+
### 配置参数
30+
31+
| 参数名 | 参数默认值 | 是否必填 | 参数描述 |
32+
| --------------------------------------------------------- | ------------------------ | -------- | ------------------------------------------------------------ |
33+
| `api.boot.datasource.primary` | master || 主数据源名称 |
34+
| `api.boot.datasource.druid.{poolName}.url` ||| 数据库连接字符串 |
35+
| `api.boot.datasource.druid.{poolName}.username` ||| 用户名 |
36+
| `api.boot.datasource.druid.{poolName}.password` ||| 密码 |
37+
| `api.boot.datasource.druid.{poolName}.driver-class-name` | com.mysql.cj.jdbc.Driver || 驱动类型 |
38+
| `api.boot.datasource.druid.{poolName}.filters` | stat,wall,slf4j || Druid过滤 |
39+
| `api.boot.datasource.druid.{poolName}.max-active` | 20 || 最大连接数 |
40+
| `api.boot.datasource.druid.{poolName}.initial-size` | 1 || 初始化连接数 |
41+
| `api.boot.datasource.druid.{poolName}.max-wait` | 60000 || 最大等待市场,单位:毫秒 |
42+
| `api.boot.datasource.druid.{poolName}.validation-query` | select 1 from dual || 检查sql |
43+
| `api.boot.datasource.druid.{poolName}.test-while-idle` | true || 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 |
44+
| `api.boot.datasource.druid.{poolName}.test-on-borrow` | false || 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
45+
| `api.boot.datasource.druid.{poolName}.test-on-return` | false || 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
46+
| `api.boot.datasource.hikari.{poolName}.url` ||| 数据库连接字符串 |
47+
| `api.boot.datasource.hikari.{poolName}.username` ||| 用户名 |
48+
| `api.boot.datasource.hikari.{poolName}.password` ||| 密码 |
49+
| `api.boot.datasource.hikari.{poolName}.driver-class-name` | com.mysql.cj.jdbc.Driver || 数据库驱动类全限定名 |
50+
| `api.boot.datasource.hikari.{poolName}.property` ||| HikariCP属性配置 |
51+
52+
`HikariCP`数据源是`SpringBoot2.x`自带的,配置参数请访问[HikariCP](https://github.com/brettwooldridge/HikariCP)
53+
54+
### 单主配置
55+
56+
`ApiBoot DataSource Switch`支持单主数据源的配置,`application.yml`配置文件如下所示:
57+
58+
```yaml
59+
api:
60+
boot:
61+
datasource:
62+
# 配置使用hikari数据源
63+
hikari:
64+
# master datasource config
65+
master:
66+
url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=Asia/Shanghai
67+
username: root
68+
password: 123456
69+
```
70+
71+
### 修改主数据源名称
72+
73+
`master`为默认的主数据源的`poolName`,这里可以进行修改为其他值,不过需要对应修改`primary`参数,如下所示:
74+
75+
```yaml
76+
api:
77+
boot:
78+
datasource:
79+
# 主数据源,默认值为master
80+
primary: main
81+
# 配置使用hikari数据源
82+
hikari:
83+
# main datasource config
84+
main:
85+
url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=Asia/Shanghai
86+
username: root
87+
password: 123456
88+
```
89+
90+
在上面配置主数据源的`poolName`修改为`main`。
91+
92+
### 主从配置
93+
94+
如果你的项目内存在`单主单从`、`一主多从`的配置方式,如下所示:
95+
96+
```yaml
97+
api:
98+
boot:
99+
datasource:
100+
# 配置使用hikari数据源
101+
hikari:
102+
# master datasource config
103+
master:
104+
url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=Asia/Shanghai
105+
username: root
106+
password: 123456
107+
# 默认值为【com.mysql.cj.jdbc.Driver】
108+
#driver-class-name: com.mysql.cj.jdbc.Driver
109+
# slave 1 datasource config
110+
slave_1:
111+
url: jdbc:mysql://localhost:3306/oauth2?characterEncoding=utf8&serverTimezone=Asia/Shanghai
112+
username: root
113+
password: 123456
114+
# slave 2 datasource config
115+
slave_2:
116+
url: jdbc:mysql://localhost:3306/resources?characterEncoding=utf8&serverTimezone=Asia/Shanghai
117+
username: root
118+
password: 123456
119+
```
120+
121+
在上面是`一主多从`的配置方式,分别是`master`、`slave_1`、`slave_2`。
122+
123+
### 多类型数据库配置
124+
125+
`ApiBoot DataSource Switch`提供了一个项目内连接多个不同类型的数据库,如:`MySQL`、`Oracle`...等,如下所示:
126+
127+
```yaml
128+
api:
129+
boot:
130+
# 主数据源,默认值为master
131+
primary: mysql
132+
hikari:
133+
mysql:
134+
url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=Asia/Shanghai
135+
username: root
136+
password: 123456
137+
oracle:
138+
url: jdbc:oracle:thin:@172.16.10.25:1521:torcl
139+
username: root
140+
password: 123456
141+
driver-class-name: oracle.jdbc.driver.OracleDriver
142+
```
143+
144+
在上面配置中,`master`主数据源使用的`MySQL`驱动连接`MySQL`数据库,而`slave`从数据源则是使用的`Oracle`驱动连接的`Oracle`数据库。
145+
146+
### 动态创建数据源
147+
148+
`ApiBoot DataSource Switch`内部提供了动态创建数据源的方法,可以通过注入`ApiBootDataSourceFactoryBean`来进行添加,如下所示:
149+
150+
```java
151+
@Autowired
152+
private ApiBootDataSourceFactoryBean factoryBean;
153+
154+
public void createNewDataSource() throws Exception {
155+
// 创建Hikari数据源
156+
// 如果创建Druid数据源,使用DataSourceDruidConfig
157+
DataSourceHikariConfig config = new DataSourceHikariConfig();
158+
// 数据库连接:必填
159+
config.setUrl("jdbc:mysql://localhost:3306/resources");
160+
// 用户名:必填
161+
config.setUsername("root");
162+
// 密码:必填
163+
config.setPassword("123456");
164+
// 数据源名称:必填(用于@DataSourceSwitch注解value值使用)
165+
config.setPoolName("dynamic");
166+
167+
// 创建数据源
168+
DataSource dataSource = factoryBean.newDataSource(config);
169+
Connection connection = dataSource.getConnection();
170+
System.out.println(connection.getCatalog());
171+
connection.close();
172+
}
173+
```
174+
175+
### 自动切换
176+
177+
`ApiBoot DataSource Switch`的数据源自动切换主要归功于`Spring`的`AOP`,通过切面`@DataSourceSwitch`注解,获取注解配置的`value`值进行设置当前线程所用的数据源名称,从而通过`AbstractRoutingDataSource`进行数据源的路由切换。
178+
179+
我们沿用上面**一主多从**的配置进行代码演示,配置文件`application.yml`参考上面配置,代码示例如下:
180+
181+
#### 从数据源示例类
182+
183+
```java
184+
@Service
185+
@DataSourceSwitch("slave")
186+
public class SlaveDataSourceSampleService {
187+
/**
188+
* DataSource Instance
189+
*/
190+
@Autowired
191+
private DataSource dataSource;
192+
193+
/**
194+
* 演示输出数据源的catalog
195+
*
196+
* @throws Exception
197+
*/
198+
public void print() throws Exception {
199+
// 获取链接
200+
Connection connection = dataSource.getConnection();
201+
// 输出catalog
202+
System.out.println(this.getClass().getSimpleName() + " ->" + connection.getCatalog());
203+
// 关闭链接
204+
connection.close();
205+
}
206+
}
207+
```
208+
209+
#### 主数据源示例类
210+
211+
```java
212+
@Service
213+
@DataSourceSwitch("master")
214+
public class MasterDataSourceSampleService {
215+
/**
216+
* DataSource Instance
217+
*/
218+
@Autowired
219+
private DataSource dataSource;
220+
/**
221+
* Slave Sample Service
222+
*/
223+
@Autowired
224+
private SlaveDataSourceSampleService slaveDataSourceSampleService;
225+
226+
/**
227+
* 演示输出主数据源catalog
228+
* 调用从数据源类演示输出catalog
229+
*
230+
* @throws Exception
231+
*/
232+
public void print() throws Exception {
233+
Connection connection = dataSource.getConnection();
234+
System.out.println(this.getClass().getSimpleName() + " ->" + connection.getCatalog());
235+
connection.close();
236+
slaveDataSourceSampleService.print();
237+
}
238+
}
239+
```
240+
241+
- 在`主数据源`的示例类内,我们通过`@DataSourceSwitch("master")`注解的`value`进行定位连接`master`数据源数据库。
242+
- 同样在`从数据库`的示例类内,我们也可以通过`@DataSourceSwitch("slave")`注解的`value`进行定位连接`slave`数据源数据库。
243+
244+
#### 单元测试示例
245+
246+
在上面的测试示例中,我们使用交叉的方式进行验证`数据源路由`是否可以正确的进行切换,可以编写一个单元测试进行验证结果,如下所示:
247+
248+
```java
249+
@Autowired
250+
private MasterDataSourceSampleService masterDataSourceSampleService;
251+
@Test
252+
public void contextLoads() throws Exception {
253+
masterDataSourceSampleService.print();
254+
}
255+
```
256+
257+
运行上面测试方法,结果如下所示:
258+
259+
```sh
260+
MasterDataSourceSampleService ->test
261+
2019-04-04 10:20:45.407 INFO 7295 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Starting...
262+
2019-04-04 10:20:45.411 INFO 7295 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Start completed.
263+
SlaveDataSourceSampleService ->oauth2
264+
```
265+
266+
单次执行数据源切换没有任何的问题,`master`数据源获取`catalog`输出后,调用`slave`示例类进行输出`catalog`。
267+
268+
> `ApiBoot DataSource Switch`会在项目启动时首先初始化`master`节点`DataSource`实例,其他实例会在第一次调用时进行初始化。
269+
270+
### 压力性能测试
271+
272+
单次执行单线程操作没有问题,不代表多线程下不会出现问题,在开头说到过`ApiBoot DataSource Switch`是线程安全的,所以接下来我们来验证这一点,我们需要添加压力测试的依赖,如下所示:
273+
274+
```xml
275+
<dependency>
276+
<groupId>org.databene</groupId>
277+
<artifactId>contiperf</artifactId>
278+
<version>2.3.4</version>
279+
<scope>test</scope>
280+
</dependency>
281+
```
282+
283+
接下来把上面的单元测试代码改造下,如下所示:
284+
285+
```java
286+
// 初始化压力性能测试对象
287+
@Rule
288+
public ContiPerfRule i = new ContiPerfRule();
289+
290+
@Autowired
291+
private MasterDataSourceSampleService masterDataSourceSampleService;
292+
/**
293+
* 开启500个线程执行10000次
294+
*/
295+
@Test
296+
@PerfTest(invocations = 10000, threads = 500)
297+
public void contextLoads() throws Exception {
298+
masterDataSourceSampleService.print();
299+
}
300+
```
301+
302+
> 测试环境:
303+
>
304+
> 硬件:i7、16G、256SSD
305+
>
306+
> 系统:OS X
307+
>
308+
> 整个过程大约是10秒左右,`ApiBoot DataSource Switch`并没有发生出现切换错乱的情况。
309+
310+
### 注意事项
311+
312+
1. 在使用`ApiBoot DataSource Switch`时需要添加对应数据库的依赖
313+
2. 如果使用`Druid`连接池,不要配置使用`druid-starter`的依赖,请使用`druid`依赖。
314+
3. 配置`poolName`时不要添加特殊字符、中文、中横线等。
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>api-boot-samples</artifactId>
7+
<groupId>org.minbox.framework</groupId>
8+
<version>1.0.3.RELEASE</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>api-boot-sample-datasource-switch</artifactId>
13+
<description>ApiBoot DataSource Switch 示例</description>
14+
<dependencies>
15+
<!--SpringBoot Web-->
16+
<dependency>
17+
<groupId>org.springframework.boot</groupId>
18+
<artifactId>spring-boot-starter-web</artifactId>
19+
</dependency>
20+
<!--ApiBoot DataSource Switch-->
21+
<dependency>
22+
<groupId>org.minbox.framework</groupId>
23+
<artifactId>api-boot-starter-datasource-switch</artifactId>
24+
</dependency>
25+
<!--SpringBoot Test-->
26+
<dependency>
27+
<groupId>org.springframework.boot</groupId>
28+
<artifactId>spring-boot-starter-test</artifactId>
29+
</dependency>
30+
<!--HikariCP-->
31+
<dependency>
32+
<groupId>com.zaxxer</groupId>
33+
<artifactId>HikariCP</artifactId>
34+
<version>3.2.0</version>
35+
</dependency>
36+
<!--MySQL-->
37+
<dependency>
38+
<groupId>mysql</groupId>
39+
<artifactId>mysql-connector-java</artifactId>
40+
</dependency>
41+
<!--性能测试依赖-->
42+
<dependency>
43+
<groupId>org.databene</groupId>
44+
<artifactId>contiperf</artifactId>
45+
<version>2.3.4</version>
46+
<scope>test</scope>
47+
</dependency>
48+
</dependencies>
49+
<!--ApiBoot版本依赖-->
50+
<dependencyManagement>
51+
<dependencies>
52+
<dependency>
53+
<groupId>org.minbox.framework</groupId>
54+
<artifactId>api-boot-dependencies</artifactId>
55+
<version>1.0.3.RELEASE</version>
56+
<type>pom</type>
57+
<scope>import</scope>
58+
</dependency>
59+
</dependencies>
60+
</dependencyManagement>
61+
</project>

0 commit comments

Comments
 (0)