- 后端
- 即时通讯
【免费下载链接】dendrite
Dendrite is a second-generation Matrix homeserver written in Go!
本指南以 Dendrite(用 Go 编写的第二代 Matrix 联邦式服务器)官方开发文档 docs/development.md 为骨架,系统讲解面向开发者的一整套工作流:如何通过内置 pprof 剖析 CPU/内存占用、如何借助 Docker 化 SyTest 运行集成测试并维护白名单、如何生成与合并测试覆盖率,以及如何规范地提交代码贡献。读完本文,你将掌握 Dendrite 性能问题定位、集成测试驱动开发与覆盖率评估的完整实操能力,并了解这些流程在仓库源码中的真实落点。
开发文档体系概览
docs/development.md 是 Dendrite 文档中"开发(Development)"章节的入口页,其下包含四个独立子文档,分别面向开发者的不同任务场景:
| 子文档 | 核心主题 | 适用场景 |
|---|---|---|
| CONTRIBUTING.md | 贡献规范、代码风格、单元测试与 CI | 准备提交 PR 的新老贡献者 |
| sytest.md | SyTest 集成测试的运行与白名单维护 | 验证功能修复是否让更多规范测试通过 |
| PROFILING.md | 内置 pprof 性能剖析 | 排查 CPU/内存资源占用过高问题 |
| coverage.md | 单元测试、SyTest、Complement 覆盖率统计 | 评估测试覆盖度与回归风险 |
下文按"贡献入门 → 性能剖析 → 集成测试 → 覆盖率"的顺序展开,并在每部分结合仓库源码与脚本给出可验证的实现细节。
贡献规范:Dendrite 接受什么样的代码
本节内容对应 docs/development/CONTRIBUTING.md。
贡献类型与取舍原则
Dendrite 是一个小团队维护的大型项目,因此不会无条件合并所有功能,即使某个功能没有 bug 且确实有用,团队也需为其承担长期维护责任。当前明确的接受边界如下:
- 始终接受:bug 修复、安全修复(安全漏洞请先通过
security@matrix.org负责任地私下披露,再创建 Pull Request); - 有条件接受:文档修复(前提是不添加会过时的额外说明,如示例配置、shell 命令);性能修复(不显著增加维护负担);既有功能上的小型可维护扩展;缺失会损害生态的功能(如垃圾与滥用治理);纯测试改动(提升覆盖率或覆盖棘手代码路径);
- 可能不被接受:配置或 CLI 变更,尤其是扩大整体配置面的改动;
- 短期内不太可能进入主线:新的 MSC 实现、不在 Matrix 规范中的新功能。
Sign-off(DCO)要求
所有贡献者必须按照 Developer Certificate of Origin 签署贡献,即在 PR 或提交信息中加入:
Signed-off-by: Full Name <email address>注意:只能接受法律可识别姓名(如政府证件上的姓名或惯常法定姓名)的签署,不接受笔名、别名或匿名贡献。如希望私下签署(不在公开 PR 上暴露全名),可将签署声明与 PR 链接邮件至 Matrix.org Foundation 的dco@matrix.org,一次私密签署后未来贡献无需重复操作。
代码风格、注释规范与 lint
- 格式化:遵循
gofmt、goimports等工具的输出,提交前务必运行其中一个格式化器; - 注释:注释应解释代码为什么这样做;对不显然的语句补充说明。仓库内使用三类可检索的特殊标记(这也是全仓库的通用惯例,例如 internal/log.go 中就有此类注释):
// TODO:—— 未来可能需要复查、重写或重构的位置;// FIXME:—— 已知待修复的未解决 bug;// NOTSPEC:—— 行为刻意偏离 Matrix 规范之处,并说明原因。
- Lint:使用 golangci-lint 执行:
golangci-lint run确认为误报时可用// nolint:注释临时压制,但应尽量避免。
单元测试与数据库配置
仓库单元测试使用标准 Go testing 包,并配合自有test包中的辅助函数(见 test/db.go)。运行方式:
DENDRITE_TEST_SKIP_NODB=1 go test --race ./...该命令只跑 SQLite 数据库测试。DENDRITE_TEST_SKIP_NODB=1的作用在源码中清晰可见:test/db.go 通过var Required = os.Getenv("DENDRITE_TEST_SKIP_NODB") == ""决定无数据库环境下是直接t.Fatalf失败还是t.Skipf跳过——设置该变量后,没有本地数据库的测试会被跳过而不是报错。
若需同时执行 Postgres 测试,需要本地安装 Postgres(脚本会调用createdb)或提供远程/容器化实例,通过以下环境变量配置连接:
POSTGRES_USER=postgres POSTGRES_PASSWORD=yourPostgresPassword POSTGRES_HOST=localhost POSTGRES_DB=postgres # the superuser database to use持续集成(CI)与本地预检
PR 提交后 GitHub Actions 会自动运行 CI:覆盖不同 Go 版本、完整 HTTP API 与两种数据库引擎(SQLite/Postgres),并执行单元测试及 Complement、SyTest 两套集成测试。所有 CI 任务通过是合并的前提;若认为测试本身有问题,欢迎提交修复测试的 PR。
在本地复现 CI 检查以节省排队时间,可运行:
./build/scripts/build-test-lint.sh该脚本会依次完成构建、lint 与开启竞态检测的go test ./...;注意 lint 阶段会消耗较多 CPU 和内存。代码构建通过后,再按本文后续的 SyTest 指南 运行集成测试,确认没有破坏既有行为且存在新通过的测试。
新手可先找
good first issue标签的议题入手,熟悉后转向help wanted标签;也请熟悉 Dendrite 的老贡献者把good first issue留给新人。
性能剖析:用内置 pprof 定位 CPU 与内存瓶颈
本节内容对应 docs/development/PROFILING.md。
启用剖析器
当 Dendrite 出现 CPU 或内存占用过高时,可使用内置剖析器定位问题。Dendrite 内置了 Go 标准工具链中的pprof,通过PPROFLISTEN环境变量指定监听地址与端口来启用:
PPROFLISTEN=localhost:65432 ./bin/dendrite ...其实现位于 internal/log.go:SetupPprof()读取PPROFLISTEN,非空时通过http.ListenAndServe(hostPort, nil)在独立端口启动 pprof HTTP 服务(使用 Go 的 DefaultServeMux)。该函数在 cmd/dendrite/main.go 及各 demo 程序入口(如 cmd/dendrite-demo-pinecone/main.go、cmd/dendrite-demo-yggdrasil/main.go)中统一调用,因此无论哪种形态的 Dendrite 进程都支持该开关。
启用成功后,启动日志会出现类似以下的一行(源码中对应logrus.Warn("Starting pprof on ", hostPort)):
WARN[2020-12-03T13:32:33.669405000Z] [/Users/neilalexander/Desktop/dendrite/internal/log.go:87] SetupPprof Starting pprof on localhost:65432下文示例均以PPROFLISTEN=localhost:65432为前提,实际部署请按需调整。
剖析 CPU 使用
调用profile端点并按需指定采样时长:
http://localhost:65432/debug/pprof/profile?seconds=30该请求会持续阻塞指定的秒数,采集这段时间内 Dendrite 各函数的 CPU 占用,结束后返回剖析结果。
方式一:用 Go 工具链交互查看。安装 Go 后可直接启动 pprof,-http=参数会启动一个提供可视化界面的本地 Web 服务器:
go tool pprof -http=localhost:23456 http://localhost:65432/debug/pprof/profile?seconds=30随后浏览器访问http://localhost:23456查看可视化结果,其中 View 菜单的Flame Graph(火焰图)尤其有用,可交互式查看按占比分层的 CPU 使用分布。
方式二:用 curl 下载剖析文件。无需 Go 工具链,仅需捕获结果转交给他人分析:
curl -O http://localhost:65432/debug/pprof/profile?seconds=30命令会阻塞指定的秒数完成采集,生成名为profile的文件,可将其直接发送给其他人。
剖析内存使用
调用heap端点即可:
http://localhost:65432/debug/pprof/heap该请求几乎立即返回。
方式一:Go 工具链交互查看:
go tool pprof -http=localhost:23456 http://localhost:65432/debug/pprof/heap在浏览器可视化界面中,Sample 菜单可切换四种不同的内存剖析视角:
| 采样模式 | 含义 | 典型用途 |
|---|---|---|
inuse_space | 每个函数当前实际占用的堆内存 | 诊断内存占用过高时通常最有用 |
inuse_objects | 每个函数当前持有的堆对象数量 | 分析对象数量层面的占用 |
alloc_space | 每个函数累计分配的内存(可能已被回收) | 观察分配热点 |
alloc_objects | 每个函数累计执行的分配次数(可能已被回收) | 观察高频分配路径 |
同样可在 View 菜单中选择 Flame Graph 查看内存占用的交互式火焰图。
方式二:curl 下载堆剖析文件:
curl -O http://localhost:65432/debug/pprof/heap该命令几乎立即生成名为heap的文件,可转交他人分析。
SyTest 集成测试:从 Docker 运行到白名单维护
本节内容对应 docs/development/sytest.md。
Dendrite 使用 SyTest 作为集成测试框架,项目的整体进展可通过通过的 SyTest 用例数量来追踪。提交 PR 时,应将你的改动使之通过的新测试 ID 追加到仓库根目录的 sytest-whitelist 文件中;并非每个 PR 都必须让新测试通过,但若维护者认为你的 PR 本应让某个测试通过,可能会要求你补充该文件。
确定该添加哪些测试
官方强烈建议先在本地运行测试,通常使用 SyTest Docker 镜像。测试运行结束后,脚本会打印需要加入sytest-whitelist的测试清单。
前提:确保构建无问题,在仓库根目录执行:
go build -o bin/ ./cmd/...该命令构建所有命令二进制到bin子目录(与 docs/installation/manual/1_build.md 中的构建说明一致)。
如果你在修复标记为Are We Synapse Yet的问题,那么该 issue 下会列出修复后应加入白名单的 SyTest 清单,必须在 PR 中一并包含,否则问题不算彻底解决。仓库根目录的 are-we-synapse-yet.list 正是此类测试清单的落地文件(形如reg POST /register yields a set of flows)。
使用 SyTest Docker 镜像
官方强烈推荐使用 Docker 镜像运行 SyTest。拉取最新镜像并运行测试:
docker pull matrixdotorg/sytest-dendrite docker run --rm -v /path/to/dendrite/:/src/ -v /path/to/log/output/:/logs/ matrixdotorg/sytest-dendrite其中/path/to/dendrite/替换为 Dendrite 源码的实际路径;测试结果的 TAP 文件与 homeserver 日志输出将写入/path/to/log/output。命令输出会提示是否需要向sytest-whitelist添加测试。
调试时有用的 Docker run 选项:
-v /path/to/sytest/:/sytest/:使用本地 SyTest 仓库(/path/to/sytest)代替从 GitHub 拉取,适合加速或修改 SyTest;-v "/path/to/gopath/:/gopath":挂载本地 GOPATH,避免每次运行都重新下载依赖包;--entrypoint bash:阻止容器自动启动测试,之后需在容器内手动执行/bootstrap.sh dendrite启动;-e "DENDRITE_TRACE_HTTP=1":向服务器日志添加 HTTP 追踪。该变量在源码 internal/httputil/httpapi.go 中读取(os.Getenv("DENDRITE_TRACE_HTTP") == "1"),启用后输出每个 HTTP 请求的追踪信息;-e "DENDRITE_TRACE_INTERNAL=1":向服务器日志添加 roomserver 内部 API 追踪;-e "COVER=1":使用带插桩(instrumented)的二进制运行 SyTest,为每个服务器生成 Go 覆盖率文件;-e "RACE_DETECTION=1":以-race标志构建二进制(会显著拖慢测试运行)。
运行单个测试文件:Docker 命令支持一个位置参数指定要运行的.pl测试文件,例如:
docker run --rm --name sytest -v "/Users/kegan/github/sytest:/sytest" -v "/Users/kegan/github/dendrite:/src" -v "/Users/kegan/logs:/logs" -v "/Users/kegan/go/:/gopath" -e "POSTGRES=1" -e "DENDRITE_TRACE_HTTP=1" matrixdotorg/sytest-dendrite:latest tests/50federation/40devicelists.pl该命令挂载了本地 SyTest、Dendrite 源码与 GOPATH,启用 Postgres 后端与 HTTP 追踪,只运行联邦设备列表这一个测试文件。
仓库配套脚本:白名单的自动化校验
仓库根目录的 run-sytest.sh 是对上述 Docker 流程的脚本化封装:若../sytest目录存在则先重新构建 SyTest 镜像(可用DISABLE_BUILDING_SYTEST跳过),支持SYTEST_POSTGRES=1环境变量切换 Postgres 后端,并将日志与 Go 构建缓存持久化到../sytestout。
另一个脚本 show-expected-fail-tests.sh 解析 SyTest 的results.tap文件与白名单,找出"已通过但未列入白名单"的测试并输出错误信息(::error::The following tests passed but are not present in ...),同时检出"在白名单中但仍被标记为 expected fail"的用例,供 CI 构建注解使用。
白名单与黑名单机制共同维护测试预期:sytest-whitelist 记录应通过的测试 ID;sytest-blacklist 则记录因规范问题、偶发不稳定(flakey)、HTTP API 模式差异等原因被临时排除的测试,每条均附注释说明原因(例如# Blacklisted due to https://github.com/matrix-org/matrix-spec/issues/942、# Flakey)。
测试覆盖率:单元测试、SyTest 与 Complement 的测量与合并
本节内容对应 docs/development/coverage.md。
单元测试覆盖率
启用覆盖率运行单元测试,会生成integrationcover.log:
go test -covermode=atomic -coverpkg=./... -coverprofile=integrationcover.log $(go list ./... | grep -v '/cmd/') go tool cover -func=integrationcover.log-covermode=atomic用于并发场景下的安全插桩,-coverpkg=./...统计所有包,grep -v '/cmd/'排除命令行工具包。
SyTest 覆盖率
在 SyTest 中启用COVER=1运行(Postgres 与 SQLite 两种后端建议都跑,见上节的 Docker 参数说明):
docker run --rm --name sytest -v "/Users/kegan/github/sytest:/sytest" \ -v "/Users/kegan/github/dendrite:/src" -v "$(pwd)/sytest_logs:/logs" \ -v "/Users/kegan/go/:/gopath" -e "POSTGRES=1" \ -e "COVER=1" \ matrixdotorg/sytest-dendrite:latest # 为得到更准确的覆盖率,还需要以 SQLite 为数据库再跑一次: docker run --rm --name sytest -v "/Users/kegan/github/sytest:/sytest" \ -v "/Users/kegan/github/dendrite:/src" -v "$(pwd)/sytest_logs:/logs" \ -v "/Users/kegan/go/:/gopath" \ -e "COVER=1" \ matrixdotorg/sytest-dendrite:latest这会在每个服务器的目录下生成covdatafiles文件夹(如server-0/covdatafiles)。在 Dendrite 仓库根目录下解析这些文件:
go tool covdata func -i="$(find -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)"输出形如:
... github.com/matrix-org/util/json.go:132: MakeJSONAPI 70.0% github.com/matrix-org/util/json.go:151: respond 84.6% github.com/matrix-org/util/json.go:180: WithCORSOptions 0.0% github.com/matrix-org/util/json.go:191: SetCORSHeaders 100.0% github.com/matrix-org/util/json.go:202: RandomString 100.0% github.com/matrix-org/util/json.go:210: init 100.0% github.com/matrix-org/util/unique.go:13: Unique 91.7% github.com/matrix-org/util/unique.go:48: SortAndUnique 100.0% github.com/matrix-org/util/unique.go:55: UniqueStrings 100.0% total (statements) 64.0%(该示例为 Postgres 与 SQLite 均运行后的输出。)
注意:最后一行 total 的数字具有误导性——Dendrite 可运行于多种配置(如 sqlite/postgres),单次测试运行永远无法覆盖全部配置组合。要得到更准确的值,需要分别运行 Postgres 和 SQLite 的 SyTest(如上),并进一步剔除永远不会被测到的包与扩展 MSC(relayapi、setup/mscs):
# 若上面两个命令都执行了,用以下命令得到总覆盖率: go tool covdata textfmt -i="$(find -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)" -o sytest.cov grep -Ev 'relayapi|setup/mscs' sytest.cov > final.cov go tool cover -func=final.cov # 若只跑了 Postgres: go tool covdata textfmt -i="$(find -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)" -o sytest.cov grep -Ev 'relayapi|sqlite|setup/mscs' sytest.cov > final.cov go tool cover -func=final.cov # 若只跑了 SQLite: go tool covdata textfmt -i="$(find -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)" -o sytest.cov grep -Ev 'relayapi|postgres|setup/mscs' sytest.cov > final.cov go tool cover -func=final.covComplement 覆盖率
Complement 的覆盖率获取步骤稍多。首先构建与 Complement 兼容的 Docker 镜像(在 Dendrite 仓库内执行):
docker build -t complement-dendrite -f build/scripts/Complement.Dockerfile .克隆 Complement 到任意目录:
git clone https://github.com/matrix-org/complement.git cd complement接着创建测试结束后的回调脚本posttest.sh并赋予执行权限(chmod +x posttest.sh),内容如下:
#!/bin/bash mkdir -p /tmp/Complement/logs/$2/$1/ docker cp $1:/tmp/covdatafiles/. /tmp/Complement/logs/$2/$1/该脚本将每个容器的covdatafiles复制到类似/tmp/Complement/logs/TestLogin/94f9c428de95779d2b62a3ccd8eab9d5ddcf65cc259a40ece06bdc61687ffed3/的路径($1为容器 ID,$2为测试名)。
随后执行 Complement:
COMPLEMENT_BASE_IMAGE=complement-dendrite \ COMPLEMENT_SHARE_ENV_PREFIX=COMPLEMENT_DENDRITE_ \ COMPLEMENT_DENDRITE_COVER=1 \ COMPLEMENT_POST_TEST_SCRIPT=$(pwd)/posttest.sh \ go test -tags dendrite_blacklist ./tests/... -count=1 -v -timeout=30m -failfast=false完成后将covdatafiles文件复制回 Dendrite 仓库:
cp -pr /tmp/Complement/logs PathToYourDendriteRepository也可以单独统计 Complement 运行的覆盖率:
go tool covdata func -i="$(find /tmp/Complement -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)"合并(几乎)全部运行的覆盖率结果
将 SyTest 与 Complement 的covdatafiles都收集进 Dendrite 仓库后,可得到整体覆盖率(不含单元测试):
go tool covdata func -i="$(find -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)"实战小结:一条完整的"修 bug → 验证 → 提交"路径
把上述四个环节串起来,一个典型的开发循环是:
- 构建:
go build -o bin/ ./cmd/...,确保代码可编译; - 单元测试:
DENDRITE_TEST_SKIP_NODB=1 go test --race ./...,有 Postgres 环境时再补跑 Postgres 用例; - 本地 CI 预检:运行
./build/scripts/build-test-lint.sh,通过构建、lint 与竞态检测; - 集成测试:用
matrixdotorg/sytest-dendrite镜像运行 SyTest,必要时用-e "DENDRITE_TRACE_HTTP=1"或-e "DENDRITE_TRACE_INTERNAL=1"开启追踪辅助排错;新通过的测试追加到 sytest-whitelist; - 性能回归检查:设置
PPROFLISTEN=localhost:65432启动 Dendrite,用go tool pprof -http=...或 curl 抓取 CPU/heap 剖析,确认改动未引入资源占用异常; - 提交:按 DCO 要求附
Signed-off-by签署行,确保 CI 全绿。
如需进一步深入,可继续阅读 docs/installation 下的部署文档来搭建测试实例,并在 #dendrite-dev:matrix.org 获取开发相关的帮助。
- 后端
- 即时通讯
【免费下载链接】dendrite
Dendrite is a second-generation Matrix homeserver written in Go!
相关推荐
Sonic 贡献者开发指南:从源码构建、单元测试到集成测试的完整工作流
Sonic 贡献者开发指南:从源码构建、单元测试到集成测试的完整工作流 Sonic 是一个用 Rust 编写的快速、轻量、无模式的搜索后端( sonic ser
搜索引擎后端全文检索cjdns 开发者贡献指南:从 Rust 互操作、FFI 代码生成到代码规范、测试与性能剖析的完整 HACKING 手册
cjdns 开发者贡献指南:从 Rust 互操作、FFI 代码生成到代码规范、测试与性能剖析的完整 HACKING 手册 cjdns 是一个基于公钥密码学分配地
网络网络安全kOps 开发者实战指南:从源码构建、测试到贡献的完整工作流
kOps 开发者实战指南:从源码构建、测试到贡献的完整工作流 本文基于仓库根目录 AGENTS.md https://link.gitcode.com/i/e2
云原生集群管理运维IaC
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考