# DBAPI 企业版使用指南

# 介绍

  • 狭义上说,DBAPI 是一个面向数仓开发人员的低代码工具,只需在页面上编写 sql,并配置好参数,就可以自动生成 http 接口。它可以帮助程序员快速的开发后端数据接口,尤其适用于 BI 报表、数据可视化大屏的后端接口开发。
  • 广义上说,DBAPI 是整个企业数据接口的管理中心,是企业对外提供数据服务的管理平台。它提供了数据接口的动态创建发布功能,对接口的统一管理,并提供了对客户端的管理能力,可以监控客户端对接口的调用、控制客户端对接口的权限。

# 特点

  • 开箱即用,不需要编程,单机模式不需要依赖其他软件(只需要 java 运行环境)
  • 支持单机模式、集群模式,支持windows Linux mac
  • 支持动态创建、修改 API;动态创建、修改数据源。热部署全程无感。
  • 支持 API 级别的访问权限控制,支持 IP 白名单、黑名单控制
  • 支持所有类型数据库(JDBC 协议),包括 mysql/sqlserver/postgreSql/hive/oracle 等等
  • 支持动态 sql,类似 mybatis 的动态 sql,支持 sql 编辑、运行、调试
  • 丰富的插件扩展,支持缓存、数据转换、失败告警
  • 支持 API 配置导入导出,方便测试环境到生产环境的 API 迁移
  • 支持一个接口内多条 SQL 执行(例如分页功能),支持事务开启关闭
  • 支持复杂嵌套 JSON 传参
  • 支持编排API
  • 支持接口调用记录查询,接口访问信息统计

# 基本概念

  • 执行器

执行器有多种,包括 SQL 执行器、elasticsearch 执行器、HTTP 执行器。API 内的业务执行都抽象成执行器来执行

SQL 执行器就是用来在数据库执行 sql,将结果返回

elasticsearch 执行器是用来在 elasticsearch 执行 DSL 语言,将执行结果返回

HTTP 执行器相当于网关接口代理,可以使用 http 客户端转发访问 http 协议接口,将执行结果返回

个人版目前只支持 SQL 执行器

  • API 分组

分组可以对 API 进行归类,比如业务相同的 API 可以归入同一个分组,便于管理

  • 客户端

DBAPI 是一个接口平台,客户端就是调用此平台上 API 的调用方,比如 python 程序、java 程序都可以调用 API,python 程序、java 程序都是客户端。

客户端的唯一标识是 clientId,客户端拥有 clientId 和 密钥(secret),管理员可以给客户端分配私有 API 的访问权限

客户端由管理员创建

  • 开发者

DBAPI 平台可以新建多个开发者账号,管理员可以对开发者账号授权允许访问哪些 API 分组,开发者登录自己的账号后,可以看到自己的分组,并且可以操作分组内的 API,包括新建、编辑、删除、上线、下线

# 使用操作入门

# 登录

  • 使用默认的账号密码 admin/admin登录

# 创建数据源

  • 点击数据源菜单,点击创建数据源,进入创建数据源页面

  • 选择jdbc类型,填写数据库账号地址并保存

  • 保存后回到了数据源页面,可以看到多了一条数据源,可以对其编辑删除

理论上支持所有支持 JDBC 协议的数据库

创建数据源的时候如果数据库选择其他类型,需要用户手动填写 JDBC 驱动 class 类,同时将相应的 JDBC 驱动 jar 包放入 DBAPI 的extlib目录或者lib目录下并重启生效(如果是集群模式,每个节点都需要拷贝 jar 包并重启集群)

推荐放到extlib目录,这样便于统一管理用户自己添加的 jar 包

DBAPI 的lib目录已经自带 mysql/sqlserver/postgreSql/clickhouse/的驱动包,如果版本不匹配请手动删除lib目录下的相应驱动 jar 包,并添加自己的驱动包

# 创建 API

# 创建 API 分组

  • 点击 API 菜单,点击左侧创建 API 分组按钮

  • 在弹窗中填写分组名称,并勾选刚才建好的数据源,并保存

    表示允许该分组使用该数据源

    因为不同的分组的 API 对应了企业内不同的业务模块,不同的业务模块只允许使用相应的业务相关的数据源

    如果需要修改分组的数据源权限,可以在系统设置-分组管理菜单下设置

  • 保存后发现左侧多了新的分组,点击分组上的更多按钮,可以编辑、删除分组

# 创建 API

  • 在分组上点击创建 API 按钮,进入创建 API 页面

  • 点击基本信息,填写 API 基础信息

