夜天之书 #98 Rust 程序库生态合作的例子

近期主要时间都在适应产品市场(Product Marketing)的新角色,不少想法还在酝酿和斟酌当中,于是文章输出没有太多时间来推敲和选题,只能保持每月发布相关的进展或一些零碎的思考。或许我可以恢复最早的模式,多做更新但是文章内容可能不会太过完整。

原本这一期想讨论的是 ASF 开源项目代码的所有权,以及开源软件变更协议的具体含义与操作方式。但是这个话题稍显枯燥,而且要把相关细节讲清楚,还需要继续斟酌。所以我改为采取把最近开始全职投入 Rust 开发,并接触相关生态发展和合作的经历做一点梳理,分享个人在其中的所见所闻。

我会从 Rust HTTP 库的生态切入,从最近一个大事件出发,即 Rust 采用范围最广的 HTTP 库 hyper 和 http 前后发布 1.0 版本,讲述其导致的整个 Rust 应用开发上下游牵一发而动全身的变化。

起因是我在整蛊 GreptimeDB HTTP 相关代码的时候,发现项目依赖的 axum 库是 0.6 版本,而上游是 0.7 版本。这天降的升级闲手,不升有点对不起自己了。通过解决升级过程中的问题,也能帮助摸清 GreptimeDB HTTP 模块的逻辑。

不过,我显然是小看了 Rust 生态荼毒甚广的 ZeroVer[1] 文化的威力。

ZeroVer 是一个揶揄的说法,即在采用语义化版本(SemVer[2])的前提下,因为各种原因,项目迟迟不愿发布 1.0 版本。

29a90b7f85a0aa35353b64cf788788c1.png

0ver 的魅力时刻

在语义化版本中,0.x 版本是在项目正式发布或说进入稳定期前,一个相对动荡的快速迭代阶段。语义化版本的核心价值是告诉用户升级版本可能面临什么变化:

  • 升级补丁版本(Patch Version):应当只有缺陷修复和性能提升等,不会破坏用户程序的改动。

  • 升级小版本(Minor Version):可能包含新功能,应当向后兼容,用户应用应当可以顺滑升级。

  • 升级大版本(Major Version):可能包含破坏性变更,用户需要做好应对逻辑甚至数据迁移的准备。

当然,在实践当中,语义化版本并不那么严格执行。尤其对于大型项目的实验性功能,是可能有一个独立的可靠机制的。但是无论如何,进入 1.0 之后就意味着项目对用户做了一个向后兼容的保证,除非升级大版本,否则用户会假设软件升级是可以非常激进的。

ZeroVer 方案的反面极端是 Apache Arrow 和 Apache DataFusion 每次发版都升级大版本的做法,很难评价。

  • build(deps): update datafusion to latest and arrow to 51.0[3]

  • build(deps): bump datafusion 20240528[4]

话说回来,axum 0.6 到 0.7 的版本升级是一个巨大的变更,基本把核心的类型设计做了一个颠覆,即 Body 不再是泛型了。

这其实也是一个槽点。国内开发者油条哥做的 Poem[5] 就不搞这些花里胡哨的泛型,直接用胖指针抹掉底下的差别,提供更好的开发体验。你说我都 HTTP 了,搞应用层接口开发了,我是跟你抠这点性能的人吗?

c928631d08a5809b17244ec7dcf29520.png

一个屏幕都写不完的 breaking changes 列表

34fa8303e1ac6fdc45d986b47d8b512a.png

Rust HTTP 生态泛型的魅力时刻

于是我着手升级 axum 的版本,一上来就是好几个屏幕的编译错误。没事,Rust 开发者的日常而已。

第一个小问题,我们依赖了 axum-test-helper 这个库,它没跟上 axum 0.7 的版本。我先试着给上游提 PR 升级:

  • feat: support axum 0.7[6]

未果。

