代码绘制架构图:D2入门实战,版本控制友好的Diagram as Code工具
1. 一次“画架构图”的深夜崩溃,让我换了工具
做后端和系统设计这几年,画架构图一直是个躲不掉又让人头疼的活儿。早期用Visio画,电脑卡倒是次要的,最绝望的是图一复杂,连线满天飞,改一个模块要把周边线条全部拖一遍;后来换到在线白板和传统绘图软件,协作是方便了,但版本管理基本靠手动另存为“最终版3.0再改”,代码仓库里想审阅一张架构图的变更记录,几乎不可能。
直到有一次,我在GitHub上看到团队维护的一个基础设施仓库,所有架构图不是图片,而是一行行类似代码的纯文本。改动靠commit,评审靠diff,渲染交给命令行——当时我心里就在想:这不是把架构图画成了“代码”吗?
这正是本文要聊的主角:一个GitHub上2.5万+标星的开源项目D2(Diagram as Code的缩写,项目地址是d2lang/d2)。它把架构图的绘制从“拖拽鼠标”彻底切换到“写声明式代码”的模式。你用一个简单的DSL描述组件和连接关系,D2负责把它们渲染成一张结构清晰的矢量架构图,并能直接导出PNG、SVG、PDF,甚至支持类似PPT的动画演示。
这篇文章不是官方文档的翻译,也不是标题党的吹捧。我会从实际使用的角度,把D2的定位、语法、踩坑、适用场景讲透彻,拿它跟Visio做一轮客观对比,并附上一套可以直接照抄的实操流程。如果你画图是为了在投标PPT里“好看”,那Visio可能仍然是答案;但如果你画图是要沉淀进代码仓库、伴随系统演进不断维护,D2会是更值得认真对待的一个方向。
2. D2的核心设计思路:为什么要把画图变成写代码
2.1 架构图的第一性原理:图是文本的投影
先说结论:大部分架构图,本质上不应该被存储为“图片”,而应该被存储为“文本”。
Visio把图存储为复杂的二进制或XML画布文件,里面记录了每个形状的位置、线条的锚点、样式的覆盖值。这是“所见即所得”的代价:你保存的不是逻辑关系,而是某一次手工排版后的物理坐标。一旦模块数量超过20个,或者一个人画完另一个人接手,整张图的维护成本就会迅速失控。
D2选择的路线是“图即代码”:你在.d2文件里写的东西,就是这张图的结构化定义。组件之间有什么关系,一眼就能从文本里读出来;布局的细节全部交给内置布局引擎TALA(TALA是D2自研的自动布局算法)去计算。手动微调位置不是没有,但它是一个可选项,而不是你保存文件的默认形态。
这套思路和Graphviz、Mermaid是同一个流派,但D2在设计上有几个非常明显的取舍:语法学成本极低,关键字少到一页纸能写完;DSL纯净,不会和HTML、Markdown的语法互相污染;默认输出观感现代,TALA布局引擎铺出来的图,节点间距均匀,连线基本不交叉,不用像Graphviz那样反复调rankdir和constraint。
2.2 与Mermaid和Visio的定位差异
选型的时候,首先要认清一个事实:D2不是要“替代Visio”这么简单,它替代的是“在Visio里手工排版架构图”这个动作。
Mermaid擅长的是流程图、时序图、甘特图这类“自带时间或顺序语义”的图,渲染效率高,适合内嵌到Markdown文档里。但Mermaid的大节点布局一直不太稳定,节点多了会挤成一团,想精细控制样式反而费力。D2的强项是“面向软件系统的架构图、拓扑图、组织架构图”,它支持分组、集群、语义化标签,TALA布局引擎对这类“树状和网状混合结构”的处理明显更平整。
Visio则完全是另一个物种。它是微软Office体系里的重型矢量绘图工具,模板丰富、形状库庞大、导出交互PDF能力无人能及。问题也很明显:授权付费、跨平台受限(macOS上要装虚拟机)、不能进Git做文本级diff、图纸样式过度依赖个人排版习惯。换句话说,Visio画“一次性交付”的图很快,画“长期维护”的图很累。
D2恰好切在两者中间。它比Mermaid更擅长软件架构场景,比Visio更适合团队协作和版本化管理。它不在每个维度都赢,但在“代码仓库里的架构图”这个细分场景里,它几乎没有对手。
3. 上手D2:从安装到渲染出第一张图
3.1 安装方式与版本选择
D2的安装非常朴素,官方推荐的方式有三种。
macOS用户直接走Homebrew:
Linux用户用安装脚本,或者直接拖官方release里的二进制包:
Windows用户建议直接用WSL装Linux版,或者从GitHub的releases页面下载Windows二进制,手动把可执行文件放进PATH。我个人在Windows上更推荐WSL,因为D2的插件生态和命令行工具在类Unix环境里协作更顺。
装完以后验证一下:
如果要导出PDF,D2其实不内置PDF渲染,它内部先做SVG再转PDF,所以系统里要有对应工具。导出PNG则依赖本地安装的Inkscape或cairosvg,这一步很多人会漏,提前装好能少折腾半天:
3.2 十秒生成第一张图:Hello Architecture
D2的语法直观到不需要专门学。创建一个hello.d2文件,写入以下内容:
然后运行:
默认会在同目录生成hello.svg,浏览器打开就是一张能无限缩放的矢量架构图。三个节点、两条连线、一条带标签的链路,整个过程从创建文件到出图十秒以内。
这里要说一下D2的极简主义体验:它不会像Visio那样给你弹一堆模板和形状库让你选,也不存在工具栏和属性面板。你面对的是一个空白文本文件,写什么得什么。刚开始可能有点不习惯,但适应之后反而更专注——你在思考系统结构,而不是在思考怎么连线更好看。
3.3 输出格式工具箱
D2支持多种导出格式,常用的是这几个:
| 命令 | 输出格式 | 典型用途 |
|---|---|---|
d2 input.d2 |
SVG | 矢量图,网页嵌入、看图放大不糊 |
d2 --format=png input.d2 |
PNG | 文档插图、微信群发送 |
d2 --format=pdf input.d2 |
汇报材料、打印 | |
d2 --format=pptx input.d2 |
PPTX | 单页PPT直接插入 |
d2 --watch input.d2 |
实时刷新 | 边改代码边看效果 |
--watch模式强烈推荐。你启动d2 --watch diagram.d2后,D2会监听文件变化,每次保存自动重新渲染SVG,浏览器配合自动刷新,体验接近写前端热更新。调整架构图的速度会比在Visio里拖拽快得多,因为改动一行代码就能完成模块重命名,而不是去画布上一个个形状地找。
4. D2语法精讲:这些功能才是架构图的刚需
4.1 形状、连接线、标签的基础组合
D2的底层模型并不复杂,核心就是三类元素:形状(shape)、连接线(edge)、标签(label)。
形状就是一个个节点。默认无障碍写法是直接写名称:
冒号右边的内容会被解析为图表内部的描述文字,渲染出来是这个形状的主要标签。接口和类也能通过shape关键字定义,比如:
带形状的节点嵌在花括号里,这是D2扩展节点类型的方式。目前原生支持的形状包括矩形(默认)、圆角矩形(shape: rectangle)、菱形(shape: diamond)、人形(shape: person)、云朵(shape: cloud)、圆柱(shape: cylinder,适合画数据库)、队列(shape: queue),基本覆盖了软件架构图常用到的视觉元素。
连接线的语法也非常直接:
->是带箭头连线,<->是双向箭头,--是无箭头连线。连线右侧用冒号跟一个标签,能同时表达链路语义。如果你需要连线上有多个文字,可以用花括号展开:
4.2 分组与集群:大型架构图的骨架
系统架构图一旦到了微服务规模,节点少说几十个,如果全部平铺在一个画布上,再智能的布局引擎也没办法理清楚层次。D2用容器容器(classes)和嵌套分组来解决这个问题,不是扁平地堆节点,而是通过容器表达系统边界,语义非常自然。
看下面这段设计:
这个嵌套结构渲染出来,是一张典型的三层架构拓扑图。外层容器自动用圆角矩形包裹内部元素,并且TALA布局引擎会在容器内做独立的子图布局,避免所有节点挤成一锅粥。
这种表达方式最打动我的一点:源代码里的缩进关系,直接映射成视觉上的层级关系。你不需要手动调整任何坐标,架构边界在哪里,系统分层怎么组织,代码里一目了然。
组之间也能拉连线。D2支持在容器上定义连线,也可以让容器内的子节点和外部节点直连:
路径里用点号连接容器名和节点名,就能精确定位到容器内部的某个具体组件。结构复杂的项目,这种路径寻址方式是语义最清晰的表达。
4.3 变量、模块与场景:复用逻辑是D2的最大底牌
写代码久了的人,最怕的是重复。D2知道这一点,所以它内置了一套成熟的模块化和复用机制。
变量支持得很实在。你可以把高频字符串抽出来:
模块导入更猛。你可以把一组容器和连线定义在另一个.d2文件里,然后在主图导入:
这样多个项目的架构图就能共享一套底座组件定义,改底座,所有引用它的图同步更新。这在多业务线团队里非常实用,基础设施团队维护一份基础架构资产,各业务团队基于它做二次扩展。
**场景(scenarios)**是D2的高级功能,用来描述“同一套系统在不同状态下的呈现”。最典型的使用方式:一张图默认画的是全部组件,然后定义异常场景、数据流场景等,不同场景里可以高亮特定节点、隐藏非相关组件。
导出的图通过d2 --scenario=异常高亮 diagram.d2渲染,就能得到同一架构的变体视图。做故障预案、线上演练复盘,这一手是真的能救命。
4.4 样式系统:架构图不代表马尾辫
很多人对“代码画图”的最大偏见是,出来的图丑。D2在样式方面下了不少功夫,它支持全局主题(theme)和细粒度的样式覆盖,并不是那种只能画黑白线框图的极简玩具。
内置主题在渲染参数里直接选:
D2官方文档里提供了多套预设主题,从冷色系到暖色系都有。比如--theme=300偏水墨灰,--theme=101是蓝色系,具体数值和效果对照,翻一下官方文档的主题专区,几分钟就能选出团队喜欢的风格。
如果觉得主题还不够,可以覆盖任意节点的样式属性:
字体、圆角、阴影、虚线样式都能单独控制。在这个维度上说,D2不输Visio,因为它支持的是“可编程的样式”,而不是“手动点选的颜色”,统一改风格只需要改一行全局配置。
5. 实战:用D2画一张微服务架构图全流程
5.1 需求边界与图形拆解
动手之前先把场景定清楚。这次目标是画一张“电商系统微服务架构图”,要素包括:客户端、API网关、注册中心、业务服务集群、消息队列、数据库、缓存、日志系统。要求能清晰表达调用关系和主链路,视觉效果不能输给投标PPT。
拆解之后,图形结构分为四层:
- 顶层:客户端和负载均衡
- 接入层:API网关 + 注册中心
- 业务层:用户服务、订单服务、支付服务、库存服务、消息服务
- 基础层:MySQL、Redis、Kafka、ELK
每层是一个容器,服务之间画连线,链路上标注协议和关键信息。
5.2 完整代码实现
下面是我在实际项目中使用的完整示例,结构经过简化,但方法通用:
保存为ecommerce.d2,执行:
TALA布局引擎会自动计算所有节点的位置和连线路由。说到这我必须给TALA正名,它默认输出的效果非常稳:同层节点水平对齐,跨层连线基本不交叉,密集区域自动加大间距。相比之下,Graphviz在复杂图上需要反复调参才能做到相近的整洁度,D2是开箱即用的。
5.3 几个重要的实战技巧
第一,用vars统一全图颜色。同一套微服务图,基础设施层的节点用同一色系,核心业务模块用另一色系,通过变量维护,改起来只动一处,全图变色。对于需要频繁做汇报版、评审版、运维版多版本图的团队,这是提效利器。
第二,连线标签要精简。架构图最忌讳每根线上都挂长篇大论。线上只标注协议或核心动作(如RPC、SQL、HTTPS),更详细的字段写进节点描述或单独的设计文档。D2的标签渲染在小字号下依然清晰,但信息过载照样会毁掉一张图。
第三,善用透明度表达主次。默认全实线全饱和,重要链路和外围链路视觉权重相同,读者一眼抓不住重点。我会把非核心依赖用虚线表示,把主要调用链路的线条加粗:
渲染完之后,打开SVG复核节点文字、连线交叉情况,确认没问题后,一张能进文档、能进代码库、能进PPT的架构图就完成了。整个流程从零到出图,“写得快一点5分钟,写慢一点10分钟”,比Visio从模板开始拖拽再手工排线节省太多时间。
6. 高级玩法:数据模型、动画与插件扩展
6.1 直接内嵌SQL定义数据模型
架构图里画数据库表结构,历来是最麻烦的。Visio里要一个一个表格形状去画字段,D2则直接支持在容器里声明SQL结构:
渲染出来的是一张标准的数据库表结构图,字段名、类型、约束条件一目了然。更重要的是,这份定义是可以被版本管理和评审的,数据库字段变更,团队做code review就能先把改动看清楚,而不需要截图来回发。
6.2 用动画做架构演进汇报
D2的动画功能是它魄力的体现,也是我见过最出圈的特性。你在图里不同组件集上定义步骤编号,D2能生成一步一帧的动画,效果是节点和连线按你的脚本逐个出现、高亮、移动。
定义方式很简单:
多个步骤可以在同一个源文件中声明,渲染时用--animate参数:
它输出的是一段SMIL动画SVG。浏览器打开,页面自动播放“请求走进系统”的完整过程。做系统方案汇报、在线答辩、技术分享时,这种演示效果比静态PPT高出一个段位,且不需要任何视频剪辑或PPT动画调试。
6.3 插件机制:D2的隐藏门槛
D2目前最受争议的一点不在核心DSL,而在插件机制。它支持官方插件和一些社区插件(比如d2-plugins仓库的一些扩展),但安装方不像开箱即用的npm那样简单,需要手动下载二进制放到指定目录。
好在官方文档对常见插件(如文本测量、LaTeX渲染、表格支持)有明确的安装指引。我给的建议是:先用核心功能,等确实需要LaTeX公式或精细文本渲染时再折腾插件,不要为了“万物齐聚”在入门阶段被环境问题劝退。
官方也提供了d2 --publish直接将图发布到D2公开的Playground分享链接,方便把图丢给同事快速预览,不用先把SVG文件传来传去。
7. D2与Visio的客观对比:到底谁适合谁
7.1 两者优劣势对照
我这两周把公司系统架构图全部用D2重画了一遍,Visio在老项目里的存量图也翻出来逐张对比过。这里给出一个尽量客观的维度对比表:
| 对比维度 | Visio | D2 |
|---|---|---|
| 学习成本 | 高,模板库和工具栏庞大 | 低,DSL一页纸基本讲完 |
| 上手速度 | 熟悉形状库+排版约半天到一天 | 写几行练手,10分钟基本入门 |
| 布局控制 | 完全手动托拽,可控性强 | 默认自动布局,手动控制有限但足够 |
| 复杂大图表现 | 节点多时连线必须手工梳理 | 自动布局稳定,几十节点不出错 |
| 版本管理与协作 | 二进制文件,diff不可读 | 纯文本,Git天然支持 |
| 跨平台 | Windows为主 | Windows/macOS/Linux全支持 |
| 富文本排版 | 强,模板化和手绘效果丰富 | 较弱,复杂排版要靠自定义样式 |
| 演示交互 | 手动做PPT动画,工作量大 | 自带SMIL动画,脚本控制十分灵活 |
这个表说明一件事:没有全能的神器,只有场景匹配的工具。
7.2 D2不适合什么场景
必须泼几盆冷水,D2不是万能的。
第一,复杂形状需求多的图请留步Visio。D2的形状库里基本都是基础几何体和少量icon,如果你想画一栋建筑、一组网络机柜、一套非常拟真的机房平面图,D2会让你的表达极其痛苦。Visio的庞大形状库依然是这领域的王者。
第二,非程序员或非技术团队用D2,初期有门槛。虽然语法简单,但“在文本文件里描述图形”这件事本身需要一点点抽象思维。市场、销售、行政部门画组织架构图,直接打开Visio或在线模板可能更顺手。
第三,极度精细的像素级排版控不了。自动布局意味着你交出了一部分“位置审美”的控制权。虽然可以手动指定坐标,但用起来跟Visio的精细对齐功能比仍然差一些。如果要画的是一张对外交付的精美网络拓扑海报,D2默认渲染色调偏简约风,需要额外调主题和样式。
7.3 什么场景无脑选D2
反过来,如果满足下面任一条件,D2大概率是更优选择:
- 系统架构图要“活”在代码仓库里,跟项目代码一起发版、一起评审
- 团队协作时频繁改架构,需要看清楚每个人具体改了哪个节点
- 有自动化需求,比如想在CI/CD流程里自动生成最新架构图
- 画图的人多少会一点代码,接受“一切皆文件”的工程师思维
我个人踩过一轮坑之后的结论:把“架构设计文档”这个领域画分成两类——一类是“给人看的永久的图”,一类是“给系统当文档的活图”。前者Visio和Figma更合适,后者D2是当前这类工具里最好的选择之一。
8. 常见问题与排查技巧实录
8.1 中文渲染乱码或字体隐形的解决方案
D2默认的字体对中文支持不如英文稳定,很多人在macOS上第一次导出PNG,发现中文字全变成了方框。问题出在系统缺少合适的CJK字体,而不是D2语法错误。解决办法很简单:
或者指定全图默认字体:
Linux环境下推荐安装Noto Sans CJK SC,Windows下用“Microsoft YaHei”。配置好之后,中文渲染清晰度跟Visio基本没有区别。
8.2 导出PNG失败怎么办
D2导出PNG不是内置功能,依赖外部的Inkscape或cairosvg。如果你运行d2 --format=png时报错,先确认这两样是否安装并加入了PATH。
不想装额外软件也有一条出路:先导出SVG,再丢给在线转换工具转PNG。但频繁操作不建议,CI流程里还是正经装好依赖更省心。
8.3 复杂的图为什么布局仍然会“打架”
TALA智能归智能,但如果你的图里容器嵌套过深、连线交叉严重,偶尔也会出现布局不够理想的情况。最直接的解决办法是拆分图:一图一主题,不要把注册中心、限流规则、线程池状态全压进一张图。
D2的模块导入能力在这里很有价值——拆成多个.d2文件,各自聚焦,再用@import组合出全景图。每个子图渲染都十分清爽,组合图虽然节点多,但因为每个子图的内部结构已经优化过,整体布局依然可读性很高。
8.4 特殊字符需转义,别在标签里裸写引号
D2的标签如果用双引号括起来,内部不能再直接写双引号,否则解析会出错。正确做法是使用\"转义,或者尽量把标签文字改成不带引号的短语。这个细节我在写业务链路描述时踩过一次,排查了半天才发现是引号问题。
8.5 版本升级要注意DSL兼容性
D2更新节奏快,当前核心DSL基本稳定,但插件和进阶语法偶尔会有变动。团队多人协作时,建议在仓库里固定一个D2版本,比如通过d2 --version的输出记录在README里,避免不同电脑版本差异导致渲染结果不同。如果用到CI自动化出图,建议在Dockerfile里固定二进制版本,不要用latest。
9. 一些基于实际项目经验的最终建议
如果看到这里,你大概已经能判断D2是不是你的菜了。从我个人的项目实践来说,D2给我带来的最大改变不是“画图变快了”这么简单,而是“我愿意画图了”。以前架构图是项目启动时画一次、给领导汇报时画一次,图一旦定型就不想再动,因为改一次图成本太高。有了D2之后,架构图的维护成本被压到了极低,每周花两分钟扫一眼仓库里的.d2文件,系统有变更顺手改几行代码,图永远是最新的,这件事在Visio时代想都不敢想。
最后分享一个小技巧:D2的--watch模式特别适合在团队技术评审会上用。与会者提出改动意见,你当场在编辑器里改源码,大屏上的架构图实时刷新。讨论变成了一场活的设计会话,而不是对着过期的静态图各抒己见。这种“架构图跟着对话生长”的体验,是我个人觉得D2最大的价值所在。
如果你想快速验证这份合不合口味,直接跑一条命令,画五个节点拉两条线,看看SVG输出是不是长在了审美点上。合适的话,你大概率也会慢慢把更多的图从传统绘图软件里迁出来。
相关推荐
基于Python+Open CV的手势识别算法设计
伴随着人工智能时代的到来,人机交互的领域也逐渐成为研究的一大方向;其中,手势识别是人机交互领域的一项关键技术,自此手势识别也迎来了一波高潮,近几年无论是在消费领域、电子领域、数码领域、家电领域甚至汽车领域上,都能或多或少的见到手势识别的身影。 手势识别在设计智能高效的人机界面方面具有至关重要的作用, 目前手势识别已应用到手语识别、智能监控、到虚拟现实等各个领域,手势识别的原理都是利用各种传感器(例如红外、摄像头等)对手部的形态进行捕捉并进行建模,形成一个模型信息的序列帧,而后将这些信息序列转换为机器能够识别的相对应指令(例如打开、切换菜单、移动等)来完成控制。 采用Python的集成开发环境Pycharm进行本次课程设计,在Pycharm中进行需要库(模块)的下载,调取电脑摄像头,按帧读取摄像头采集到的头像,形态学处理,图像旋转(由于摄像头采集到的图像是镜像,需要用cv2.flip将摄像头采集到的图像进行处理),选取图片中固定位置作为手势输入,用红线画出手势识别框,基于hsv的肤色检测,进行高斯滤波,找出轮廓,求出图像中手势的凹凸点,手指间角度求取。
ROS-CV-ImageConverter:将ROS图像转换为OpenCV(cv :: Mat)类型的图像
ROS-CV图像转换器 依存关系 cv_bridge(ROS软件包)
计算机视觉图像处理-CV2与图像插值算法
用最近邻插值与双线性插值原理完成图像缩放 最近邻插值,是指将目标图像中的点,对应到源图像中后,找到最相邻的整数点,作为插值后的输。缺点:使用最近邻插值算法处理图像放大时,在图象中可能出现明显的块状效应 双线性插值,在两个方向上做三次线性插值。使用双线性插值算法处理图像放大时,在图象中可能出现马赛克 代码 import cv2 img = cv2.imread(’./xiabang.jpg’, cv2.IMREAD_UNCHANGED) #缩放比例 scale_percent = 0.3 width = int(img.shape[1] * scale_percent) height = int
基于水平集CV模型的图像分割(Matlab代码)
基于水平集CV模型的图像分割(Matlab代码),全套代码
导向滤波(guide_filter)的python实现
导向滤波(guide_filter)的python实现,可以结合暗通道;理论实现比较好的图像去噪效果。

