尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

用代码设计图表:Mermaid从零绘制API网关架构图实战

用代码设计图表:Mermaid从零绘制API网关架构图实战 作为一个常年跟架构图、流程图、UML图打交道的开发者我最早对“画图”这件事的态度是能躲就躲。直到有一次项目迭代需要频繁调整调用链关系我在绘图软件里拖了一下午框线改一处连线条目就要重新拉线对齐最后实在顶不住才认真开始尝试用代码来设计图表。那时候我才意识到diagram-design用代码设计图表不是把画图工具搬到命令行而是把“画图”变成“写代码”让图表进入版本管理变成可以评审、可以复用、可以自动生成的工程资产。这篇文章不聊抽象概念直接讲清楚 diagram-design 到底能解决什么问题当前主流的工具怎么选以及我用 Mermaid 从零画一套 API 网关架构图的全过程。不管你是后端开发、前端开发还是经常写方案文档的产品和技术负责人只要被“手工画图”折磨过这篇文章应该能让你少走不少弯路。1. 先把思路理清楚diagram-design 到底在解决什么问题1.1 传统画图方式的痛点远不止“画得慢”我见过很多团队的技术文档里面贴的架构图都是从绘图软件导出的图片。这种图最大的问题不是丑而是“画完就死”。业务一变流程一变就得打开源文件重新拖拽调整。更要命的是图片文件拖进文档之后源文件往往不知道丢到哪去了下一个人想改只能对着像素级别的 PNG 望图兴叹。还有协作问题。几个人同时要改一张架构图没有 diff 概念没有合并概念谁后改谁覆盖。最后只能靠口头约定“我改完了你再动”效率极低。还有一致性问题。代码里明明是 A 服务调 B 服务文档图里却画成 B 调 A真实系统有 5 个模块图上只画了 3 个。图一旦不跟着代码走就失去了参考意义。说白了传统画图工具做的是“呈现层”的事情而 diagram-design 的核心主张是把图的本质还原为数据让图可以像代码一样被编写、被审查、被版本化。我们不再需要手动挪动框的位置只需要描述清楚“有什么节点、节点之间什么关系”布局和渲染交给引擎去算。1.2 图表即代码核心主张和它带来的连锁好处diagram-design 的底层逻辑是用一种结构化的 DSL领域特定语言来描述图表内容再通过渲染引擎生成图片或交互式图表。这套思路跟写代码极其相似。好处是连锁的。第一图表可以放进 Git 仓库改了什么一目了然代码评审的时候顺便就把图评了。第二图表的文本形态天然适合复用像函数封装一样抽出一张基础架构图模板哪里都能引用。第三文本比图形更容易搜索想在二十份文档里找到所有含“Redis”的图直接在仓库里 grep 就行。实际操作中还有一点很实用图跟代码放在同一份文档里改代码的时候顺手把图更新掉文档和系统实现的偏差会大幅缩小。这套工作流一旦跑起来你会发现图表不再是“额外的负担”而是开发流程里的一部分。1.3 别迷信diagram-design 也不是万能的说实话用代码画图也有不适用的地方。如果你想画的是一张精美得能拿去当新闻稿配图的运营增长图表或者一张需要精确控制每个图标位置的 UI 线框图DSL 方案通常搞不定。这类需求对“像素级”控制的要求太高文本描述很难覆盖。另外如果你只是跟同事闲聊时想快速比划一个想法直接拿笔画草图或者在线白板拖两下比打开编辑器写 DSL 要快得多。工具选型永远要看场景不要为了用代码而用代码。我个人的判断标准是这张图未来有没有可能要“修改”或“复用”只要答案是肯定的就值得用 diagram-design 来做。2. 工具选型四个主流方案选错了真会走弯路2.1 先认识四个主流选手Mermaid、PlantUML、D2、Graphviz现在做 diagram-design 的工具不少但我实际用下来真正经得住场景考验的还是那几个。每个工具都有自己的脾气选型前得先摸清楚它们的底子。Mermaid目前热度最高的一个官网声称已经被 GitHub、Notion、GitLab 等大量平台原生支持。语法非常接近自然语言画流程图、时序图、状态图、甘特图都行。我最早入坑 diagram-design 就是因为它支持在 Markdown 里直接写 mermaid 代码块写完刷新就能看图。PlantUML老牌工具在 UML 领域深耕了很多年。画类图、时序图、用例图的体验很成熟语法自成体系生态里有很多 Eclipse 和 JetBrains 家族的插件支持。D2相对年轻的新秀主打“现代感”和“声明式布局”。语法设计很克制学习成本低比较适合画系统架构图这种偏拓扑类的图。它还有一整套主题系统出来的图颜值挺高。Graphviz老前辈了处理复杂拓扑结构图的能力非常强。但它不是为“人写”而生的语法相对底层得靠 DOT 语言描述图结构更适合程序化生成而不是手写。2.2 参数对比一句话说清各自的优势和劣势工具语法上手难度布局引擎能力平台生态最适合的场景主要短板Mermaid低接近自然语言中等日常够用极强三大代码托管平台和笔记软件都支持嵌入 Markdown 文档、快速画流程/时序/状态图复杂大图的布局不太可控PlantUML中等类 Java 风格中等偏上UML 领域强插件丰富老牌 IDE 支持好UML 类图、时序图、用例图语法略显陈旧非 UML 场景表现一般D2低声明式风格清爽较强注重自动排版生态尚在成长期架构图、拓扑图、层级图社区规模比前两者小周边工具少Graphviz高DOT 语法偏底层极强适合复杂拓扑图多年积累工具链成熟程序化生成大图、复杂网状结构学习曲线陡默认渲染比较朴素2.3 我的实际选型建议如果让我给一个跨团队协作的标准答案我会优先推荐 Mermaid。原因很简单生态太关键了。因为 GitHub 和 GitLab 原生支持渲染同事打开仓库里的 Markdown 文件就能看不需要任何额外工具链这个“零门槛”优势直接决定了方案能不能推进下去。但如果你要画的图以 UML 类图为主而且团队里都在用 IntelliJ 系列 IDEPlantUML 的集成体验会更顺滑。如果你想给架构文档配几张“赏心悦目”的架构图对颜值有要求D2 是更好的选择。至于 Graphviz我一般只在写脚本批量生成图的时候才用它比如根据云上资源列表自动生成网络拓扑图这种程序化场景是它的主场。选择障碍的解决办法很简单从 Mermaid 开始遇到它搞不定的场景再换。不要一开始就在工具选型上花太多时间。3. 实操案例用 Mermaid 从零画一套 API 网关架构图3.1 动手前先想清楚这张图要传达什么空讲语法没意义直接拿我最近做的“API 网关接入层架构图”当例子。假设现在要做一个微服务系统的接入层设计核心诉求是向新同学解释清楚请求是怎么从客户端进来经过网关做了哪些处理后再转发到后端服务的。动手前先列需求这张图要给谁看新同学所以不能画得太抽象要表达什么请求的流转路径以及网关的核心功能模块需要用哪类图流程图更合适因为有明确的请求方向。搞清楚这三个问题之后再开始写 Mermaid 语法思路就清楚了。不建议上来就写代码先拿纸笔列一下节点哪怕只是简单的“客户端 - 负载均衡 - 网关 - 服务 - 缓存/数据库”这样的链路也比直接在编辑器里瞎敲好。把节点关系捋清楚后面写 DSL 就是水到渠成的事。3.2 图的三大要素拆解节点、连线、分组任何一张图拆到底就是三件事节点Node、连线Edge、分组Subgraph。Mermaid 的语法也是围绕这三要素展开的。节点代表系统里的实体比如客户端、网关、各个微服务。连线代表实体之间的关系比如调用、依赖、消息传递。分组代表归属或分层比如把几个微服务归到“业务层”。先把要画的图在脑子里拆成这三个维度写代码时就非常顺。回到网关门禁的例子上节点有 Client、SLB负载均衡、API Gateway、Order Service、User Service、Redis、数据库连线是 Client 调 SLB、SLB 转发给 Gateway、Gateway 路由到 Service、Service 访问 Redis 和数据库分组是“接入层”“业务层”“数据层”三个大块。这不就清晰了。3.3 DSL 语法逐段拆解从空白文件到完整图形Mermaid 的流程图语法以graph关键字开头后面跟上方向。我用graph TD表示从上到下布局TD 是 Top Down 的缩写。下面是我实际使用的代码我分段解释。graph TD A[客户端] -- B[SLB 负载均衡] B -- C[API 网关] C -- D[用户服务] C -- E[订单服务] D -- F[(用户数据库)] E -- G[(订单数据库)] E -- H[(Redis 缓存)]先看第一行graph TD。TD 表示“从上到下布局”如果你希望图从左往右展开改成graph LRLeft Right即可。节点写法是“ID[显示文本]”方括号表示矩形节点圆括号表示圆角矩形。数据库我习惯用[( )]这种圆柱形语法Mermaid 会渲染成数据库图标。这段代码虽然能画出基本链路但所有节点平铺在一起没有体现“层”的概念。所以接下来要用subgraph来分组。graph TD subgraph 接入层 A[客户端] -- B[SLB 负载均衡] B -- C[API 网关] end subgraph 业务层 C -- D[用户服务] C -- E[订单服务] end subgraph 数据层 D -- F[(用户数据库)] E -- G[(订单数据库)] E -- H[(Redis 缓存)] endsubgraph的语法是subgraph 分组名里面放节点和连线最后以end结尾。这样渲染出来的图会自动把节点收进对应的虚线框里层次感立刻出来。小组之间还可以嵌套不过我建议分组别超过三层不然图会很拥挤。光分组还不够我想让同一个层级内的节点并排排列避免所有节点挤成一条纵向直线。Mermaid 里可以在 subgraph 内声明方向比如direction LR表示该分组内部从左往右排列。这个细节很实用不写的话图会很死板。3.4 让图更好看样式定制与可读性优化基础图画好之后下一步是美化。Mermaid 提供了classDef语法来定义样式类类似 CSS 类可以把颜色、边框、填充统一定义再通过class关键字应用到节点上。graph TD classDef green fill:#d4edda,stroke:#28a745,stroke-width:2px; classDef blue fill:#d1ecf1,stroke:#007bff,stroke-width:2px; classDef orange fill:#fff3cd,stroke:#ffc107,stroke-width:2px; subgraph 接入层 A[客户端] -- B[SLB 负载均衡] B -- C[API 网关] end subgraph 业务层 C -- D[用户服务] C -- E[订单服务] end subgraph 数据层 D -- F[(用户数据库)] E -- G[(订单数据库)] E -- H[(Redis 缓存)] end class A orange class B,C blue class D,E green class F,G,H green这段代码给不同层级的节点上了颜色视觉上一下子就能区分接入层、业务层和数据层。颜色本身没有标准答案但建议同色系代表同一类角色降低认知成本。还有一个很实用的技巧给关键连线上加标签。比如在业务层连线时标注“/api/user”和“/api/order”这样的路由路径看图的人一眼就知道网关是怎么分发请求的。连线加文字的语法是C --|/api/user| D在横线后面用竖线框住标签文字。复杂图里为了避免线条交叉可以调整节点声明顺序把关系密切的节点放得近一点。Mermaid 没法像绘图软件那样拖拽坐标它的布局算法是自动的但我们可以通过调整代码顺序和 subgraph 方向来影响最终结果。3.5 从架构图到时序图一个语法切换就搞定的事流程图只是 diagram-design 的一个入口实际工作中我更常画的是时序图。同一套 DSL 思路换一个图类型关键字表达的东西完全不同。拿用户登录场景举例客户端请求网关网关校验 Token 后转发给用户服务用户服务查库后返回结果。用 Mermaid 的 sequenceDiagram 表达会非常直观代码我贴在下面。sequenceDiagram participant C as 客户端 participant G as 网关 participant U as 用户服务 participant D as 用户数据库 C-G: 登录请求 G-G: 校验 Token G-U: 转发请求 U-D: 查询用户信息 D--U: 返回结果 U--C: 登录成功时序图的语法更贴近自然语言participant声明参与者-实线表示同步调用--虚线表示返回。这里“参与者”就是“节点”“调用”就是“连线”本质还是节点加连线的组合。一旦理解了 diagram-design 的通用思维模型换图类型就只是换一套 DSL 语法的问题。4. 进阶玩法把 diagram-design 变成工作流的一部分4.1 把图表写进技术方案文档一个代码块搞定我现在写任何技术方案文档架构图和时序图都是用 Mermaid 直接写在 Markdown 里的。这么做最大的好处是文档本身就是可运行的项目资源其他人克隆仓库之后用 VS Code 装个 Mermaid 插件打开文档就能预览图形。GitHub 和 GitLab 在 Markdown 渲染层面已经原生支持 mermaid 代码块意味着团队评审的时候不需要任何额外步骤。我在实际推进中遇到的最大阻力不是工具不会用而是有人习惯性想“打开绘图工具去画”这时候直接把示例代码贴给他让他改两个节点名字接受度会高很多。还有一点值得做在 CI 流程里加一道校验用 mermaid-cli 把仓库里所有 mermaid 代码块渲染成 PNG如果不是最新就报错强制文档更新。这一步把“画图”从手工劳动升级成了工程约束长期收益非常可观。4.2 脚本批量生成图表Graphviz 的程序化用武之地如果你面对的是几百个节点的大图手写 DSL 就不现实了这时候图必须“算出来”。我举一个实际场景公司云账号下有一百多台云主机、几十个负载均衡、几十个数据库实例想看它们之间的网络拓扑关系手工画图是不可能的。我的做法是写一个 Python 脚本调用云厂商的 API 拉取所有资源关系自动生成 DOT 格式的文本再交给 Graphviz 渲染成图。整个流程里人只负责写逻辑图和真实资源保持同步更新这才是 diagram-design 真正威力所在。DOT 语言的语法类似声明式的节点边列表核心就是digraph G { A - B; }这样的结构配合 Python 脚本生成一点都不难。虽然 Mermaid 也支持从代码库动态生成但复杂拓扑下 Graphviz 的布局引擎明显更稳定不容易出现边交叉得乱七八糟的情况。4.3 团队协作里的实践心得用代码评审的方式评审图把图纳入开发流程之后协作方式也会自然变化。以前改图是“一个人私下改完再发截图”现在是“改一行 DSL、提一个 MRMerge Request”图表的改动和代码改动放在同一个变更里评审人既能看代码 diff也能看图的 diff讨论起来非常具体。坦白说这个习惯不是一天养成的需要团队形成共识。我自己的做法是每次新增或修改接口顺手更新对应时序图提交信息里写清楚“更新登录流程时序图”。坚持一段时间后文档的可信度会越来越高大家开始真正把图当作“活文档”而不是摆设。5. 常见问题与排查技巧实录5.1 中文乱码和字体问题Mermaid 默认渲染在大多数情况下对中文支持没问题但如果你用命令行工具导出 PNG遇到中文变成方块的概率不低。原因是运行时环境中缺少中文字体。解决思路很直接检查渲染环境有没有安装字体。比如在 Linux 服务器上用 mermaid-cli 导出请确保fonts-noto-cjk这类中文字体包已安装。另外可以在 Mermaid 配置里统一指定 fontFamily我一般这样设置{ theme: default, fontFamily: PingFang SC, Microsoft YaHei, Noto Sans CJK SC }这段配置放在 mermaid 的初始化参数里不同的使用环境位置不太一样比如在 VS Code 插件里是在设置中配置在网页里是在初始化代码中传入。这个问题看起来小但真遇到的时候很熬人。5.2 布局不理想节点连成一坨怎么办Mermaid 的自动布局在日常小图上表现不错但图一复杂节点挤成一堆、连线绕来绕去是常有的事。我总结了几条实用的调优小技巧。第一条是利用direction调整局部排列方向前面已经说过第二条是拆分大图一张图超过二三十个节点时无论什么工具都很难清晰这时候该拆成多张图第三条是巧用不可见连线来“撑开”空间比如在两个不相干的节点之间加一条A ~~~ B的虚线用不渲染的连线样式设为透明度 0拉开距离。第三条属于偏门招但非常有效。graph LR A[服务A] ~~~ B[服务B]5.3 语法报错排查先定位再修Mermaid 的语法错误提示有时候不那么直观尤其是有多个错误时。我的排查习惯是先用官方在线编辑器Mermaid Live Editor粘贴代码它会把解析错误定位到具体字符比自己对着文本猜效率高很多。常见的报错原因包括节点 ID 里出现特殊字符没加引号连线标签里的竖线|没有配对subgraph 的end丢失中文括号混入了英文语法。这些错误只要定位到具体行改起来都很快。5.4 常见问题速查表现象大概率原因解决方案中文显示成方块渲染环境缺中文字体安装 Noto CJK 等中文字体配置 fontFamily节点重叠、连线交叉图太大或方向混乱拆图用 direction 控制局部布局用透明连线撑间距语法一直报错特殊字符未转义或括号不匹配粘贴到 Mermaid Live Editor 定位错误GitHub 上预览正常本地 CLI 导出报错本地环境版本过旧升级 mermaid-cli 和相关浏览器组件图解析成功但样式不生效classDef 名称拼写不一致全局搜索 class 名确保大小写完全一致时序图参与者太多导致横向溢出参与者过多拆成多个时序图或换用流程图表达6. 个人实践里的几点额外体会用 diagram-design 画图这件事坚持下来靠的不是工具多先进而是习惯。很多开发者一开始觉得写 DSL 比拖拽慢但实际上一旦图进入版本控制、进入评审流程、可以自动生成它的长期价值远超“画出来那一刻”的效率。我的个人建议是从今天开始任何一张你准备画超过十分钟的图都试着用代码来描述。哪怕第一次用起来不顺手也值得坚持两三张图你会发现自己对“图”的理解会从“画得好看”变成“表达准确”。当图表变成跟代码一样可以被讨论、被审计、被复用的东西时你会发现它已经不是你文档里的装饰而是系统设计的一部分了。
返回列表