自己维护比较头疼,其实只有一个文件,最后我在 GreptimeDB 里 vendor 掉了:

  • refactor: bundle the lightweight axum test client[7]

  • chore: respect axum test client's origin[8]

这里我又要吐槽了。axum 是不是哪里有问题,居然不提供测试套件,还要下游自己 embedded 然后去掉 (crate) 修饰词,好玩吗?虽然其实也是可以用 reqwest 套一个 TestClient 解决,按照上游的说法:“这只是很薄的一层”[9],但是这么简单提升使用体验的事情,为何不做呢?

反观 Poem 就提供了 TestClient[10] 工具,开发起来舒服多了。不管是不是我一个文件就能解决,这不是下游应该解决的事情。

紧接着,发现 axum 自己的 TestClient 有落后,以及一个 Rust Nightly toolchain 的兼容问题,提 PR 解决:

  • Upgrade reqwest to 0.12[11]

  • Enable diagnostic attributes for Rust 1.78+[12]

Rust Nightly toolchain 的兼容问题是一个很奇妙的问题。因为 Nightly 顾名思义就是最新的 Rust 开发版本,不提供语义化版本保证,只是在 Rust 1.x 的时间线上大体向后兼容。但是结合上 Rust Stabilize 的流程,以及打开 feature gate 如果找不到 feature flag 就会编译失败等等细碎的问题,经常会导致生态在跟进 Nightly 之前有一个无法编译的窗口。

这不算是绝对的坏事,甚至推着生态跟新版本是合理的。但某种程度上其实是 Rust Stabilize 太慢,导致系统开发比如 GreptimeDB 不得不用 Nightly 版本,而出现的新问题。Rust 开发很多用 Nightly 版本,跟 0ver 可能也有某种互相呼应的巧合。在这种环境下,开发公共库并保持多平台多版本兼容,其实是一件非常困难的事情,怪不得都 0ver 了。

然后我就遇到了本次升级最大的大魔王。Rust 生态的 gRPC 库 tonic[13] 闪亮登场!

这里的依赖关系大概是这样的。

首先,axum 0.7 除了接口变化,还有一个关键的依赖变化是把 hyper 和 http 给升到了 1.0 上。因为 Rust HTTP 生态都依赖 hyper 和 http 这两个库,这就导致如果你的接口开始交互,那么所有的结构都要同步到同一个版本。

这个问题并不那么致命。因为如果你合理的 re-export 了依赖的接口,那么同一个 crate 的多个版本是可以共存的,就像我最近在升级 Apache OpenDAL 的时候连带需要升级 reqwest 到 0.12 版本,它依赖了 hyper 1.0 和 http 1.0 但是跟 axum 的 server 端代码关系较小,所以我可以切割开:

  • build(deps): upgrade to reqwest 0.12[14]

注意以上 PR 里修改 use 语句以选择正确的 re-export 符号的变更。

不过,tonic 的情况就比较幽默了。tonic 依赖了 axum 0.6 版本,而且 tonic 和 axum 都用了 tower 作为中间件的第三方库,而且都没有 re-export 而是标榜自己能无缝接入 tower 丰富的中间件生态。

由于 tonic 尚未完成 axum 0.7 和 hyper 1.0 的升级,这下就连环爆炸了:你找不到一个合适的 tower 版本,或者说你找不到一个合适的 axum 版本,来作为 GreptimeDB 被传递关联起来的版本约束。

于是,直到今天,GreptimeDB 的升级还是未完成的状态:

  • Upgrade Axum to 0.7[15]

不过,在三月底,tonic 上游就出现了一位大英雄开了一个升级的 PR 完成了主要的工作:

  • Upgrade to Hyper 1.0 & Axum 0.7[16]

这个 PR 我看到的时候还需要两个 hyper-util 的改动,我给帮忙推着合并了:

  • Adds max_pending_accept_reset_streams for legacy[17]

  • feat: add {http1,http2}_only for auto conn[18]

