目前最新版本的ShardingJdbc中,没有支持基于Nacos作为配置数据源的功能,经过阅读源码,发现ShardingJdbc底层是基于SPI机制来扫描的,所以决定对ShardingJdbc进行二次开发,使其可以支持Nacos的动态配置功能。
从shardingsphere-jdbc-core的 jar 包中可以发现,有些类似于SPI机制。于是开始推测,这个地方是不是可以做二次开发。
接着,根据这份SPI文件内部记录的类名,可以深入进行观察,发现它们都存在相同的接口。在接口中定义了accept
和getContent
函数,这两个函数从实现类的逻辑上看,感觉是在根据 url
的格式去判断用哪个 URLProvider
读取配置。
为了验证这个逻辑是否正确,在ClasspathDriverURLProvider的accept
处加入断点进行观察。
这里会发现,在org.apache.shardingsphere.driver.jdbc.core.driver.ShardingSphereDriverURLManager中有一个do-while
循环,它会将spi
文件中的所有类都进行一次校验,如果accept
返回成功,那么就会使用匹配的URLProvider对象去进行配置的进一步读取。代码如下图所示:
所以基本上,我们看到了这里,基本上可以想到设计思路了,自定义一个扩展类,也是采用SPI的思路去实现,在新定义的扩展类中实现对nacos的相关配置读取,然后在getContent函数中返回出去。接下来进行二次开发设计。
基于ShardingSphereDriverURLProvider接口实现SPI扩展类
- 这里需要用到关于shardingjdbc和nacos的依赖,相关依赖配置如下:
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</dependency>
- 在golive-framework-datasource-starter模块中,编写一个基于Nacos配置的类,代码内容如下:
package org.golive.framework.datasource.starter.config;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import org.apache.commons.lang3.StringUtils;
import org.apache.shardingsphere.driver.jdbc.core.driver.ShardingSphereDriverURLProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.module.Configuration;
import java.util.Properties;
public class NacosDriverURLProvider implements ShardingSphereDriverURLProvider {
private static Logger logger = LoggerFactory.getLogger(NacosDriverURLProvider.class);
private static final String NACOS_TYPE = "nacos:";
private static final String GROUP = "DEFAULT_GROUP";
@Override
public boolean accept(String url) {
return StringUtils.isNotBlank(url) && url.contains(NACOS_TYPE);
}
/**
* 从url中获取到nacos的连接配置信息
*/
@Override
public byte[] getContent(final String url) {
if (StringUtils.isEmpty(url)) {
return null;
}
// 得到例如:golive.nacos.com:8848:golive-user-shardingjdbc.yaml?username=nacos&&password=nacos&&namespace=golive-test 格式的url
String nacosUrl = url.substring(url.lastIndexOf(NACOS_TYPE) + NACOS_TYPE.length());
/**
* 得到三个字符串,分别是:
* golive.nacos.com
* 8848
* golive-user-shardingjdbc.yaml
*/
String nacosStr[] = nacosUrl.split(":");
String nacosFileStr = nacosStr[2];
/**
* 得到两个字符串
* golive-user-shardingjdbc.yaml
* username=nacos&&password=nacos&&namespace=golive-test
*/
String nacosFileProp[] = nacosFileStr.split("\\?");
String dataId = nacosFileProp[0];
String acceptProp[] = nacosFileProp[1].split("&&");
// 从这里获取到
Properties properties = new Properties();
properties.setProperty(PropertyKeyConst.SERVER_ADDR, nacosStr[0] + ":" + nacosStr[1]);
for (String propertyName : acceptProp) {
String[] propertyItem = propertyName.split("=");
String key = propertyItem[0];
String value = propertyItem[1];
if ("username".equals(key)) {
properties.setProperty(PropertyKeyConst.USERNAME, value);
} else if ("password".equals(key)) {
properties.setProperty(PropertyKeyConst.PASSWORD, value);
} else if ("namespace".equals(key)) {
properties.setProperty(PropertyKeyConst.NAMESPACE, value);
}
}
ConfigService configService = null;
try {
configService = NacosFactory.createConfigService(properties);
String content = configService.getConfig(dataId, GROUP, 6000);
logger.info(content);
return content.getBytes();
} catch (NacosException e) {
throw new RuntimeException(e);
}
}
}
- 遵循shardingjdbc的spi机制,创建/META-INF/services/org.apache.shardingsphere.driver.jdbc.core.driver.ShardingSphereDriverURLProvider文件,然后在里面写上我们上边的这个NacosDriverURLProvider类的全路径地址。
org.golive.framework.datasource.starter.config.NacosDriverURLProvider
- 编写完上述内容后,整个模块基本如下图所示:
最后,修改在nacos的golive-user-provider.yaml中配置的shardingjdbc配置url参数为:
jdbc:shardingsphere:nacos:ip地址:8848:golive-user-shardingjdbc.yaml?username=nacos&&password=nacos&&namespace=golive-test
放上golive-user-shardingjdbc.yaml配置文件内容:
dataSources:
user_master: ##新表,重建的分表
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://ip地址:8808/qiyu_live_user?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: root
user_slave0: ##新表,重建的分表
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://ip地址:8809/qiyu_live_user?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: root
rules:
- !READWRITE_SPLITTING
dataSources:
user_ds:
staticStrategy:
writeDataSourceName: user_master
readDataSourceNames:
- user_slave0
- !SINGLE
defaultDataSource: user_ds # 不分表分分库的默认数据源
- !SHARDING
tables:
t_user:
actualDataNodes: user_ds.t_user_${(0..99).collect(){it.toString().padLeft(2,'0')}}
tableStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: t_user-inline
t_user_tag:
actualDataNodes: user_ds.t_user_tag_${(0..99).collect(){it.toString().padLeft(2,'0')}}
tableStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: t_user_tag-inline
shardingAlgorithms:
t_user-inline: # 路由规则 : t_user分表
type: INLINE
props:
algorithm-expression: t_user_${(user_id % 100).toString().padLeft(2,'0')}
t_user_tag-inline: # 路由规则 : t_user_tag分表
type: INLINE
props:
algorithm-expression: t_user_tag_${(user_id % 100).toString().padLeft(2,'0')}
props:
sql-show: true