Red Arrow Flight 实战指南:使用 Ruby 构建 Apache Arrow Flight 高性能网络数据传输
2026/9/23 18:29:33 网站建设 项目流程
  • 数据工程
  • 大数据
  • 序列化
  • 数据分析

【免费下载链接】arrow

Apache Arrow is a multi-language toolbox for accelerated data interchange and in-memory processing

项目地址:https://gitcode.com/gh_mirrors/arrow13/arrow
点击查看免费下载

Apache Arrow Flight 是 Apache Arrow 生态中用于跨网络高速读写数据的客户端-服务端框架,而 Red Arrow Flight(red-arrow-flightgem)则是它在 Ruby 世界的官方绑定实现。本文以仓库内 ruby/red-arrow-flight/README.md 为主线,结合源码与测试用例,系统讲解 Red Arrow Flight 的定位、安装方式、加载机制、客户端与服务端 API 的完整用法,帮助你用 Ruby 写出可运行的 Flight 客户端与服务器程序。

一、Red Arrow Flight 是什么

Red Arrow Flight 是 Apache Arrow Flight 的 Ruby 绑定(Ruby bindings),其核心设计目标是:让 Ruby 开发者能够直接读写"存储在任意位置、以任意格式存在的语义飞行数据(semantic flights)",而无需触碰 C/C++ 层代码。

与 Python(pyarrow)或 Java 等语言直接绑定 C++ 实现不同,Red Arrow Flight 走了一条"间接绑定"的技术路线,整条技术链由四层构成:

层次组件作用
1Apache Arrow Flight C++(cpp/src/arrow/flight)Flight 协议与 RPC 的核心实现
2Apache Arrow Flight GLib(c_glib/arrow-flight-glib)C 语言包装层,桥接 C++ 与 GObject Introspection
3GObject IntrospectionC 库的运行时绑定生成中间件
4gobject-introspection gem + Red Arrow FlightRuby 侧加载与封装

之所以需要中间的 GLib 与 GObject Introspection 两层,是因为GObject Introspection 无法直接消费 Apache Arrow Flight C++ 的 C++ API——C++ 缺乏稳定的 ABI 与可反射的类型系统。Apache Arrow Flight GLib 正是为解决这个问题而存在的"桥梁",它在 C++ 与 GObject Introspection 之间提供了一层 C 接口;而 gobject-introspection gem 则是 GObject Introspection 的 Ruby 绑定,Red Arrow Flight 正是通过它把 GLib 层的类型与函数在运行时自动映射为 Ruby 类与方法。

这一架构的实现在 lib/arrow-flight/loader.rb 中清晰可见:Loader < GObjectIntrospection::Loader,通过super("ArrowFlight", ArrowFlight)加载名为ArrowFlight的 GIR namespace,并在加载完成后按需 require 各 Ruby 增强模块。

二、安装 Red Arrow Flight

2.1 前置条件:安装 Apache Arrow Flight GLib

由于 Red Arrow Flight 是建立在 Apache Arrow Flight GLib 之上的运行时绑定,安装 Red Arrow Flight 之前必须先安装 Apache Arrow Flight GLib(以及它依赖的 GLib、gobject-introspection 运行库)。具体安装方式请参照 Apache Arrow 官方安装文档(https://arrow.apache.org/install/)中对应你所在发行版的步骤,此处不再赘述。

2.2 安装 gem

GLib 安装完成后,通过 RubyGems 安装 Red Arrow Flight:

$ gem install red-arrow-flight

从 red-arrow-flight.gemspec 可以看到它的运行时依赖只有一个:red-arrow(且要求与当前 gem 完全相同的版本,即spec.add_runtime_dependency("red-arrow", "= #{spec.version}"))。这意味着安装 red-arrow-flight 时 RubyGems 会自动拉取同版本的 red-arrow,二者必须保持版本一致,否则会安装失败。

值得留意的是,gemspec 中通过spec.extensions = ["dependency-check/Rakefile"]注册了一个编译扩展,其作用是在安装时执行依赖检查,确认系统中已存在可用的 Arrow Flight GLib 与 GObject Introspection 环境。因此如果你在gem install阶段看到"无法找到 ArrowFlight typelib"之类的报错,通常说明前置的 GLib 层没有正确安装或GI_TYPELIB_PATH环境变量未指向 typelib 所在目录。

2.3 版本与许可证

当前仓库中 lib/arrow-flight/version.rb 记录的版本为17.0.0-SNAPSHOTMAJOR/MINOR/MICRO三个分量会被 gemspec 用于拼接最终版本号),许可证为 Apache-2.0。

