快速开始Getting started

从零到一条消息被发出去、又被读回来。

From nothing to a message produced and read back.

前置条件Prerequisites

必须指定 native 目标 moonkafka 的 socket 实现只支持 MoonBit native 目标,两个包都声明了 supported_targets = "+native"。省略 --target native 会在构建期失败。
The native target is required moonkafka's socket layer supports the MoonBit native target only, and both packages declare supported_targets = "+native". Omitting --target native fails at build time.

启动本地 KafkaStart a local Kafka

仓库附带的配置与 moonkafka 主仓库一致:单节点、KRaft 模式、监听 9092,容器名为 moonkafka-demo-kafka,镜像 apache/kafka:4.3.0。

The bundled configuration matches the moonkafka repository: a single node in KRaft mode listening on 9092, container moonkafka-demo-kafka, image apache/kafka:4.3.0.

docker compose -f docker-compose.kafka.yml up -d

容器自带健康检查,每 5 秒执行一次 kafka-topics.sh --bootstrap-server localhost:9092 --list, 命令成功即视为就绪。等它就绪后创建演示主题:

The container healthchecks itself every 5 seconds by running kafka-topics.sh --bootstrap-server localhost:9092 --list; the first success means it is ready. Once it is, create the demo topic:

docker exec moonkafka-demo-kafka /opt/kafka/bin/kafka-topics.sh \
  --bootstrap-server localhost:9092 \
  --create --if-not-exists \
  --topic events \
  --partitions 3 \
  --replication-factor 1
主题必须先存在 两个子命令都不建主题。对不存在的主题执行 produce 会失败, consume 同样如此。请先按上面的命令建好主题,或让 broker 的自动建主题 策略处理。
The topic must already exist Neither subcommand creates topics. Producing to a topic that does not exist fails, and so does consuming. Create it first as above, or let the broker's auto-create policy handle it.

构建Build

moon build --target native

产物是 native 调试构建的可执行文件,路径为 _build/native/debug/build/cmd/main/main.exe。集成测试默认直接执行这个 二进制,因此它也是 harness 期望的路径。

This produces the native debug executable at _build/native/debug/build/cmd/main/main.exe — the same path the integration test executes by default.

跑通第一条消息A first round trip

先在终端 A 启动 consumer。consume 会持续轮询,直到你用 Ctrl-C 停掉它,或者用 --max-messages 让它读够 n 条后正常退出。

Start the consumer in terminal A first. consume keeps polling until you stop it with Ctrl-C, or until --max-messages lets it exit cleanly after n records.

终端 ATerminal A
moon run --target native cmd/main -- consume events

它会先打印一行说明,然后开始输出记录:

It prints one line describing the session, then starts emitting records:

consuming topic "events" from 127.0.0.1:9092; press Ctrl-C to stop
offset=0 timestamp=1757923200000 key=demo-key value=Hello from MoonBit

然后在终端 B 发送一条消息:

Then send a message from terminal B:

终端 BTerminal B
moon run --target native cmd/main -- produce events "Hello from MoonBit" demo-key

producer 等待 broker 确认后,打印它写入的偏移量:

The producer waits for the broker to acknowledge and prints the offset it wrote to:

sent to topic "events" at offset 0
只想读一条就走 加上 --max-messages 1,consumer 打印完这一条就正常退出(退出码 0), 适合写脚本。这也是集成测试用的方式。
Read one record and exit Add --max-messages 1 and the consumer exits cleanly (code 0) right after printing that record — handy in scripts, and exactly what the integration test does.

用 make 跑Driving it with make

Makefile 把日常命令收成目标。直接运行 make 会打印目标列表。

The Makefile collects the day-to-day commands into targets. Running bare make prints the list.

