Client Design

openGemini大约 7 分钟约 2215 字

背景

由于Influxdb 1.X的客户端已经基本处于维护状态,同时openGemini仍在不断发展中,为了能够更好地支持openGemini,如支持对接多个服务端地址、支持对接Apache Arrow Flight协议等,社区决定开发属于openGemini自己的客户端SDK。

客户端SDK规划功能

  • 支持对接多个服务端地址
  • 支持对接Apache Arrow Flight协议
  • 支持Sql查询、结构化查询、写入、批量写入等,详见下文UML图
  • 默认超时,连接超时10秒,读写超时30秒

本文的方法假定编程语言不支持重载,如编程语言支持重载,可以对方法名进行一些优化调整。

客户端构造参数设计

Database & RetentionPolicy管理设计

写入点位设计

Execute 接口设计

Execute 接口提供了一个统一的 SQL 执行接口,可以自动将不同类型的语句路由到相应的底层方法。该设计支持类 SQL 语句,包括 INSERT、SELECT、CREATE、DROP 和其他数据库操作,并提供参数支持和类型安全。

语句路由逻辑

参数支持

Execute 接口支持参数化语句并自动进行类型转换:

使用示例

基本用法

result, err := client.Execute(opengemini.Statement{
    Database: "mydb",
    Command:  "SELECT * FROM weather LIMIT 10",
})

参数化查询

result, err := client.Execute(opengemini.Statement{
    Database: "mydb", 
    Command:  "SELECT * FROM weather WHERE location=$loc AND temp>$temp",
    Params: map[string]any{
        "loc":  "beijing",
        "temp": 25.0,
    },
})

参数化插入

result, err := client.Execute(opengemini.Statement{
    Database: "mydb",
    Command:  "INSERT weather,location=$location temperature=$temp,humidity=$hum",
    Params: map[string]any{
        "location": "shanghai", 
        "temp":     30.2,
        "hum":      70,
    },
})

查询设计

OpenTelemetry 集成设计

为增强 OpenGemini 客户端的可观测性,便于追踪与查询、写入操作相关的性能指标、错误及其他信息,本方案采用拦截器模式集成 OpenTelemetry,实现全链路追踪。该设计支持非侵入式扩展,允许与其他拦截器(如日志、认证拦截器)共存,同时最大限度减少对原有客户端的修改。

拦截器设计

拦截器模式定义了标准化接口,用于挂钩客户端操作(查询/写入)并注入遥测逻辑。

定义基础客户端类,关联拦截器接口

基础 Client 类管理一组拦截器,允许在客户端操作期间动态注册和执行拦截器逻辑。

定义集成 OpenTelemetry 的拦截器实现类,实现 Interceptor 接口

OtelClient 类实现 Interceptor 接口,嵌入 OpenTelemetry 逻辑以捕获客户端操作的跟踪、指标和日志。

追踪系统核心模块

使用示例(Go language examples)

func main() {
    var ctx = context.Background()
    shutdown, err := setupOtelSDK(ctx)
    if err != nil {
        return
    }
    defer func() {
        err = errors.Join(err, shutdown(ctx))
    }()

    config := &opengemini.Config{
        Addresses: []opengemini.Address{{
            Host: "127.0.0.1",
            Port: 8086,
        }},
    }
    client, err := opengemini.NewClient(config)
    if err != nil {
        fmt.Println(err)
        return
    }

    client.Interceptors(opengemini.NewOtelInterceptor())

    err = client.CreateDatabase("db0")
    if err != nil {
    }
}

查询构造器设计

Ping 设计

Inner Http client 设计

使用类似InnerHttpClient的设计,将鉴权、负载均衡、重试等逻辑封装在内部,对client提供简单的接口。增强模块化和代码清晰度。

错误处理

错误信息

场景1 http请求失败

$operation request failed, error: $error_details

场景2 http响应码不符合预期

$operation error resp, code: $code, body: $body

场景3 其他异常

$operation failed, error: $error_details
# example:
writePoint failed, unmarshall response body error: json: cannot unmarshal number ...