nebula_dart_gdbc,是访问 NebulaGraph 的 Dart 语言客户端,在 dart_gdbc 的规范下进行开发。
dart_gdbc 是一套使用 Dart 语言定义的图数据库标准数据接口,整体思路参考了 JDBC 的规范。目前已经实现了对 NebulaGraph 的支持。
在先前写 GraphDesk 的过程中,曾使用过 nebula-java(Socket)+ Spring Boot(HTTP)的方式实现了 Flutter 应用对 NebulaGraph 的访问。这无异于一辆可以高速行驶的汽车(Socket),进出家门的时候非要用人推(HTTP)的方式,这一点很难让人接受。而使用 Dart Socket 访问图数据库无疑是 Flutter 应用一个不错的选择。不过,因为 Dart 相比其他编程语言来说,目前相对小众,需求量小,并且是个偏前端的语言,纵观现有的图数据库生态,它们都不支持 Dart。
于是,dart_gdbc 与 nebula_dart_gdbc 应运而生。
随着 Dart 客户端的打通,对于拥有图数据库的朋友来说,通过 Dart 在手机上,甚至嵌入式平台上进行 NebulaGraph 的图探索,已经成为可能。而对于 Flutter 开发者来说,可以试试创建自己的图数据库,使用 nebula_dart_gdbc 直连数据库来实现自己的应用,也是一种奇妙体验。虽然,数据库的常规操作还是经过服务端做数据转发合理一点,但不影响大家尝试这种新玩法。
如果一切顺利的话,Dart 这一环节补上之后,之前我做的 GraphDesk,将会有以下变化:
意义层面的内容就说到这,接下来聊聊 nebula_dart_gdbc 的诞生历程,下文带有很强的技术色彩,非业内人士可以选择性跳过。
nebula_dart_gdbc 的大概实现思路同其他语言的客户端一致,按照了下面这个步骤:
显然,真正的开发并没有这么简单。在实际的过程中,我遇到了很多问题,也做了很多尝试,最终才有了现在的 nebula_dart_gdbc。下面来记录下开发一个 Dart 客户端,有哪些操作步骤以及相关问题的解决方案。
第一步,先下载数据接口的 thrift 文件,这是 NebulaGraph 给出的链接:https://github.com/vesoft-inc/nebula/tree/master/src/interface,包含下面这些文件:
注意:下载完之后,需要往文件添加对应的包名,如:namespace dart nebula.graph
第二步,下载完接口所需的 NebulaGraph thrift 文件之后,我们再来下载 Thrift,这是 Thrift 的官方下载地址:https://thrift.apache.org/download,这里使用的是最新版本 0.18.1(如果你阅读本文的时候,0.18.1 不是最新版本,下载最新版本即可)。
第三步,逐个文件生成 Dart 代码。使用下面的命令就行:
thrift --gen dart common.thrift
thrift --gen dart graph.thrift
thrift --gen dart meta.thrift
thrift --gen dart storage.thrift
这个过程中,我们会遇到一些问题,这里记录了它们的解决方法。
第四步,将生成的 Dart 代码,放到项目中。
至此,完成“通过 thrift 文件,生成 NebulaGraph 数据访问接口”。
这里,先插播一条广告(请耐心读下去)。
给大家介绍一家的烧烤摊。这家店的特点是,一个服务员有一个托盘,并且这个服务员只服务一桌。服务员很庄重地用托盘,端着一张空白的纸来到顾客面前,由顾客们轮流写上自己喜欢的牛肉串、烤面筋、烤花菜…,再选择调料黑胡椒、孜然…等信息。将纸放到托盘中后,由服务员送到烧烤师傅手中,烧烤师傅按照纸上的要求,烤好后将菜品装到碟子里,放在原来的托盘上。由服务员再端到顾客面前,顾客们分别拿走自己点的菜品。
这家烧烤摊名字叫:Thrift。
这可能是一段迄今为止对 Thrift 最奇怪的比喻。Thrift 是一个 RPC 框架,可将接口的参数转换成二进制数据。下面,介绍 Thrift 两个重要的接口:TProtocol 跟 TTransport。以刚刚烧烤摊为例子,大致可以有以下对应关系(可能有些许不恰当):
TProtocol
:可以有很多不同的顾客,负责写内容TTransport
:托盘 + 服务员,负责传递内容好了,广告结束。相信机智的你看到这里,对 Thrift 有了一定的了解。下面继续我们的 Dart 之旅:
首先要参考 nebula-java 源码,对 GraphServiceClient 进行调用。
这个环节我遇到了两个问题:
搞定上面两个问题后,我们可以通过 Dart Socket 连接,实现对 NebulaGraph 的访问。
这个步骤怎么说呢?就好像又来了一桌,现在两桌,一桌顾客和服务员都只会闽南语,一桌顾客和服务员都只会普通话。而烧烤师傅啥也不管,只看菜单。新来的这桌只有一个要求,跟隔壁桌点的要一模一样…在两桌语言不通的情况下,没有意外的话,这里要出意外。
好的,我们现在遇到了几个问题:
至此,建立如图所示的结构:
基本思路确定之后,开始了漫长的翻译(Java 代码变 Dart 代码)、调试,再翻译、再调试…,在 L2 跟 L3 中反复横跳。每个环节的数据表现形式都是纯数字数组,数组长度、下标对应的值错一个都可能功亏一篑。这里需要细心跟耐心。特别是数字操作上涉及到反码、补码、移位的操作,不同的参数有不同的字节长度,按位读写。以下,再列几个 L2、L3 反复横跳的过程中几个让人比较头大的问题:
到这里,L3 已经走通,C1 跟 C2 访问 S2,返回的数据一致,拿下模拟考!继续迎接挑战:
List
拼接成一段合并发送,在 S2 的表现完全一致,这个问题就很隐蔽,过了相当长一段一行代码不写还不得不保持干活状态的时间。到最后改成在 Dart Socket 的一次传输中,强行将两个 List
剥离,使用两个 Stream 进行传输。至此,L4 成功,连通性问题解决,说实话,最后这点带有一丝运气成分。在这个环节,实现思路中断的情况比较少碰到,整体算流畅,比较花时间的是结果集的处理。
nebula_dart_gdbc,并没有沿用 NebulaGraph 其他客户端的写法。而是整体上参考了 JDBC 的思路,将数据库连接、执行、结果集等进行了封装,形成 dart_gdbc。在这套接口标准下,对 nebula_dart_gdbc 进行实现。
熟悉 JDBC 的朋友,对以下流程就不陌生了,dart_gdbc 的调用流程如下:
以下是 nebula_dart_gdbc 在 dart_gdbc 的规范下的代码示例:
import 'package:nebula_dart_gdbc/nebula_dart_gdbc.dart';
void main() async {
// 注册驱动
DriverManager.registerDriver(NgDriver());
// 获取连接
var conn = await DriverManager.getConnection(
'gdbc.nebula://127.0.0.1:9669/?space=test',
username: 'root', // username is optional
password: 'nebula', // password is optional
);
// 创建语句
var stmt = await conn.createStatement();
// 执行语句
var rs = await stmt.executeQuery(gql: 'SHOW SPACES;');
// 打印结果
print(rs);
// 关闭连接
conn.close();
}
就目前而言,拿到结果集之后,还需要自行按数组下标位进行数据组装。结果集的结构采用“头部数组”+“数据数组”的形式。在实际的开发中,处理上可能会麻烦一些。目前,我正在考虑在 dart_gdbc 未来的版本中,加入对象图映射的埋点回调。或者再开一个 OGM 的项目。关于这一点,目前只是一个还不太成熟的想法。
在文章结束部分,跟大家分享下写这个项目的心路历程。
起初,我对工作量大有一定的心理准备,铁了心想试一试。上手之后,才意识到并不仅仅是语法翻译一下这么简单,特别是不同语言的生态不同,引用的第三方也就随之不同,即使是相同的功能,运行机制也有所不同,这一部分的工作量会比想象中的要大。
再一个就是没有办法对数据库直接断点调试,算是一种黑盒调用,如同凭借着微弱的光线在黑夜中前行,对毫无头绪的问题有时候会产生一种无力感,只能凭借着经验做一些猜测,然后一点点去验证。但当连上实际的数据库,并拿到数据的那一刻,即使多年来已经习惯了被 bug 吊打再消灭 bug 的流程,但还是激动了一下。当开始着手建立 dart_gdbc 规范的时候,在《序》里写到的“责无旁贷”在脑海里回荡。直到今天发布第一个版本,这份热情算多了一份实实在在的兑现。
最后,当然是希望这个项目能够对基础软件生态有所帮助。个人也会不断修复 bug,完善功能,希望能够成为一个稳定可靠的项目。对项目感兴趣的话,欢迎参与开发。另外,dart_gdbc 与 nebula_dart_gdbc 目前收录在【图社区】(https://github.com/graph-cn),以后有新的关于图的开源项目,也会在这里发布,欢迎围观。
最后的最后,这篇文章及项目的背后有思为和想念小鱼干的清蒸两位老师的帮助,感谢。
谢谢你读完本文 (///▽///)
如果你想尝鲜图数据库 NebulaGraph,记得去 GitHub 下载、使用、(з)-☆ star 它 -> GitHub;和其他的 NebulaGraph 用户一起交流图数据库技术和应用技能,留下「你的名片」一起玩耍呀~
2023 年 NebulaGraph 技术社区年度征文活动正在进行中,来这里领取华为 Meta 60 Pro、Switch 游戏机、小米扫地机器人等等礼品哟~ 活动链接:https://discuss.nebula-graph.com.cn/t/topic/13970