前一段时间姜同学在做一些传统的企业信息化建设,也就是搭建一个新的平台去整合企业下诸如OA,CRM,PLM,U8等一系列数据独立的系统,这个系统除了具有传统的BI系统将整合的数据用作上层系统的展示之外,还可以对外提供HTTP接口用来反哺其它的子系统,所以我们称它为数据中台。
考虑到一些系统是很久之前从其它厂商那里采购来了,还有一些是企业诞生之初便存在的年久失修的系统,具提失修到什么程度,我就不再过多描述,大家懂得都懂。所以为了不影响这些系统,便诞生了这篇文章的主角,Http动态数据源。
用途
当然主要的用途还是前两个,至于用作报表系统的Http的数据源就不多件了,毕竟这种应用层协议之间的交互还是存在效率问题的,至于做报表和大屏的的工具,我向大家推荐一个用过感觉还不错的。datart
理论落地
理论也非常简单,只要将我们的需求进行分层,管理层用于动态数据源的管理,查询层提供多数据源的查询能力,增加鉴权层为数据安全提供保障。
管理层
管理层建议使用关系型数据库将数据源的连接信息管理起来,但是姜同学这里因为数据源比较少以及为了快速交付考虑,采用了配置文件来管理数据源。
代码语言:javascript复制pins:
dataset:
- url: jdbc:mysql://127.0.0.1:3306/rds
user: query
pwd: 123456
use: rds
- url: jdbc:mysql://127.0.0.1:3306/oa
user: query
pwd: 123456
use: oa
- url: jdbc:sqlserver://127.0.0.1:1433;databasename=UFmeta_003
user: query
pwd: "123456"
use: u8
- url: jdbc:mysql://127.0.0.1:3306/u8
user: query
pwd: 123456
use: plm
- url: jdbc:oracle:thin:@127.0.0.1:1521:orcl
user: query
pwd: 123456
use: operation-log
代码语言:javascript复制@ConfigurationProperties(prefix = "pins")
@Component
@Data
public class DynamicDatasetProperties {
private List<DataSet> dataSet;
private Common common;
@Data
public static class DataSet{
String url;
String user;
String pwd;
String use;
}
}
java
在程序启动之后将连接生成对应的JdbcTemplate
JdbcTemplateUtils
代码语言:javascript复制@Component
@Slf4j
public class JdbcTemplateUtils {
private final DynamicDatasetProperties dynamicDatasetProperties;
/**
* 动态数据源
*/
private final Map<String, HikariDataSource> pinsDataSources = new HashMap<>();
/**
* jdbcTemplate key:数据源唯一标识 oa:jdbcTemplate
*/
public final static Map<String, JdbcTemplate> pinsJdbcTemplate;
static {
pinsJdbcTemplate = new HashMap<>();
}
public JdbcTemplateUtils(DynamicDatasetProperties dynamicDatasetProperties) {
this.dynamicDatasetProperties = dynamicDatasetProperties;
}
@PostConstruct
void init(){
dynamicDatasetProperties.getDataSet().forEach(
dataSet -> {
makeJdbcTemplate(dataSet.getUrl(), dataSet.getUser(), dataSet.getPwd(), dataSet.getUse());
}
);
}
@PreDestroy
void destroy(){
for (Map.Entry<String, HikariDataSource> entry : pinsDataSources.entrySet()) {
HikariDataSource hikariDataSource = entry.getValue();
if (null != hikariDataSource) {
hikariDataSource.close();
}
}
}
private void makeJdbcTemplate(String mysqlConnectionUrl, String userName, String password, String uniqueCode) {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setJdbcUrl(mysqlConnectionUrl);
hikariConfig.setUsername(userName);
hikariConfig.setPassword(password);
hikariConfig.setMaximumPoolSize(50); // 连接池的最大连接数,
HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
Connection connection = DataSourceUtils.getConnection(hikariDataSource);
try {
boolean closed = connection.isClosed();
if (!closed) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(hikariDataSource);
pinsDataSources.put(uniqueCode, hikariDataSource);
pinsJdbcTemplate.put(uniqueCode, jdbcTemplate);
}
} catch (SQLException e) {
log.error("Connect Tenant MySql Fail : ", e);
}
}
}
java
注意这里的连接池使用了SpringBoot自带的Hikari。
查询层
不解释什么了,很简单。
代码语言:javascript复制@Service
public class PinsQlService {
public List<Map<String, Object>> pinsDynamicQuery(String use, String pinsQl) throws NullPointerException {
return JdbcTemplateUtils.pinsJdbcTemplate.get(use).queryForList(pinsQl);
}
}
java
Http层
代码语言:javascript复制@RestController
@RequestMapping({"pins-ql"})
public class PinsQlController {
private final PinsQlService pinsQlService;
public PinsQlController(PinsQlService pinsQlService) {
this.pinsQlService = pinsQlService;
}
@PostMapping({"dynamic/{use}"})
public Result<List<Map<String, Object>>> dynamicQuery(@PathVariable("use") String use,
@RequestBody SQL sql) {
if (use.contains("op")) {
return Result.fail("高危操作请联系管理员");
}
return Result.success(this.pinsQlService.pinsDynamicQuery(use, sql.str));
}
}
java
其他系统只需要提供要查询的应用类型已经对应的SQL就可以获得结果了。
鉴权
动态数据源服务部署方式采用网络的物理隔离不对外提供端口,认证鉴权由网关统一来做。