对于大数据场景,计算的业务逻辑基本都在数据处理过程中完成,最后往往会持久化下来计算的结果,存储在mysql,es,hbase等适合提供查询的地方。而对于在不同数据库上的数据,需要开发接口,管理接口也是一个不小的工作量。Dataway的出现,正好解决了这个场景下的接口开发,管理等问题。只需要通过SQL,Dataql等配置就能完成一个接口的上线,大大提高了开发效率,以及管理成本。对于数据中台建设中,Dataway可以使用在统一服务层,对接各种数据源,管理数据出口。

一、安装

引入依赖

Dataway 是 Hasor 生态中的一员,使用 Dataway 第一步需要通过 hasor-spring 打通两个生态。

<!-- 引入依赖 -->
<dependency>
    <groupId>net.hasor</groupId>
    <artifactId>hasor-spring</artifactId>
    <version>4.2.2</version>
    <!-- 查看最新版本:https://mvnrepository.com/artifact/net.hasor/hasor-spring -->
</dependency>
<dependency>
    <groupId>net.hasor</groupId>
    <artifactId>hasor-dataway</artifactId>
    <version>4.2.2</version>
    <!-- 查看最新版本:https://mvnrepository.com/artifact/net.hasor/hasor-dataway -->
</dependency>

启用 Hasor

import net.hasor.spring.boot.EnableHasor;
import net.hasor.spring.boot.EnableHasorWeb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import static net.hasor.spring.boot.WorkAt.Interceptor;


@EnableHasor
@EnableHasorWeb(at =Interceptor )
@SpringBootApplication
@Configuration
public class DatawayApplication implements WebMvcConfigurer {

	public static void main(String[] args) {
		SpringApplication.run(DatawayApplication.class, args);
	}

}

启用 Dataway

然后第二步,在应用的 application.properties 配置文件中启用 Dataway

# 启用 Dataway 功能(默认不启用)
HASOR_DATAQL_DATAWAY=true
# 开启 ui 管理功能(注意生产环境必须要设置为 false,否则会造成严重的生产安全事故)
HASOR_DATAQL_DATAWAY_ADMIN=true
 
# (可选)API工作路径
HASOR_DATAQL_DATAWAY_API_URL=/api/
# (可选)ui 的工作路径,只有开启 ui 管理功能后才有效
HASOR_DATAQL_DATAWAY_UI_URL=/interface-ui/
# SQL执行器方言设置(可选,建议设置)
HASOR_DATAQL_FX_PAGE_DIALECT=mysql

初始化必要的表(例:MySQL)