访问权限,开放接口可以直接访问,私有接口需要客户端申请 token 才能访问

Content-Type,如果是application/x-www-form-urlencoded类型的 API,需要配置参数,如果是application/json类型的 API,需要填写 json 参数实例。

  • 点击执行器,填写执行器信息 执行器类型选择 SQL 执行器

填写要执行的 SQL,类似 mybatis 的动态 sql 语法,不需要写最外层的<select> <update> 标签,参数名用 #{} ${}表示,可以参考mybatis 文档 (opens new window)

事务,默认关闭事务,如果是 insert/update/delete 语句,建议开启事务,开启事务后如果 sql 执行失败事务会回滚。如果 API 内有多条 sql,开启事务后多条 sql 是放在一个事务内执行的

如果是 HIVE 等不支持事务的数据库,请不要开启事务,否则会报错

数据转换,如果需要数据转换,就填写数据转换插件的 java 类名,不填写表示不转换。插件如果需要传参数就填写参数。

点击添加按钮可以新增 sql,一个 API 内可以执行多条 sql,最后的多个结果封装后一起返回,比如分页查询,一个接口内既要查询详情也要查询总条数。 一个 sql 编写窗口内只能写一条 sql

如果一个执行器内包含多条 sql,那么每条 sql 会对应一个数据转换插件配置,数据转换插件永远是针对单条 sql 查询结果进行转换

  • 点击窗口最大化按钮,进入 sql 调试界面

  • 点击运行 sql,可以执行 sql,如果 sql 中有参数需要设置参数

  • 点击全局插件,填写全局插件信息

缓存,如果需要数据缓存,就填写缓存插件的 java 类名,不填写表示不开启缓存。插件如果需要传参数就填写参数。

告警,如果 API 执行失败需要告警的话,就填写告警插件的 java 类名,不填写表示不开启失败告警。插件如果需要传参数就填写参数。

全局数据转换,如果需要数据转换,就填写数据转换插件的 java 类名,不填写表示不转换。插件如果需要传参数就填写参数。

参数处理,如果需要参数处理,就填写参数处理插件的java类名,不填写表示不处理。插件如果需要传参数就填写参数。

数据转换(脚本),如果需要数据转换,就填写 js 脚本进行返回数据的格式转换,不填写表示不转换。js 脚本中可以直接使用内置变量data,js 脚本最后一行必须是一个变量(不需要return关键字),最后一行就是格式转换后的数据。 例如您想对 API 返回数据去除msg success字段,只保留data字段的数据,那么脚本就可以填写data.data

  • 点击保存即可创建 API,点击菜单 API,回到 API 列表页面

# API 发布

  • 点击 API 上的更多按钮,展开了上线按钮,点击上线按钮发布 API

# API 请求测试

  • 点击 API 上的更多按钮,展开了请求测试按钮,点击请求测试按钮进入请求测试页面

  • 点击发送请求按钮,可以发起请求,如果有参数需要填写参数

# API 参数校验

DBAPI 支持对参数进行校验

  • 一个 API 可以创建多条参数校验规则,每条规则需要填写一个 JavaScript 脚本和校验失败的提示信息

  • 任何一条规则校验失败,API 都会访问失败

  • JavaScript 脚本可以写多行代码,最后一行代码必须是一个 bool 表达式或者 bool 变量。

  • 例如校验参数 name 的长度不能超过 10 个字符,可以写以下脚本

name.length <= 10
  • 例如校验 email 参数必须是邮箱格式,可以写以下脚本
var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
emailRegex.test(email);

# 编排API

# 什么是编排API?

  • DBAPI 可以将多个API组合成一个API,从而实现更复杂的业务场景。通过页面上编辑流程图,可以快速实现业务场景的编排
  • 比如电商业务中,有多个API:创建订单表记录A、商品信息减库存B、生成物流订单C,那么我们可以编排A-> B-> C,实现一个完整的下单API。客户端调用下单API,会自动按流程访问A、B、C

  • 同时编排支持判断节点,在判断节点编写自定义脚本,可以根据判断脚本自动流转到不同的分支执行API

  • 编排也支持将前一个API的结果作为参数传入下一个API

# 节点

节点分为:API节点、判断节点、开始节点、结束节点

# API节点

执行API,API参数值需要填写JS表达式,系统通过JS脚本动态计算参数的值

JS脚本中可以使用系统内置变量 parametersresultsparameters的值是整个编排API最外层的所有参数值;results的值是每个节点执行返回的结果,如果是API节点就是API请求返回的结果,如果是判断节点就是判断后的结果(true或者false),可以通过节点ID作为key取出某个节点的结果

