
Nebula Graph 源码解读 | 客户端的通信秘密——fbthrift
Nebula Clients 给用户提供了多种编程语言的 API 用于和 Nebula Graph 交互,并且对服务端返回的数据结构进行了重新封装,便于用户使用。
目前 Nebula Clients 支持的语言有 C++、Java、Python、Golang 和 Rust。
>>>>通信框架
Nebula Clients 使用了 fbthrift https://github.com/facebook/fbthrift 作为服务端和客户端之间的 RPC 通信框架,实现了跨语言的交互。
fbthrift 提供了三方面的功能:
- 生成代码:fbthrift 可将不同语言序列化成数据结构
- 序列化:将生成的数据结构序列化
- 通信交互:在客户端、服务端之间传输消息,收到不同语言的客户端的请求时,调用相应的服务端函数
>>>>例子
这里以 Golang 客户端为例,展示 fbthrift 在 Nebula Graph 中的应用。
Vertex 结构在服务端的定义:
首先,在 src/interface/common.thrift 中定义一些数据结构:
在这里我们定义了一个 Vertex 的结构,其中 (cpp.type = "nebula::Vertex") 标注出了这个结构对应了服务端的 nebula::Vertex 。
fbthrift 会自动为我们生成 Golang 的数据结构:
在 MATCH (v:Person) WHERE id(v) == "ABC" RETURN v 这条语句中:客户端向服务端请求了一个顶点( nebula::Vertex ),服务端找到这个顶点后会进行序列化,通过 RPC 通信框架的 transport 发送到客户端,在客户端收到这份数据时,会进行反序列化,生成对应客户端中定义的数据结构( type Vertex struct )。
>>>>客户端模块
在这个章节会以 nebula-go 为例,介绍客户端的各个模块和其主要接口。
配置类 Configs,提供全局的配置选项。
客户端会话 Session,提供用户直接调用的接口。
接口定义如下
连接池 ConnectionPool,管理所有的连接,主要接口如下
连接 Connection,封装 thrift 的网络,提供以下接口
负载均衡 LoadBalance,在连接池里面使用该模块
- 策略:轮询策略
>>>>模块交互解析
- 连接池
- 初始化:
- 在使用时用户需要先创建并初始化一个连接池 ConnectionPool,连接池会在初始化时会对用户指定的 Nebula 服务所在地址建立连接 Connection,如果在用集群部署方式部署了多个 Graph 服务,连接池会采用轮询的策略来平衡负载,对每个地址建立近乎等量的连接。
- 管理连接:
- 连接池内维护了两个队列,空闲连接队列 idleConnectionQueue 和使用中的连接队列 idleConnectionQueue,连接池会定期检测过期空闲的连接并将其关闭。这两个队列在增删元素的时候会通过读写锁来确保多线程执行的正确性。
- 当 Session 向连接池请求连接时,会检查空闲连接队列中是否有可用的连接,如果有则直接返回给 Session 供用户使用;如果没有可用连接并且当前的总连接数没有超过配置中限定的最大连接数,则新建一个连接给 Session;如果已经到达了最大连接数的限制,返回错误。
- 一般只有在程序退出时才需要关闭连接池, 在关闭时池中所有的连接都会被断开。
- 客户端会话
- 客户端会话 Session 通过连接池生成,用户需要提供用户密码进行校验,在校验成功后用户会获得一个 Session 实例,并通过 Session 中的连接与服务端进行通信。最常用的接口是 execute() ,如果在执行时发生错误,客户端会检查错误的类型,如果是网络原因则会自动重连并尝试再次执行语句。
- 需要注意,一个 Session 不支持被多个线程同时使用,正确的方式是用多个线程申请多个 Session,每个线程使用一个 Session。
- Session 被释放时,其持有的连接会被放回到连接池的空闲连接队列中,以便于之后被其他 Session 复用。
- 连接
- 每个连接实例都是等价的,可以被任意 Session 持有,这样设计的目的是这些连接可以被不同的 Session 复用,减少反复开关 Transport 的开销。
- 连接会将客户端的请求发送到服务端并将其结果返回给 Session。
- 用户使用示例
>>>>返回数据结构
客户端对部分复杂的服务端返回的查询结果进行了封装并添加了接口,以便于用户使用。
对于 nebula::Value ,在客户端会被包装成 ValueWrapper ,并通过接口转换成其他结构。(i.g. node = ValueWrapper.asNode())
>>>>数据结构的解析
对于语句 MATCH p= (v:player{name:"Tim Duncan"})-[]->(v2) RETURN p ,返回结果为:
我们可以看到返回的结果包含了一行,类型是一条路径. 此时如果需要取得路径终点(v2)的属性,可以通过如下操作实现:
>>>>客户端地址
各语言客户端 GitHub 地址:
- https://github.com/vesoft-inc/nebula-cpp
- https://github.com/vesoft-inc/nebula-java
- https://github.com/vesoft-inc/nebula-python
- https://github.com/vesoft-inc/nebula-go
- https://github.com/vesoft-inc/nebula-rust
本文转载自公众号nebula graph community