目标作用
make check类型检查,不做代码生成——最快的反馈
make check-strict同上,但把警告视为错误;CI 用的就是它
make test运行测试套件(native)
make build构建 native CLI
make fmt就地格式化 MoonBit 源码
make fmt-check只检查格式,不写入;有文件会被改动即失败
make cicheck-strict + build + test + fmt-check
make itest端到端集成测试:容器起 Kafka 再驱动真实 CLI
make itest-plan只打印集成测试将要执行的命令,不启动任何东西
make kafka-up用 compose 启动 Kafka
make kafka-down停止那个 Kafka
make run-producer发送一条消息:make run-producer VALUE="hello"
make run-consumer消费主题直到 Ctrl-C
make clean删除构建产物
TargetWhat it does
make checkType-check without code generation — the fast feedback loop
make check-strictThe same, but warnings are errors; what CI gates on
make testRun the test suite (native)
make buildBuild the native CLI
make fmtFormat MoonBit sources in place
make fmt-checkVerify formatting without writing; fails if any file would change
make cicheck-strict + build + test + fmt-check
make itestEnd-to-end test: start Kafka in a container, drive the real CLI
make itest-planPrint what the integration test would run, without starting anything
make kafka-upStart Kafka via compose
make kafka-downStop that Kafka
make run-producerSend one message: make run-producer VALUE="hello"
make run-consumerConsume the topic until Ctrl-C
make cleanRemove build artifacts

持续集成Continuous integration

.github/workflows/ci.yml 在 push(main、develop) 和每个 pull request 上依次跑上面这几个目标:检查、构建、测试、格式检查。 集成测试不在这里跑——它需要容器运行时和一个真实 broker。

.github/workflows/ci.yml runs the targets above in order — check, build, test, formatting — on every push to main or develop and on every pull request. The integration test is not part of it: that one needs a container runtime and a live broker.

可覆盖的变量Overridable variables

所有变量都可以在命令行上覆盖,例如 make run-producer VALUE=hi TOPIC=orders。

Every variable can be overridden on the command line, e.g. make run-producer VALUE=hi TOPIC=orders.

变量默认值说明
MOONmoonmoon 可执行文件
TARGETnative构建目标
PKGcmd/main要运行 / 构建的包
TOPICevents演示主题
HOST127.0.0.1broker 地址
PORT9092broker 端口
KEYdemo-keyrun-producer 的消息 key
STARTearliestrun-consumer 的起始位置
RUNTIME自动探测(podman 优先)kafka-up / kafka-down 使用的引擎
ITEST_RUNTIMEdockermake itest 固定使用的引擎
ITEST_FLAGS空追加给集成测试的参数,如 --keep
VariableDefaultMeaning
MOONmoonThe moon executable
TARGETnativeBuild target
PKGcmd/mainPackage to run or build
TOPICeventsDemo topic
HOST127.0.0.1Broker host
PORT9092Broker port
KEYdemo-keyMessage key for run-producer
STARTearliestStart position for run-consumer
RUNTIMEauto-detected (podman first)Engine used by kafka-up / kafka-down
ITEST_RUNTIMEdockerEngine make itest is pinned to
ITEST_FLAGSemptyExtra flags for the integration test, e.g. --keep
RUNTIME 与 ITEST_RUNTIME 是两回事 RUNTIME 只影响 kafka-up / kafka-down 这两个手动开关,默认走自动探测。ITEST_RUNTIME 只影响 make itest,默认固定 docker——因为集成测试自己的探测会 逐个候选尝试并打印「不可用」的中间日志。两者的原因见 集成测试。
RUNTIME and ITEST_RUNTIME are separate RUNTIME only affects the manual kafka-up / kafka-down helpers and auto-detects by default. ITEST_RUNTIME only affects make itest and is pinned to docker, because the harness's own probe walks its candidates and logs each miss. See Integration test for why.

便捷目标Convenience targets

make run-producer VALUE="hello" TOPIC=events KEY=demo-key
make run-consumer TOPIC=events START=latest

run-producer 在缺少 VALUE 时会打印用法并以退出码 2 结束。

run-producer prints its usage and exits 2 when VALUE is missing.

停止 KafkaStop Kafka

docker compose -f docker-compose.kafka.yml down