# 判断节点

执行判断脚本,相当于代码中的if(xxx){...} else{...},判断节点有2个出口,分别是Y和N,Y代表判断结果为true,N代表判断结果为false。判断结果为true时执行Y分支,判断结果为false时执行N分支。

需要填写JS判断脚本,必须是bool表达式,系统通过JS脚本动态计算判断的值

JS脚本中可以使用系统内置变量 parametersresultsparameters的值是整个编排API最外层的所有参数值;results的值是每个节点执行返回的结果,如果是API节点就是API请求返回的结果,如果是判断节点就是判断后的结果(true或者false),可以通过节点ID作为key取出某个节点的结果

注意判断节点的两个分支不能闭合

# 开始节点

开始执行编排,一个流程只有一个开始节点

# 结束节点

结束编排,一般只有一个结束节点,如果流程中有判断节点,可以有多个结束节点,每个判断分支对应一个结束节点

需要填写返回数据JS脚本,系统通过JS脚本动态计算的值作为整个API最后返回的值,如果不填写,最后整个编排API不返回数据。

JS脚本中可以使用系统内置变量 parametersresultsparameters的值是整个编排API最外层的所有参数值;results的值是每个节点执行返回的结果,如果是API节点就是API请求返回的结果,如果是判断节点就是判断后的结果(true或者false),可以通过节点ID作为key取出某个节点的结果

# 案例实操

业务场景:插入数据之前判断数据是否已存在,如果不存在就插入,存在就更新

  • 先准备三个API
  1. 根据name查询已存在的学生数量


  1. 新增学生,根据name和age参数插入学生信息


  1. 更新学生,根据name修改学生的年龄信息


  • 接下来开始编排
  • 点击分组上的创建编排API按钮,进入编排页面

  • 左侧定义API的基本信息,注意定义两个参数nameage

  • 点击开始节点,在页面会创建一个开始节点,可以拖动到画布合适位置

  • 点击API节点,在页面会创建一个API节点,鼠标悬浮在API节点会显示一个编辑按钮,点击按钮进入编辑界面

  • 选择API为查询学生数量,此时参数定义会自动加载出来,开始填写参数

参数值请填写JS脚本,系统通过JS脚本动态计算参数的值。

JS脚本中可以使用系统内置变量 parametersresultsparameters的值是整个编排API最外层的所有参数值;results的值是每个节点执行返回的结果,如果是API节点就是API请求返回的结果,如果是判断节点就是判断后的结果(true或者false),可以通过节点ID作为key取出某个节点的结果

这里填写name参数为parameters.name

填写节点ID,注意必须是英文字母和数字组成且以英文字母开头,如果画布中有多个节点,注意节点ID不能重复,要唯一

注意这个ID可以在后面的节点中使用,例如results.[id]可以提取此节点执行结果

  • 将开始节点和API节点用连线连接起来

  • 点击判断节点,在页面会创建一个判断节点,鼠标悬浮会显示一个编辑按钮,点击按钮进入编辑界面

  • 填写节点ID,填写判断脚本

判断脚本请填写JS脚本,必须是bool表达式,系统通过JS脚本动态计算判断的值

JS脚本中可以使用系统内置变量 parametersresultsparameters的值是整个编排API最外层的所有参数值;results的值是每个节点执行返回的结果,如果是API节点就是API请求返回的结果,如果是判断节点就是判断后的结果(true或者false),可以通过节点ID作为key取出某个节点的结果

这里我们填写results.count.data[0].num == 0

因为查询学生数量API的执行结果格式为{"data":[{"num":0}],"msg":null,"success":true}results.count.data[0].num表示取出num值

  • 连线

  • 添加API节点,选择插入新的学生API,定义id为add

这里填写name参数为parameters.name, age参数为parameters.age


  • 添加API节点,选择修改学生年龄API,定义id为update

这里填写name参数为parameters.name, age参数为parameters.age


  • 将判断节点的Y端口连线到节点add,将判断节点的N端口连接到节点update

判断结果为true时执行Y分支,判断结果为false时执行N分支。

  • 添加结束节点,编辑脚本内容为results.add,将节点add连接到此节点


  • 添加结束节点,编辑脚本内容为results.update,将节点update连接到此节点



  • 保存API并上线


  • 请求测试

  • 先传入参数name=刘德华 age=50,执行成功可以看到插入了一条数据