这里又岔开一下。虽然 tonic 在 hyperium 组织下,跟 hyper 和 http 一样,但是 hyper 和 http 的作者,Rust 生态真正负责人的英雄 @seanmonstar 并不怎么看 tonic 这个库。tonic 主要是 tokio 的作者 @LucioFranco 和另外两位志愿者 @tottoto 和 @djc 维护的。他们都有自己的本职工作要做,所以并没有太多时间 Review tonic 的变动。实际上,很多项目就用着 tonic 0.11 和 axum 0.6 万年不动也还行的(有没有发现 tonic 也是 ZeroVer 流派)。

不过,经过两个月当中的空闲时间累积,这个 tonic 的升级 PR 终于看着要走进尾声,希望能顺利。开源项目能有这个效率,小几个月跟进一个大型重构,虽然比不上 @seanmonstar 和其他活跃维护项目的响应速度,也绝对算是能及时更新的了。

这个 tonic PR-3610 有很多经典的开源贡献者跟维护者之间的交流和争论,我这里就不展开了。但是我非常建议各位关心开源的人去看看,了解真实的开源世界,未来或许参与进去做出自己的贡献,而不是自己想象或者冷眼旁观。

最后,贴两张图说明 Rust 生态 ZeroVer 的严重程度:

7be065416532ab350da21b6790bfdfa2.jpeg

GreptimeDB 的依赖 ZeroVer 有 782 个

4b91f14197cbaabb13ccabeb4d2cd69d.jpeg

GreptimeDB 的依赖非 ZeroVer 有 278 个

在 GreptimeDB 的依赖里,ZeroVer 的占比约莫七成。

其实,GreptimeDB 很多升级都很有工程上的说法,欢迎各位关注发现。我可能也会在今年的某些 Rust 会议上分享相关的经验。

文末放两个我在 Reddit 上跟这次 HTTP 生态大更新相关的讨论链接,在 Rust channel 上还算有一些有趣的讨论。

  • Spreading http 1.0 and hyper 1.0 among the ecosystem[19]

  • Super heavy generic on HTTP lib makes debugging hard[20]

参考资料

[1]

ZeroVer: https://0ver.org/

[2]

SemVer: https://semver.org/

[3]

build(deps): update datafusion to latest and arrow to 51.0: https://github.com/GreptimeTeam/greptimedb/pull/3661

[4]

build(deps): bump datafusion 20240528: https://github.com/GreptimeTeam/greptimedb/pull/4061

[5]

Poem: https://docs.rs/poem/latest/poem/

[6]

feat: support axum 0.7: https://github.com/cloudwalk/axum-test-helper/pull/29

[7]

refactor: bundle the lightweight axum test client: https://github.com/GreptimeTeam/greptimedb/pull/3669

[8]

chore: respect axum test client's origin: https://github.com/GreptimeTeam/greptimedb/pull/3805

[9]

“这只是很薄的一层”: https://github.com/tokio-rs/axum/issues/1146

[10]

TestClient: https://docs.rs/poem/latest/poem/test/struct.TestClient.html

[11]

Upgrade reqwest to 0.12: https://github.com/tokio-rs/axum/pull/2688

[12]

Enable diagnostic attributes for Rust 1.78+: https://github.com/tokio-rs/axum/pull/2693

[13]

tonic: https://github.com/hyperium/tonic

[14]

build(deps): upgrade to reqwest 0.12: https://github.com/GreptimeTeam/greptimedb/pull/4037/commits/ad0793b48cb0ac19fbd7c52be215140d96ca6eb5

[15]

Upgrade Axum to 0.7: https://github.com/GreptimeTeam/greptimedb/issues/3610

[16]

Upgrade to Hyper 1.0 & Axum 0.7: https://github.com/hyperium/tonic/pull/1670

[17]

Adds max_pending_accept_reset_streams for legacy: https://github.com/hyperium/hyper-util/pull/102

[18]

feat: add {http1,http2}_only for auto conn: https://github.com/hyperium/hyper-util/pull/111

