Kotlin/Native 重写 Python 程序实践与思考

Viewed 0

上周花了5天时间用 Kotlin/Native 重写了一个 Python 程序,简单记录一下想法。

Kotlin 2.0

Kotlin 近期发布了 2.0,新的 K2 编译器也正式发布了!我在本地(XPS 17)上做了简单的测试,编译一个 Kotlin/Native 的 Hello World 程序:

package me.zzp

fun main() {
    println("Hello world!")
}

编译命令是:

./gradlew --parallel linkDebugExecutableNative
./gradlew --parallel linkReleaseExecutableNative

对比结果如下:

Debug Release
首次编译时间 1s 9s
增量编译时间 0.9s 6s
文件尺寸(strip前) 5.7MB 452KB
文件尺寸(strip后) 964KB 320KB

这个文件尺寸实在太吸引我了!而且编译时间也能忍受。正好最近有个 Python 程序需要升级,于是决定用 Kotlin/Native 重写。

Python项目重写

待重写的 Python 程序是一个数据同步工具,可以看作是一款轻量级的 ETL:负责在多张多维表格、三方系统之间相互同步数据,以及一些简单的数据处理。数据的获取与提交方式都是调用相应系统的 HTTP/S 接口。

程序一开始逻辑很简单:每新增一家需要对接接口的下游机构,就新建一张与之对应的多维表格;然后定时扫描多维表格,当新增了数据后,就调用机构接口提交数据;同时调用机构数据状态查询接口,把数据最新状态同步回多维表格中。

最近上游机构也提出要与我们系统对接;并且内部还需要对部分上游推送的数据做审核、筛选,然后分配给指定的下游;接着再从下游同步最新状态回来,同时推送给上游。即数据流形成了从“上游系统”到"数据池"、再到“下游系统”、再回到"数据池",最后回到“上游系统”这样的回路。

数据流转过程大同小异,无非是从数据源 A 中查询出符合条件的记录,然后保存到数据源 B 中。我转念一想,如果数据源都变成关系数据库的表,就能很方便地用 SQL 在表之间同步数据了:

select
  字段1,
  字段2,
  ...
from
  数据源A
where
  ...
into
  数据源B

Kotlin DSL

比较简便的方式是把这些 HTTP 接口映射成 PostgreSQL 或 H2 Database 的 Foreign Table,然后用 SQL 去查询、处理和保存数据。但为了折腾 Kotlin/Native,我打算自定义一套类 SQL 的 DSL,提供 selectinsertupdatedelete 四个 CRUD 操作。效果如以下代码所示:

select(
  上游编号,
  跟进状态,
  顾问备注
).from(
  张泽鹏的客户
).where(
  跟进状态 ne "未添加"
).map {
  Record(
    id = it[上游编号],
    跟进状态 to it[跟进状态],
    顾问备注 to it[顾问备注],
    是否有效 to (it[跟进状态] != "无效客户"
            && it[跟进状态] != "同行"),
    更新时间 to now,
  )
}.updateTo(线索池)

上述代码从“张泽鹏的客户”这张多维表格中同步跟进状态到“线索池”这张表,并且是合法的 Kotlin 代码,这可比之前用 for 循环、gettersetter 美观多了。实现这套 DSL 的过程遇到不少坎坷,我采用了以下这些功能或类库:

  • Ktor CIO Server:HTTP 服务端。开放 RESTful 接口供上游渠道方调用。
  • Ktor Curl Client:HTTP 客户端。调用飞书多维表格和三方系统的接口。
  • Kotlinx Serialization:序列化和反序列化 JSON 数据。
  • Kotlinx Coroutines:用协程实现每隔 1 分钟轮询功能。
  • cinterop 调用 OpenSSL:用于数据 AES ECB 加密。
  • platform.posix.*:读写本地文件。用于缓存飞书开放平台等 Access Token。

整体体验都还不错,虽然文档比较欠缺,但借助 Kimi 等 AI 工具,也能快速得到需要的答案。唯一让我沮丧的是:当加入 cinterop 调用 OpenSSL 后,编译时间显著增加,需要花费 30 多秒,导致无法愉快地边修改边运行测试,比较影响开发效率。

我的感受

我关注 Kotlin/Native,是为了找一门语法更加现代的编程语言代替 C/C++ 做日常开发。就目前的体验,我认为 Kotlin/Native 之于 C/C++,犹如当年 CoffeeScript 之于 JavaScript,前者之于后者都只限于编程语言层面的改进,属于自己的类库生态都还不成熟。

对于那些需要依赖 C/C++ 生态库,但又希望用更优秀的编程语言来开发的,我觉得 Kotlin/Native 是一个有吸引力的选择,用 cinterop 调用 C/C++ 库非常简单;但如果没有 C/C++ 的包袱,是从零开始的新项目,我会更倾向于用 Rust。

0 Answers