通过日志也可以看出执行了add节点

  • 再传入参数name=刘德华 age=60,执行成功可以看到更新了一条数据


通过日志也可以看出执行了update节点

# HTTP 执行器

DBAPI 也支持代理转发其它系统的 http 协议接口

假设我们有个接口,地址是http://localhost:8524/api/student/detail ,参数是id,以此为例来实现接口代理

  • 新建 http 数据源

  • 对分组授权使用上面建立的 http 数据源


  • 在分组下新建 API

    因为原来的 API 就有 id 参数,所以,我们新建的 API 也要有 id 参数 对分组授权过的数据源,在执行器标签的数据源选择框下才会出现


  • 保存接口并上线,进入请求测试页面,发起请求,可以看到成功

# ElasticSearch 执行器

TODO

# 用户管理

  • 系统自带admin账户(默认密码admin),admin 账户是超级管理员,拥有所有菜单权限。

  • admin 账户下可以创建其他用户,创建其他用户的时候要授权该用户可以访问哪些分组的 API

  • 其他用户登录后只有部分菜单权限,并且只看得到授权过的分组

  • 点击系统设置- 用户管理菜单,进入页面点击创建用户按钮

  • 创建一个新的开发者账号,注意授权该开发者可以访问哪些分组的 API

  • 退出管理员账号,使用开发者账号登录,可以看到开发者只能看到自己被授权的分组



  • admin账户下可以修改开发者的分组权限

  • 所有账户都可以修改自己的密码


# 客户端

  • 点击客户端菜单,点击创建客户端按钮

  • 填写客户端信息,保存

    如果需要开启 response 加密,可以打开开关

创建客户端会生成clientIdsecret,系统管理员需要将clientIdsecret告知客户端(API 调用方)。

客户端使用自己的clientIdsecret访问 http://192.168.xx.xx:8520/token/generate?clientId=xxx&secret=xxx接口可以动态的获取 token,客户端使用这个 token 才能访问私有 API(前提是系统管理员已经对该客户端授权了访问此私有 API)

创建客户端必须设置 token 过期时间,以后客户端每次申请的 token 就会有相应的过期时间,在这个有效期内, 使用上一次申请的 token 去访问 API 都是有效的。否则过了这个过期时间,就要重新申请 token。

如果开启了 response 加密,那么该客户端访问 API,每次返回的数据都是加密的,需要客服端自己使用 RSA 算法解密数据

  • 点击授权按钮,对客户端进行授权 API

  • 选择需要授权的分组,保存

# Token 使用说明

  • token 申请接口: http://192.168.xx.xx:8520/token/generate?clientId=xxx&secret=xxx

  • 请求私有接口时,需要把 token 值放入 header 的Authorization字段中携带,才可以访问成功。(如果是开放接口,不需要设置 header)

  • 以 python 为例,访问 API 的代码示例如下:

import requests
headers = {"Authorization": "5ad0dcb4eb03d3b0b7e4b82ae0ba433f"}
re = requests.post("http://127.0.0.1:8520/API/userById"{"idList": [12]},headers=headers)
print(re.text)
  • 修改之前的 API 为私有接口并重新上线,然后再点击请求测试按钮

  • 此时点击发送请求按钮,发现 API 不可访问

  • 填入 clientId 和 secret,点击按钮访问接口获取 token

  • 将 token 填入 header 的 Authorization 字段,点击发送请求按钮,发现 API 访问成功

# ip 防火墙设置

开启防火墙可以对 IP 进行拦截

# 流量控制

# API 限流

  • 流量控制- API限流 菜单点击按钮创建限流规则

  • 选择相应的 API,就可以设置 API 的 QPS

# 客户端限流

  • 我们也可以对某个客户端进行限流
  • 流量控制- 客户端限流 菜单点击按钮创建限流规则


# 监控

DBAPI 只依赖元数据库(mysql/sqlite)就可以使用,但是如果您还需要使用页面上的监控功能(监控 API 调用记录、访问量、成功率等等), 就必须依赖于另一个日志数据库(需用户自行搭建),来存储 API 访问日志,推荐使用 clickhouse,当然您也可以使用其它的关系型数据库。

