PlantUML 大图拆分:subgraph、package、!include 让 1000 节点不卡顿

puml.online

一张大图看着清晰,但画图工具叫苦不迭——100 节点是分水岭。本文整理让 PlantUML 大图保持可读、渲染顺畅的「拆分哲学」。

100 节点分水岭

经验值:

节点数 状态
<30 顺畅(<200ms)
30-80 偶发拥挤(500ms-1s)
80-150 边缘卡顿(1s-3s)
>150 Graphviz 卡死或崩

核心原则:超过 80 个节点就开始分层(按 responsibility 分),超过 150 节点必须拆图

第一招:subgraph 和 package

package = 矩形包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@startuml
package "Frontend" {
[WebApp]
[AdminPanel]
[DocsSite]
}

package "Backend" {
[AuthService]
[UserService]
[OrderService]
}

WebApp --> AuthService
WebApp --> OrderService
AdminPanel --> UserService
@enduml

package 实际是带边框的命名空间——视觉上把节点聚类。

subgraph = 灵活分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@startuml
skinparam rectangle {
BackgroundColor<<infra>> #ECECFF
BackgroundColor<<biz>> #FFECEC
}

rectangle "Load Balancer" <<infra>> as lb
rectangle "App Node 1" <<biz>> as n1
rectangle "App Node 2" <<biz>> as n2

subgraph cluster_db {
label "Database Cluster"
rectangle "Primary" <<infra>> as p
rectangle "Replica 1" <<infra>> as r1
}

lb --> n1
lb --> n2
n1 --> p
n2 --> p
n1 .> r1 : fallback
@enduml

subgraph ... end 是 Graphviz 的「cluster」,PlantUML 完整支持——能用 <<stereotype>> 染色。

第二招:!include 把图拆成多个文件

拆分原则

按模块拆——但模块边界要清晰

1
2
3
4
5
6
7
8
9
/docs/diagrams/
├── system.puml <- 顶层图
├── service/
│ ├── auth.puml <- 20-30 节点
│ ├── order.puml
│ └── billing.puml
└── shared/
├── nodes.puml <- 通用节点定义
└── styles.puml <- 主题

写法一:!include 子图文件

shared/styles.puml:

1
2
3
4
!pragma layout: "elk"
skinparam nodesep 40
skinparam ranksep 50
skinparam defaultFontName "Noto Sans CJK SC"

shared/nodes.puml(标准节点):

1
2
3
!define SERVICE(name, label) rectangle "==label" <<service>> as name
!define STORE(name, label) database "label" as name
!define QUEUE(name, label) queue "label" as name

system.puml:

1
2
3
4
5
6
7
8
9
@startuml system
!include shared/styles.puml
!include shared/nodes.puml
!include service/auth.puml
!include service/order.puml

auth_service --> order_service
auth_service --> postgres
@enduml

service/auth.puml:

1
2
3
4
5
SERVICE(auth_service, "AuthService")
SERVICE(sso_provider, "SSO")
STORE(postgres, "UserDB")
auth_service --> postgres
auth_service --> sso_provider

注意:!include 与渲染位置

PlantUML 默认 !include 路径是「当前文件目录」+ !include <stdio>。在 VS Code 插件里要配 plantuml.includepaths,命令行要用 -I 指定:

1
plantuml -I docs/diagrams/shared -tsvg system.puml

第三招:!pragma layout: “elk”

Graphviz 的 dot 算法是「中心辐射式」——节点一多就开始竞争中心位置。ELK (Eclipse Layout Kernel) 是另一个算法,适合层次架构图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@startuml
!pragma layout: "elk"

package "Frontend" {
[Web] [Mobile]
}
package "Gateway" {
[API GW] [Auth]
}
package "Backend" {
[Order] [Payment] [User]
}

Web --> API GW
Mobile --> API GW
API GW --> Auth
API GW --> Order
API GW --> Payment
Order --> User
@enduml