三、加载机制:require "arrow-flight"背后发生了什么

官方 README 给出的用法示例是:

require "arrow-flight" # TODO

其中# TODO说明官方 README 尚未给出完整示例,但我们可以从源码完整还原其加载流程与可用的全部 API。入口文件 lib/arrow-flight.rb 的加载顺序如下:

  1. require "arrow":先加载 red-arrow,确保 Arrow 核心数据结构(Table、RecordBatch、Schema 等)可用;
  2. require "arrow-flight/version":定义ArrowFlight::VERSIONArrowFlight::Version
  3. require "arrow-flight/loader"并调用Loader.load:通过 GObject Introspection 加载ArrowFlightnamespace,将 C 层的全部类与方法动态绑定到 Ruby 的ArrowFlight模块下;
  4. 定义ArrowFlight::Error < StandardError作为统一的异常基类。

在 lib/arrow-flight/loader.rb 中,require_libraries会按需加载以下 8 个 Ruby 增强文件:

  • call-options.rb —— 调用选项
  • client.rb —— 客户端增强
  • client-options.rb —— 客户端连接选项
  • location.rb —— 服务地址
  • record-batch-reader.rb —— 批量读取器
  • server-call-context.rb —— 服务端调用上下文
  • server-options.rb —— 服务端选项
  • ticket.rb —— 数据票据

这些文件不是重新实现 Flight 协议,而是对 GObject Introspection 自动生成的方法做 Ruby 风格增强——例如把 C 风格回调改造成 Ruby 惯用的 Enumerable 迭代、把 Hash 自动转换为选项对象等。

此外,loader 还覆写了prepare_function_info_lock_gvl并将lock_gvl_default设为false。这意味着Flight 的底层调用默认不持有 Ruby 全局锁(GVL),长时间阻塞的网络调用不会卡住其他 Ruby 线程,这对在 Web 应用或并发场景中使用 Flight 客户端是一个重要的并发友好设计。

四、客户端编程:连接、发现与拉取数据

4.1 建立连接:ClientLocation

Flight 客户端通过服务地址(Location)连接服务端。Location 使用 URI 风格的字符串表示协议与端点,从测试代码看,典型格式为:

client = ArrowFlight::Client.new("grpc://127.0.0.1:8815")

其中grpc://是当前 Flight 默认的传输协议。从源码来看:

  • location.rb 中Location.try_convert接受 String 并返回Location对象,因此Client.new的第一个参数既可以直接传Location实例,也可以传字符串(会被自动转换);
  • 测试代码中@server.listen("grpc://127.0.0.1:0")使用端口0表示由系统随机分配端口,随后通过@server.port获取实际端口拼接出@location,这种写法非常适合测试与动态端口场景。

4.2 连接选项:ClientOptions

client-options.rb 为ClientOptions提供了 Hash 到选项对象的自动转换:传入 Hash 时,会以每个键为 setter 方法名(options.__send__("#{name}=", value))完成赋值。因此你可以写出如下风格的代码(具体可用的 setter 由 GLib 层决定,例如 TLS 证书、超时等):

options = ArrowFlight::ClientOptions.new # 或通过 Hash 便捷构造: # options = { ... } # 会被 try_convert 自动处理

4.3 列出可用数据:list_flights