目前提供了 clickhouse/mysql 的日志数据库初始化脚本,在sql目录下

  • API 访问日志采集进入日志数据库有 3 种方式
  1. DBAPI 默认会将 API 访问日志写入磁盘文件logs/dbapi-access.log,用户可以自行使用datax flume等工具采集日志到日志数据库。
  2. 如果在conf/application.properties文件配置了access.log.writer=db,那么 DBAPI 会将 API 访问日志直接写入日志数据库,这种方式适用于日志量不太大的场景下。
  3. 如果在conf/application.properties文件配置了access.log.writer=kafka,那么 DBAPI 会将 API 访问日志写入 kafka, 用户需要自行从 kafka 采集日志到日志数据库,这种方式适用于日志量大的场景,可以由 kafka 去做数据缓冲。

    注意此种方式下需要在conf/application.properties文件填写 kafka 地址。

    同时 DBAPI 也自带了消费 kafka 日志并写入日志数据库的工具,请使用bin/dbapi-log-kafka2db.sh脚本。

  • 如果您不需要使用监控功能,可以不用搭建日志数据库,并配置access.log.writer=null即可。

# Dashboard

  • 点击监控菜单可以查看 API 调用记录的监控

# 查看接口调用记录

  • 点击详情标签,可以搜索 API 调用记录

# 其他功能

# 导出接口文档

  • 在 API 页面点击工具按钮,再点击导出接口文档按钮

# 插件说明

  • DBAPI 的插件分5类,分别是数据转换插件、缓存插件、告警插件、全局数据转换插件、参数处理插件
  • 您可以去插件市场 (opens new window)下载插件

# 缓存插件

  • 对执行器结果进行缓存,比如 SQL 执行器,对查询类 SQL,sql 查询结果进行缓存,这样避免频繁的查询数据库,对数据库造成压力。
  • 缓存逻辑由用户自己编写,用户可以缓存到 redis/mongodb/elasticsearch 等等。
  • 当从缓存中查询不到数据时,才去执行执行器,同时将结果缓存下来。
  • 如果是 SQL 执行器,如果一个执行器内包含多条 sql,那么缓存插件是对多条 sql 执行的结果(如果单条 sql 配置了转换插件,会先转换结果)封装成一个整体后,对整体进行缓存

# 告警插件

  • 当 API 内部报错的时候,告警插件可以将报错信息发送告警提醒,比如发邮件、发短信
  • 告警逻辑由用户自己编写

    注意系统已经自带邮件告警插件,如果要使用注意修改conf/plugin.properties中发件人参数信息

# 邮件告警插件全局参数
EMAIL_USERNAME=dbapi_test@163.com
EMAIL_PASSWORD=WGJQBFRIPUENHMUP
EMAIL_HOST=smtp.163.com

# 数据转换插件

  • 有时候 sql 无法一次性获得自己想要的数据格式,如果用代码对数据进行一些处理转换能更加方便,这时候就要用到数据转换插件。用户自己编写数据转换逻辑的代码。
  • 比如针对 sql 查询结果中的用户手机号、银行卡号进行转换脱敏。
  • 如果是 SQL 执行器,如果一个执行器内包含多条 sql,那么每条 sql 会对应一个数据转换插件配置,数据转换插件永远是针对单条 sql 查询结果进行转换

# 全局数据转换插件

  • API 返回的数据格式默认是{success:true,msg:xxx,data:xxx}
  • 有些情况下需要对 response 数据格式进行一些转换,比如前端低代码框架AMIS就要求接口返回数据必须携带status字段,这个时候就可以用全局数据转换插件对整个 API 的返回数据进行格式转换

注意数据转换插件和全局数据转换插件的区别,数据转换插件是对执行器执行结果进行格式转换(比如对 SQL 执行器执行查询 sql 得到的结果进行转换),而全局数据转换插件是对整个 API 执行结果进行格式转换

# 参数处理插件

  • 对请求参数进行用户自定义的处理
  • 比如将请求参数的值全部转换大写
  • 比如API接收加密的参数,用户自定义逻辑对参数值进行解密
  • 比如分页查询的时候,用户编写逻辑添加一个offset参数,使其值等于(pageNo-1)*pageSize

注意参数处理插件在个人版4.0.16、 企业版4.1.10版本开始支持

# 注意事项

# JDBC 数据源支持

  • 如果您要使用 Oracle 或者其他类型的数据源,请将相应的 jdbc 驱动包手动放入 DBApi 部署后的lib目录下(如果是集群部署每个节点都需要手动放入 jar 包)

# sql 编写规范

# 注意事项

  • 和 mybatis 动态 sql 语法一样,同样支持参数#{}${},可以参考mybatis 文档 (opens new window) ,不需要写最外层的<select> <update> 标签,直接写 sql 内容
  • 注意和 mybatis 一样,sql 里的小于号不要写成<,要写成 &lt;

# 权限校验流程