制作一个Http动态数据源

2022-12-08 13:43:50 浏览数 (1)

前一段时间姜同学在做一些传统的企业信息化建设,也就是搭建一个新的平台去整合企业下诸如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就可以获得结果了。

鉴权

动态数据源服务部署方式采用网络的物理隔离不对外提供端口,认证鉴权由网关统一来做。

0 人点赞