CREATE TABLE interface_info (
  api_id          varchar(64)  NOT NULL COMMENT 'ID',
  api_method      varchar(12)  NOT NULL COMMENT 'HttpMethod:GET、PUT、POST',
  api_path        varchar(512) NOT NULL COMMENT '拦截路径',
  api_status      varchar(4)   NOT NULL COMMENT '状态:-1-删除, 0-草稿,1-发布,2-有变更,3-禁用',
  api_comment     varchar(255) NOT NULL COMMENT '注释',
  api_type        varchar(24)  NOT NULL COMMENT '脚本类型:SQL、DataQL',
  api_script      mediumtext   NOT NULL COMMENT '查询脚本:xxxxxxx',
  api_schema      mediumtext   NOT NULL COMMENT '接口的请求/响应数据结构',
  api_sample      mediumtext   NOT NULL COMMENT '请求/响应/请求头样本数据',
  api_option      mediumtext   NOT NULL COMMENT '扩展配置信息',
  api_create_time varchar(32)  NOT NULL COMMENT '创建时间',
  api_gmt_time    varchar(32)  NOT NULL COMMENT '修改时间',
  PRIMARY KEY (api_id),
  UNIQUE KEY uk_interface_info (api_path)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Dataway 中的API';
 
CREATE TABLE interface_release (
  pub_id           varchar(64)  NOT NULL COMMENT 'Publish ID',
  pub_api_id       varchar(64)  NOT NULL COMMENT '所属API ID',
  pub_method       varchar(12)  NOT NULL COMMENT 'HttpMethod:GET、PUT、POST',
  pub_path         varchar(512) NOT NULL COMMENT '拦截路径',
  pub_status       varchar(4)   NOT NULL COMMENT '状态:-1-删除, 0-草稿,1-发布,2-有变更,3-禁用',
  pub_comment      varchar(255) NOT NULL COMMENT '注释',
  pub_type         varchar(24)  NOT NULL COMMENT '脚本类型:SQL、DataQL',
  pub_script       mediumtext   NOT NULL COMMENT '查询脚本:xxxxxxx',
  pub_script_ori   mediumtext   NOT NULL COMMENT '原始查询脚本,仅当类型为SQL时不同',
  pub_schema       mediumtext   NOT NULL COMMENT '接口的请求/响应数据结构',
  pub_sample       mediumtext   NOT NULL COMMENT '请求/响应/请求头样本数据',
  pub_option       mediumtext   NOT NULL COMMENT '扩展配置信息',
  pub_release_time varchar(32)  NOT NULL COMMENT '发布时间(下线不更新)',
  PRIMARY KEY (pub_id),
  KEY idx_interface_release_api  (pub_api_id),
  KEY idx_interface_release_path (pub_path)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Dataway API 发布历史。'

初始化数据源

@DimModule
@Component
public class ExampleModule implements SpringModule {
    @Autowired
    private DataSource dataSource = null;
 
    public void loadModule(ApiBinder apiBinder) throws Throwable {
        // .DataSource form Spring boot into Hasor
        apiBinder.installModule(new JdbcModule(Level.Full, this.dataSource));
        // .custom DataQL
        //apiBinder.tryCast(QueryApiBinder.class).loadUdfSource(apiBinder.findClass(DimUdfSource.class));
        //apiBinder.tryCast(QueryApiBinder.class).bindFragment("sql", SqlFragment.class);
    }
}

最后一步,将 Spring 使用的数据源导入到 Hasor 环境共 Dataway 使用。

启动工程

2020-04-01 09:13:18.502 [main] INFO  n.h.core.context.TemplateAppContext - loadModule class net.hasor.dataway.config.DatawayModule
2020-04-01 09:13:18.502 [main] INFO  n.hasor.dataway.config.DatawayModule - dataway api workAt /api/
2020-04-01 09:13:18.502 [main] INFO  n.h.c.e.AbstractEnvironment - var -> HASOR_DATAQL_DATAWAY_API_URL = /api/.
2020-04-01 09:13:18.515 [main] INFO  n.hasor.dataway.config.DatawayModule - dataway admin workAt /interface-ui/

- dataway api workAt /api/ 表示 API 的工作路径。
- dataway admin workAt /interface-ui/ 表示 管理配置界面的地址。

此时访问:http://<yourIP>:<yourProt>/interface-ui/ 就可以看到配置页面了。

高可用

多节点部署,通过nginx等做负载均衡

二、操作界面

新增

点击页面顶部的 New 就可以进入新增接口页面。

spring datasource如何支持三方驱动_sql

在新增页面中,最主要的是要设置API的请求地址和方法。在保存 API 的时候 dataway 会对 Api 地址进行校验。

合法的API地址中只能包含下列字符: ! $ ' ( ) * + , - . / : ; = @ _ ~ [0-9] [a-z] [A-Z]

编辑器中开放了 POSTPUTGETDELETE 四个可选的 Http Method。

在保存时如果遇到错误会有类似下面这样的提示(下列是,API地址冲突的错误提示)

spring datasource如何支持三方驱动_大数据_02

提示

请注意 API 创建就不允许修改请求地址和方法,可以删除重建API来达到更名目的。

spring datasource如何支持三方驱动_API_03

新增接口时会展示一段 HelloWord 用例,这个用例中包含了请求一个接口传入一个 message 参数,然后利用 DataQL 查询获取这个参数并将其返回。

开发者可以在新建接口时直接点击工具条中的 Execute 图标执行得到运行结果。

执行

在任意的编辑器界面中(新增模式 or 编辑模式),都可以直接在编辑区编写 DataQL 查询并通过右上角的 Execute 按钮执行。

spring datasource如何支持三方驱动_sql_04

发布

当接口开发配置完成,需要将其发布以供使用。一个接口的发布上线要经历三个过程,具体如下:

spring datasource如何支持三方驱动_spring_05

Execute

调试当前编辑器中的 DataQL 查询。

Smork Test

冒烟测试。和 Execute 不同,Smork 同样是执行 DataQL 查询并要求查询正常执行完毕。但是区别 Execute 的是 Smork 不会使用编辑器中的 DataQL 查询语句,而是到数据库中获取对应的查询语句。

因为接口的发布也是将数据中的 DataQL 查询语句进行发布。

Publish

当冒烟测试通过之后就可以点击发布按钮把接口发布上线了。每次发布 Dataway 都会在 interface_release 表中新增一条记录。

删除/下线

spring datasource如何支持三方驱动_大数据_06

已经发布上线的接口只能执行接口下线操作。

spring datasource如何支持三方驱动_spring_07

已经下线的接口或者正在编辑中的接口可以执行删除操作,删除操作会物理删除 interface_info 表中对应的记录。

但是删除操作并不会删除曾经的发布历史,这就留给我们了一条可以找回被删除的接口曾经发布过的历史记录。只不过这一步只能进入数据库中自行搜索。

发布历史

每个 Dataway 上的接口在发布时都会 在 interface_release 表中生成一条记录。发布历史看的就是这里的记录。

spring datasource如何支持三方驱动_spring_08

在历史记录列表的右侧 icon 按可以恢复历史记录的内容到编辑器中。

三、ES支持

为了方便对ES的访问,增加了Dataway对Es的Sql支持;

引入依赖

<dependency>
            <groupId>org.es</groupId>
            <artifactId>sql2dsl-toolkit</artifactId>
            <version>5.4.0</version>
</dependency>

由于我们的es5.4,版本比较低,所以引入的sql2dsl-toolkit

SQL语法参见:
https://github.com/gitchennan/elasticsearch-query-toolkit/wiki/elasticsearch-query-toolkit-help-doc

添加ES属性到配置文件 application.properties

spring.data.elasticsearch.cluster-nodes=host1:port1,host2:port2
spring.data.elasticsearch.cluster-name=xxxx

增加ESSQL执行器

执行器是 DataQL 的一个 FragmentProcess 扩展

import net.hasor.core.DimModule;
import net.hasor.dataql.DimFragment;
import net.hasor.dataql.FragmentProcess;
import net.hasor.dataql.Hints;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.search.SearchHit;
import org.es.sql.bean.ElasticSqlParseResult;
import org.es.sql.parser.ElasticSql2DslParser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;

import static java.util.Arrays.stream;

@DimModule
@Component
@DimFragment("esV2")
public class ESQueryV2 implements FragmentProcess {

    @Autowired
    private Client esClient;


    @Override
    public Object runFragment(Hints hint, Map<String, Object> params, String fragmentString) throws Throwable {

//        System.out.println("参数:"+params);
        /**
         * sql 语法见 :https://github.com/gitchennan/elasticsearch-query-toolkit/wiki/elasticsearch-query-toolkit-help-doc
         */

        //获取 fragmentString 中#{xx} 字符替换
        String runsql = fragmentString;
        for(String key:params.keySet()){
            runsql = runsql.replace("#{"+key+"}",params.get(key).toString());
        }
        System.out.println(runsql);

        ElasticSql2DslParser sql2DslParser = new ElasticSql2DslParser(); //解析SQL
        ElasticSqlParseResult parseResult = sql2DslParser.parse(runsql); //生成DSL(可用于rest api调用)
        String dsl = parseResult.toDsl();
        System.out.println(dsl);


        //Client esClient = init();
        //toRequest方法接收一个clinet对象参数
        SearchRequestBuilder searchReq = parseResult.toRequest(esClient); //执行查询
        SearchResponse response = searchReq.execute().actionGet();
        SearchHit[] searchHits  = response.getHits().getHits();

        return stream(searchHits).map(x -> x.getSource()).toArray();
    }
}

新建HasorModule配置

import net.hasor.core.DimModule;
import net.hasor.dataql.QueryApiBinder;
import net.hasor.dataql.QueryModule;
import net.hasor.db.JdbcModule;
import net.hasor.db.Level;
import net.hasor.spring.SpringModule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

//import net.hasor.dataql.fx.db.LookupDataSourceListener;


@DimModule
@Component
public class DatawayModule implements SpringModule,QueryModule {

    @Autowired
    private DataSource dataSource = null;


    @Override
    public void loadModule(QueryApiBinder apiBinder) throws Throwable {
        apiBinder.installModule(new JdbcModule(Level.Full, this.dataSource));
        apiBinder.loadFragment(ESQueryV2.class,springTypeSupplier(apiBinder));  //集成sql2dsl-toolkit

    }
}

重新启动服务即可

效果

spring datasource如何支持三方驱动_大数据_09

四、总结

数据部门使用Dataway来作为数据统一服务是比较快捷高效的选择;但是基于Dataway还有很多细节需要完善,比如多种流行的数据源的支持,接口权限的控制等,都需要实现相应的接口。

引用:

Dataway官方文档: https://www.hasor.net/doc/

ES查询SQL语法参见: https://github.com/gitchennan/elasticsearch-query-toolkit/wiki/elasticsearch-query-toolkit-help-doc