[19]

Spreading http 1.0 and hyper 1.0 among the ecosystem: https://www.reddit.com/r/rust/comments/1bqfjbo/spreading_http_10_and_hyper_10_among_the_ecosystem/

[20]

Super heavy generic on HTTP lib makes debugging hard: https://www.reddit.com/r/rust/comments/1czi4g4/super_heavy_generic_on_http_lib_makes_debugging/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/21249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#面:说出尽可能多的基于.Net Framework的语言

C#、VB.Net、F#、PowerShell、IronPython、J#、Ruby.Net C#是一种基于.Net Framework的编程语言,是微软公司开发的一种通型、面向对象的编程语言。C#可以用于开发种类型的应用程序,包括桌面应用程序、Web应用程序、移动应用程序和游戏等。 除了C#之外&…

YOLOv8改进(一)-- 轻量化模型ShuffleNetV2

文章目录 1、前言2、ShuffleNetV2代码实现2.1、创建ShuffleNet类2.2、修改tasks.py2.3、创建shufflenetv2.yaml文件2.4、跑通示例 3、碰到的问题4、目标检测系列文章 1、前言 移动端设备也需要既准确又快的小模型。为了满足这些需求,一些轻量级的CNN网络如MobileNe…

如何进入docker容器中

要进入正在运行的Docker容器&#xff0c;您可以使用docker exec命令。这里是一个基本的命令示例&#xff1a; docker exec -it <container_id_or_name> /bin/bash这里的-it参数是为了让我们能交互式地使用容器的shell。<container_id_or_name>是您想要进入的容器的…

十_信号4-SIGCHLD信号

SIGCHLD信号 在学习进程控制的时候&#xff0c;使用wait和waitpid系统调用何以回收僵尸进程&#xff0c;父进程可以阻塞等待&#xff0c;也可以非阻塞等待&#xff0c;采用轮询的方式不停查询子进程是否退出。 采用阻塞式等待&#xff0c;父进程就被阻塞了&#xff0c;什么都干…

力扣83. 删除排序链表中的重复元素

Problem: 83. 删除排序链表中的重复元素 文章目录 题目描述思路复杂度Code 题目描述 思路 1.定义快慢指针fast、slow均指向head&#xff1b; 2.每次fast后移一位&#xff0c;当fast和slow指向的节点值不一样时&#xff0c;将slow.next指向fast同时使slow指向fast&#xff1b; 3…

MyBatis框架-开发方式+参数传递+#{}、${}+返回值处理+查询结果封装为对象+resultType

一、开发方式 MyBatis-Dao层Mapper接口化开发 二、注意事项 1、Mapper接口与Mapper.xml映射文件要满足4个对应 &#xff08;1&#xff09;Mapper接口的全类名必须与Mapper映射文件中的namespace相同 &#xff08;2&#xff09;Mapper接口中的每一个方法名在Mapper映射文件…

创建对象和继承的多种方式

1. 创建对象的多种方式&优缺点 1.1 工厂模式 function createPerson(name) {var o new Object();o.name name;o.getName function () {console.log(this.name);};return o; }var person1 createPerson(kevin);优点&#xff1a;简单&#xff1b; 缺点&#xff1a;对象…

Linux C/C++ 系统错误

在C中&#xff0c;如果调用了库函数&#xff0c;可以通过函数的返回值判断调用是否成功。其实还有一个整型的全局变量errno&#xff0c;存放了函数调用过程中产生的错误代码。 如果调用库函数失败&#xff0c;可以通过errno的值来查找原因 #include <errno.h>strerror(…

密码学——银行应用程序安全:理解和解决关键安全问题

1. 认证和访问控制 1.1 用户认证 银行应用程序通过Web服务器让客户访问其银行账户的操作历史。用户首先通过用户名和密码进行认证&#xff0c;然后才能查看其账户历史。这个过程涉及基本的认证和访问控制机制&#xff0c;确保只有合法用户可以访问其个人信息。 2. 安全执行环境…