客户端可以询问服务端当前提供哪些数据(FlightInfo 列表)。测试用例 test-client.rb 给出了直接可用的调用方式:

client = ArrowFlight::Client.new(@location) flights = client.list_flights # flights 为 FlightInfo 数组,可从中读取 schema、端点(endpoints)等信息

4.4 拉取数据:do_getTicket

Flight 的取数模型是"先拿票据(Ticket),再凭票据取数据"。Ticket本质上是一个不透明的二进制标识,ticket.rb 显示它支持从 String 或GLib::Bytes自动转换。

测试用例中的完整取数流程:

ticket = generator.page_view_ticket # 一个 Ticket 对象 reader = client.do_get(ticket) # 返回 RecordBatchReader table = reader.read_all # 一次性读成 Arrow Table

服务端校验票据的方式(见 server.rb)是通过ticket.data.to_s取出票据的原始字节内容并与之比较,不匹配则抛出Arrow::Error::Invalid.new("invalid ticket"),这印证了 Ticket 是不透明字节串这一设计。

4.5 认证:authenticate_basic

client.rb 中实现了一个非常有用的增强方法authenticate_basic(user, password, options = nil)(自 13.0.0 起提供):

  • 使用用户名/密码向服务端发起 Basic 认证握手;
  • 成功后,服务端返回 Bearer token,该方法会把Authorization: Bearer ...形式的请求头自动写入CallOptions
  • 返回的CallOptions可直接用于后续的list_flightsdo_get等调用,实现"一次认证、全程带票";
  • 若传入的options本身是CallOptions,认证结果会写入该对象并原样返回;否则新建一个CallOptions。若 token 为空(认证失败),则不会添加任何请求头。
call_options = client.authenticate_basic("user", "password") reader = client.do_get(ticket, call_options)

4.6 调用选项:CallOptions

call-options.rb 为CallOptions增加了三个 Ruby 风格方法:

  • headers=(headers):清空现有请求头后批量设置;
  • each_header:迭代每个请求头(返回 Enumerator 或配合 block);
  • headers:把请求头收集为数组。

由于底层是 C 库的哈希表,这里通过clear_headers/add_header/foreach_header等 GLib 生成方法完成实际读写。典型用法:

options = ArrowFlight::CallOptions.new options.add_header("x-custom-header", "value")

五、服务端编程:继承Server实现 Flight 服务

5.1 服务端基座与生命周期

Red Arrow Flight 的ArrowFlight::Server由 GObject Introspection 从 GLib 层自动绑定生成,测试辅助类 server.rb 展示了标准的服务端实现范式:

class Server < ArrowFlight::Server type_register # 向 GObject 类型系统注册子类,必须调用 private def virtual_do_list_flights(context, criteria) # 返回 FlightInfo 数组 end def virtual_do_do_get(context, ticket) # 根据 ticket 返回 RecordBatchStream end end

关键点有三:

  1. 子类化时必须调用type_register:因为Server底层是 GObject 类型,Ruby 子类必须向 GObject 类型系统注册才能被 GLib 层正确实例化与回调;
  2. 覆写virtual_do_*方法:GLib 层把 C++ 的虚函数以virtual_do_*前缀暴露给 Ruby,服务端必须实现这些方法才能真正响应客户端请求;
  3. 方法以context为第一参数contextServerCallContext对象。

5.2 启动与监听

test-client.rb 展示了服务端完整的生命周期管理:

@server = Helper::Server.new @server.listen("grpc://127.0.0.1:0") # 监听随机端口 @location = "grpc://127.0.0.1:#{@server.port}" # ... 测试逻辑 ... @server.shutdown # 优雅关闭

listen接收 Location(ServerOptions.try_convert同样支持从 Hash 或 Location 转换,见 server-options.rb),shutdown用于释放资源。测试还注明"Windows 上不稳定"(omit("Unstable on Windows")),因此在 Windows 平台运行需要额外注意稳定性问题。

