之前的一篇文章中,我们探讨了不同编程语言之间的交互方式,当然那篇技术分享文章已经是三年前的了。最近接触了更多关于这方面的东西,就比如Docker和Containerd之间交互所用到的Unix Socket、RPC其实可以有更多巧妙应用,甚至作为用户界面。当然这次跟这个也是有关系的,本期的文章我主要想探讨的问题是所谓评判程序优秀程度时朗朗上口的两个指标“高内聚低耦合”,这是一个软件工程学中的概念,“内聚性”使得模块更加独立,“低耦合”使得模块之间的关系减弱。通常我们使用编程语言的一些特性还有巧妙的设计模式增强内聚、降低耦合。从而使得代码易于维护和重用,但这并不是强制的。这期我要说一些思路,但并不是所谓银弹,也就是说这个方案并不是一个万能钥匙,不过追求易于维护和职责分离的时候走投无路了,可以试一试我说的这些方案。
首先我说一个背景,假如说一个操作系统没有内置进程管理器,我们要做一个进程管理器,职责就是管理系统进程(类似systemd)。首先我们就会想到参考systemd对吧,先不说他的CLI 界面,就是这个模式来说,systemctl本身功能就是管理进程相关的,这个不会受外界影响,那它管理的进程功能应用呢?细想一下,是不是systemd 对业务进程是一种“一厢情愿”的超低耦合!那加入我们自己想做一个进程管理器,你会怎么做,难道直接将运行的业务内置进进程管理器?不过也不是不可以,看这个业务架构需要的细分程度,不过我们当然还是追求一定的通用性。现在我们讨论一种架构较为庞大,且各部分需要复用或可能需要复用,并且不同层次的服务都可能被直接或间接调用。
再来看systemd的CLI,就是我们常用的systemctl命令,它跟systemd之间呢?容易发现systemctl 与 systemd 之间是“进程隔离 + 文本协议(D-Bus)”的松耦合,就像Docker跟Containerd之间是用Unix Socket发送grpc消息一样。他们的共性就是-“进程隔离”,而根据我之前的文章,进程之间是有很多通信方式的,甚至可以跨设备、跨编程语言、跨服务。所以说在这个“颗粒度”的“隔离”是可行的,并且有效的增强高内聚低耦合的手段。
我认为低耦合的技巧在于“隔离”,恰当的软件隔离还是硬件隔离都可以实现一定程度的解耦合,得益于硬件接口和软件消息的多样性、可转换、规范性,我们可以非常方便的让各个部分各司其职,实现低耦合和复用。硬件层面,比如我有多台服务器,每一台对应着不同的服务,就像现在有的洗衣机专门分出来一个洗衣桶专门洗内衣、专门洗袜子。软件方面,进程隔离、类隔离、线程隔离都是非常常见的。但其实还有一种方式,电热毯是用来暖被窝的,热水壶就是用来烧水的,但是根本上都是电热原理,电热原理跟这里两种使用方法之间也是松耦合,这是一种原理与用途的解耦,这种机制把底层通用原理/机制与上层具体应用/场景分离开来,让同一个原理可以灵活地适配多种用途,而不被某一用途“绑死”。这第三种方式也揭示了一种新的思维,暂且我把“底层机制+中间抽象”称为“原子能力”,而原子能力可以支撑的叫“上层应用”,就像我刚才说的“电热原理”就是一种“原子能力”,“电热毯”和“热水壶”就是“上层应用”。
上面, 我对“原子能力”的定义中包含了“中间抽象”,其实“原子能力”本身也是可以分层整合成新的“原子能力”,这个衍生能力也很重要,这个一会说。我继续补充一个说法,这是一个一句话的设计原则,“不要让你的底层机制知道它在做什么业务”。如果我们要做的是实践,它是一种知识的整合体加上层封装,而这些理论知识必须对应来自几本书籍的话,这些书籍就是所谓“原子能力”,那么如果有一个图书馆呢?图书馆不是“书的堆积”,而是“知识的操作系统”。原子能力本身可以分层、整合、衍生出新的“原子能力”。
前提知识引入好了!现在你已经了解了软件和硬件“隔离解耦“和我的第三方法“机制与策略分离解耦”,现在你可以看这张表了。
| 维度 | 实现抓手(举例) | 一句话记忆 |
|---|---|---|
| 1. 空间隔离 | 进程、容器、VM、沙箱、不同微服务、不同机柜、不同芯片 | “不在同一地址空间,就别想直接牵手。” |
| 2. 时间隔离 | 消息队列、事件溯源、CQRS、批处理窗口、断点续传 | “错开时间,自然不见面。” |
| 3. 协议/格式解耦 | 统一 IDL(Protobuf/Avro)、自描述消息(CloudEvents)、转码网关 | “语言不通,带个翻译。” |
| 4. 语义层解耦 | 原子能力图书馆、机制-策略分离、DSL→引擎→执行 | “只给说明书,不给故事书。” |
| 5. 依赖方向反转 | DIP + IoC/DI、OSGi/Jigsaw SPI、插件化、Spring Factories | “别来找我,我去找你。” |
| 6. 数据访问解耦 | 读写分离、事件存储 + 投影、数据虚拟化、Polyglot DB | “数据长在一起,用法各自开花。” |
| 7. 版本解耦 | 并行版本、灰度路由、Feature Flag、Sidecar 代理兼容老协议 | “新老同堂,各走各门。” |
| 8. 组织解耦 | 康威逆运用:用架构反向约束团队边界、平台 + 业务双速团队 | “代码怎么拆,团队就怎么坐。” |
| 9. 治理解耦 | 服务网格( traffic / policy / security 三军分立)、策略引擎 OPA | “流量、安全、观察,三权分立。” |
| 10. 资源解耦 | Serverless/BaaS、弹性池、GPU/FPGA 资源抽象、统一调度层 | “谁用谁拿,用完即还。” |
| 11. 失败/重试解耦 | 断路器、舱壁、异步重试队列、幂等键、Saga 补偿 | “出错别连环,炸不到邻居。” |
| 12. 性能解耦 | 背压流控、批量聚合、缓存分层、CDN 边缘卸载 | “快慢分流,别让慢拖死快。” |
| 13. 安全解耦 | 零信任网络、mTLS 即服务、身份与业务逻辑分层(SPIFFE ID) | “先验身份,再谈业务。” |
| 14. 硬件解耦 | 总线标准(PCIe)、即插即用固件、SerDes 通道抽象、Chiplet | “管脚对齐,内部随便换。” |
| 15. 生命周期解耦 | GitOps + 不可变基础设施、蓝绿/金丝雀发布、回滚与数据迁移脚本分离 | “生归生,死归死,生死不互卡。” |
你可能从前在哪看到过这张表,确实在一些书记上有想通或相似的说法和列举,但是你可能会看的一头雾水,不知所云。不过当你理解了我上面说的几种基本解耦方式以后,只要抓住你这几条“元思维”,表里的任何招式都能瞬间归类:
| 表的维度 | 落地形式 | 映射到你的哪条思维 |
|---|---|---|
| 1 空间隔离 | 进程、容器、VM、机柜 | 隔离解耦 |
| 2 时间隔离 | 消息队列、CQRS | 隔离解耦(时间维度) |
| 3 协议解耦 | Protobuf、转码网关 | 机制-策略解耦(把“通信机制”与“业务语义”分离) |
| 4 语义层解耦 | 原子能力图书馆、DSL | 机制-策略解耦 + 原子能力衍生 |
| 5 依赖反转 | DI、SPI、插件 | 机制-策略解耦(调用方向抽象) |
| 6 数据解耦 | 事件源、读写分离 | 隔离解耦(数据变化与读模型隔离) |
| 7 版本解耦 | 灰度、Feature Flag | 隔离解耦(生命周期维度) |
| 8 组织解耦 | 康威逆运用 | 隔离解耦(人肉维度) |
| 9 治理解耦 | 服务网格、OPA | 机制-策略解耦(流量/安全策略下沉为可插拔机制) |
| 10 资源解耦 | Serverless、弹性池 | 隔离解耦(资源与业务代码分离) |
| 11 失败解耦 | 断路器、Saga | 隔离解耦(故障爆炸半径隔离) |
| 12 性能解耦 | 背压、缓存分层 | 隔离解耦(快慢路径分离) |
| 13 安全解耦 | mTLS、零信任 | 机制-策略解耦(安全机制与业务逻辑分离) |
| 14 硬件解耦 | 总线、Chiplet | 隔离解耦(物理接口维度) |
| 15 生命周期解耦 | GitOps、蓝绿 | 隔离解耦(部署与代码分离) |
好了,从此这15招不再是散落的“锦囊”,而是一张能按图索骥的“思维地图”。系统一乱,先别急着拍桌子,掏出这张表——缺哪维补哪维,哪里紧拆哪里,百试百灵。最后再送一句“万能解耦咒语”
“把变化关进笼子,把不变做成图书馆;谁变化,谁掏钱,谁稳定,谁收费。”