时序数据库介绍及应用场景,C#实例

一&#xff0c;时序数据库介绍 时序数据库&#xff08;Time Series Database&#xff0c;TSDB&#xff09;是一种专门用于存储、处理和查询时间序列数据的数据库系统。以下是关于时序数据库的详细解释&#xff1a; 定义 时序数据库是一种优化用于摄取、处理和存储时间戳数据的…

回溯算法之电话号码字母组合

题目&#xff1a; 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits "2…

【MySQL】探索 MySQL 窗口函数(Window Functions)

缘分让我们相遇乱世以外 命运却要我们危难中相爱 也许未来遥远在光年之外 我愿守候未知里为你等待 我没想到为了你我能疯狂到 山崩海啸没有你根本不想逃 我的大脑为了你已经疯狂到 脉搏心跳没有你根本不重要 &#x1f3b5; 邓紫棋《光年之外》 在大数据分…

Java web应用性能分析之【jvisualvm远程连接云服务器】

Java web应用性能分析之【java进程问题分析概叙】-CSDN博客 Java web应用性能分析之【java进程问题分析工具】-CSDN博客 前面整理了java进程问题分析和分析工具&#xff0c;现在可以详细看看jvisualvm的使用&#xff0c;一般java进程都是部署云服务器&#xff0c;或者托管IDC机…

【MySQL】探索 MySQL 中的 CASE WHEN 表达式

缘分让我们相遇乱世以外 命运却要我们危难中相爱 也许未来遥远在光年之外 我愿守候未知里为你等待 我没想到为了你我能疯狂到 山崩海啸没有你根本不想逃 我的大脑为了你已经疯狂到 脉搏心跳没有你根本不重要 &#x1f3b5; 邓紫棋《光年之外》 在数据库查…

每周统计-20240531

用于测试程序的稳定性&#xff1a; 龙虎榜&#xff1a; 成交额&#xff1a; 封成比&#xff1a; 收盘前放量&#xff1a; 开盘抢筹&#xff1a; 封单额&#xff1a;

论文阅读:Correcting Motion Distortion for LIDAR HD-Map Localization

目录 概要 Motivation 整体架构流程 技术细节 小结 论文地址&#xff1a;http://arxiv.org/pdf/2308.13694.pdf 代码地址&#xff1a;https://github.com/mcdermatt/VICET 概要 激光雷达的畸变矫正是一个非常重要的工作。由于扫描式激光雷达传感器需要有限的时间来创建…

YOLOv5训练数据集的配置文件格式与使用技巧

文章目录 一 概述二 配置文件说明2.1 官方配置文件解析2.2 自定义数据集配置文件2.3 其他格式指定数据集路径2.4 多个数据集路径指定 三 总结注意事项 一 概述 本文档主要记录 YOLOv5 算法在进行模型训练前&#xff0c;关于加载数据集的配置文件的说明。 默认情况下&#xff…

RPC-----RCF

RPC RPC(Remote Procedure Call Protocol&#xff09;——远程过程调用协议。 RCF

linux命令:调试必备工具dmesg

在服务器上进行芯片调试时&#xff0c;我们会遇到各种各样的问题&#xff0c;很多问题与操作系统相关。此时就需要了解操作系统发生了哪些事件。 dmesg 是linux系统中用来打印或控制内核缓冲区内容的命令。这个环形缓冲区记录了系统启动以来发生的各种事件消息&#xff0c;包括…

ChatTTS改良版 - 高度逼真的人类情感文本生成语音工具(TTS)本地一键整合包下

先介绍下ChatTTS 和之前发布的 Fish Speech 类似&#xff0c;都是免费开源的文本生成语音的AI软件&#xff0c;但不同的是&#xff0c;ChatTTS测试下来&#xff0c;对于人类情感语调的模仿&#xff0c;应该是目前开源项目做的最好的&#xff0c;是一款高度接近人类情感、音色、…