5.3 返回数据:RecordBatchStream

服务端virtual_do_do_get的返回值是ArrowFlight::RecordBatchStream,可以从一个Arrow::Table构造:

table = generator.page_view_table ArrowFlight::RecordBatchStream.new(table)

客户端拿到的是RecordBatchReader,二者通过 Flight 的流式传输协议对应起来。

5.4 读取客户端请求头:ServerCallContext

server-call-context.rb 为服务端的调用上下文补充了两个方法:

  • each_incoming_header:迭代客户端传入的请求头;
  • incoming_headers:把请求头收集为数组。

这样服务端就可以读取客户端通过CallOptions携带的自定义头或认证信息,用于鉴权或路由。

六、结果读取:RecordBatchReader的 Ruby 化

do_get返回的RecordBatchReader在 record-batch-reader.rb 中被扩展为 Ruby 惯用的 Enumerable:

reader.each do |record_batch| # 逐批处理 end # 等价写法: reader.each { |batch| ... }

其实现是循环调用 GLib 生成的read_next,直到返回nil表示流结束。此外测试中还用到了reader.read_all(一次性把整个流读取为Arrow::Table),方便小数据集场景下直接取得完整表结构。

七、测试与验证:如何确认你的 Flight 代码可用

仓库自带的单元测试是验证 API 用法的最佳参考,位于 test-client.rb:

  • test_list_flights:创建客户端 → 调用list_flights→ 断言返回的 FlightInfo 列表与测试服务端生成的page_view信息一致;
  • test_do_get:创建客户端 → 用do_get(ticket)获取RecordBatchReader→ 用read_all读为 Table → 与预期的page_view_table对比。

配套的测试辅助(info-generator.rb 与 server.rb)构造了一个内存中的 Flight 服务端:服务端把预置的 Table 包装为RecordBatchStream返回,客户端凭 Ticket 拉取。这一整套"服务端 + 客户端 + 断言"的结构,正是生产环境最小可复刻的 Flight 应用骨架。

八、常见问题与注意事项

  1. gem install失败:绝大多数情况是 Apache Arrow Flight GLib 未安装或 typelib 路径未配置,请先回到前置条件步骤检查,确认gobject-introspection运行库与ArrowFlight.typelib文件可用。
  2. 版本一致性:red-arrow-flight 与 red-arrow 的版本必须严格一致(gemspec 中强制=绑定),混合不同版本会导致加载失败。
  3. Windows 兼容性:仓库测试明确标注服务端在 Windows 上"不稳定",跨平台部署时建议优先在 Linux/macOS 上验证。
  4. 并发与 GVL:Flight 底层调用默认不持有 Ruby GVL(见 loader 的lock_gvl_default = false),但这也意味着数据回调的线程安全需要你自己保证。
  5. README 中的# TODO:官方 README 尚未给出完整用法示例,本文所有可运行示例均来自仓库内 lib 与 test 目录的源码证据,可放心作为参考。

九、总结

Red Arrow Flight 通过"Apache Arrow Flight C++ → GLib → GObject Introspection → Ruby"的四层桥接架构,让 Ruby 开发者得以零成本接入 Apache Arrow Flight 的高性能网络数据交换能力。本文覆盖了从安装、加载机制到客户端(Client/Location/Ticket/CallOptions/authenticate_basic)与服务端(Server子类化/virtual_do_*回调/RecordBatchStream/ServerCallContext)的完整编程模型。无论你是要构建 Ruby 侧的 Flight 数据消费者,还是要实现一个 Flight 服务端向其他语言客户端提供数据,都可以直接参照上文中的源码路径与测试用例落地实现。

  • 数据工程
  • 大数据
  • 序列化
  • 数据分析

【免费下载链接】arrow

Apache Arrow is a multi-language toolbox for accelerated data interchange and in-memory processing

项目地址:https://gitcode.com/gh_mirrors/arrow13/arrow
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询