需要 PlantUML ≥ 1.2024.x,并安装 elk.js

1
2
# 下载 elk.js 到 plantuml 加载路径
wget https://www.eclipse.org/elk/downloads/elk-0.7.2.js -O ~/plantuml/elk.js

实测:150 节点的微服务架构图用 dot 算法 8s 出图,用 elk 算法 2s 出图,视觉层次更清晰(默认按数据流向分层)。

第四招:!pragma useVerticalIf 或整理连接

节点关系一片混乱是导致渲染慢的另一个原因:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
' 反例:13 个节点随便连线
A -> B
A -> C
A -> D
B -> E
C -> E
C -> F
D -> F
E -> G
F -> G
G -> H
H -> I
I -> J
J -> K
' Graphviz 找不到优化布局,反复尝试

修正:把所有节点塞进 package / subgraph 让 Graphviz 知道空间结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package "Inputs" {
[A] [B] [C] [D]
}
package "Processing" {
[E] [F]
[G] [H]
}
package "Outputs" {
[I] [J] [K]
}

A --> E
A --> F
B --> E
B --> G
C --> F
C --> H
D --> G
D --> H
' 给 Graphviz 提示:分层
E --> I
F --> I
F --> J
G --> J
H --> K

Graphviz 看到明显的 package 层次,渲染时会优先按 package 切割 subgraph,耗时从 12s 掉到 1.5s。

第五招:!theme 切换

1
!theme cyborg

让 PlantUML 把整张大图用统一主题渲染——视觉层级从颜色呈现,不用 Graphviz 拉 nodesep 到 100+ 还挤一起。

第六招:分页渲染

VuePress / Hexo / 文档站才用得着。生成一张图后用 cut/ 拆分:

1
2
<!-- 大图被 plantuml 自动切成多张分页图,旁边的 nav 自动更新 -->
<div class="plantuml-multipage">...</div>

这是 plantuml-multipage 插件做的事。装上后 {% plantuml %} 渲染时自动嵌「Page 1/10」的小 nav。

性能优化 checklist

  • 节点按业务责任放进 package
  • 跨 package 关系明确(避免双向全连接)
  • 100 节点时 !pragma layout: "elk"

  • 复用节点用 !include 而不是复制粘贴
  • 中文统一 skinparam defaultFontName
  • 大图用 server 模式(避免 plantuml.com 跨境)
  • 文档站的「超大图」用 plantuml-multipage 分页
  • 复杂流程时序尽量**改用「时序图」+「状态机图」**组合,而不是一坨 component 图

一个真实的案例:「我们电商的中台架构图」

旧版:300 节点一坨 component 图 + 600 条边,渲染 30+ 秒,Arch 同事根本不开。

新版:

  1. core.puml / fulfillment.puml / marketing.puml / data.puml 4 张顶级图
  2. 每张图用 !include 引入各自子模块(30-50 节点)
  3. 每张图顶部 !pragma layout: "elk" + 标准 styles.puml
  4. 文档站用 plantuml-multipage 嵌入

总图阅读顺序:

1
2
3
4
点击看 core(顶层)
├── 引入 fulfillment 子模块(独立图)
├── 引入 marketing 子模块(独立图)
└── 引入 data 子模块(独立图)

每张图独立 1-3s 渲染,总浏览体感 <8s,比之前单张 30s 强很多。

小结

  • 大图最大瓶颈是 Graphviz 布局算法
  • package + subgraph + !include + !pragma layout elk 是 4 件套
  • 拆图比优化图更有效:80+ 节点就考虑拆

下一步

  • 标题: PlantUML 大图拆分:subgraph、package、!include 让 1000 节点不卡顿
  • 作者: puml.online
  • 创建于 : 2026-07-30 10:12:00
  • 更新于 : 2026-08-14 21:34:29
  • 链接: https://puml.online/blog/plantuml-large-diagram/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。