# 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脚本中可以使用系统内置变量 parameters
和 results
,parameters
的值是整个编排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脚本中可以使用系统内置变量 parameters
和 results
,parameters
的值是整个编排API最外层的所有参数值;results
的值是每个节点执行返回的结果,如果是API节点就是API请求返回的结果,如果是判断节点就是判断后的结果(true或者false),可以通过节点ID作为key取出某个节点的结果
注意判断节点的两个分支不能闭合
# 开始节点
开始执行编排,一个流程只有一个开始节点
# 结束节点
结束编排,一般只有一个结束节点,如果流程中有判断节点,可以有多个结束节点,每个判断分支对应一个结束节点
需要填写返回数据JS脚本,系统通过JS脚本动态计算的值作为整个API最后返回的值,如果不填写,最后整个编排API不返回数据。
JS脚本中可以使用系统内置变量 parameters
和 results
,parameters
的值是整个编排API最外层的所有参数值;results
的值是每个节点执行返回的结果,如果是API节点就是API请求返回的结果,如果是判断节点就是判断后的结果(true或者false),可以通过节点ID作为key取出某个节点的结果
# 案例实操
业务场景:插入数据之前判断数据是否已存在,如果不存在就插入,存在就更新
- 先准备三个API
- 根据name查询已存在的学生数量
- 新增学生,根据name和age参数插入学生信息
- 更新学生,根据name修改学生的年龄信息
- 接下来开始编排
- 点击分组上的
创建编排API
按钮,进入编排页面
- 左侧定义API的基本信息,注意定义两个参数
name
和age
- 点击开始节点,在页面会创建一个开始节点,可以拖动到画布合适位置
- 点击API节点,在页面会创建一个API节点,鼠标悬浮在API节点会显示一个编辑按钮,点击按钮进入编辑界面
- 选择API为
查询学生数量
,此时参数定义会自动加载出来,开始填写参数
参数值请填写JS脚本,系统通过JS脚本动态计算参数的值。
JS脚本中可以使用系统内置变量
parameters
和results
,parameters
的值是整个编排API最外层的所有参数值;results
的值是每个节点执行返回的结果,如果是API节点就是API请求返回的结果,如果是判断节点就是判断后的结果(true或者false),可以通过节点ID作为key取出某个节点的结果
这里填写name参数为parameters.name
填写节点ID,注意必须是英文字母和数字组成且以英文字母开头,如果画布中有多个节点,注意节点ID不能重复,要唯一
注意这个ID可以在后面的节点中使用,例如
results.[id]
可以提取此节点执行结果
- 将开始节点和API节点用连线连接起来
- 点击判断节点,在页面会创建一个判断节点,鼠标悬浮会显示一个编辑按钮,点击按钮进入编辑界面
- 填写节点ID,填写判断脚本
判断脚本请填写JS脚本,必须是bool表达式,系统通过JS脚本动态计算判断的值
JS脚本中可以使用系统内置变量
parameters
和results
,parameters
的值是整个编排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 加密,可以打开开关
创建客户端会生成clientId
和secret
,系统管理员需要将clientId
和secret
告知客户端(API 调用方)。
客户端使用自己的clientId
和secret
访问 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": [1,2]},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 种方式
- DBAPI 默认会将 API 访问日志写入磁盘文件
logs/dbapi-access.log
,用户可以自行使用datax
flume
等工具采集日志到日志数据库。 - 如果在
conf/application.properties
文件配置了access.log.writer=db
,那么 DBAPI 会将 API 访问日志直接写入日志数据库,这种方式适用于日志量不太大的场景下。 - 如果在
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 里的小于号不要写成
<
,要写成<