Portman
VSCode 插件,可在任何操作系统上通过图形界面管理网络端口。
在软件开发过程中,一个经常出现的业务障碍是网络端口分配冲突(在 Node.js 环境中被称为 EADDRINUSE 错误)。当先前的进程(无论是意图运行的还是孤儿/僵尸进程)在特定网络接口和端口上保持打开监听 socket 时,就会发生这种情况,从而阻止新实例绑定到该资源。
手动解决此状态需要开发者通过命令行界面 (CLI) 直接与操作系统交互,以:
- 探测: 运行网络分析工具(例如
lsof,netstat,ss)。 - 过滤: 处理标准输出 (stdout) 以隔离绑定到冲突端口的进程。
- 识别: 提取进程标识符 (PID)。
- 终止: 向识别出的 PID 发送系统信号(如
SIGKILL)。
业务解决方案
Portman 将此诊断和干预工作流自动化并抽象化,直接将其集成到集成开发环境 (IDE) 中。
功能工作流总结为:
- 系统状态提取: 扩展以透明的方式与宿主操作系统的原生 API 和二进制文件交互,以检索网络连接的当前状态。
- 转换与映射: 从操作系统提取的原始信息被规范化为结构化的领域模型。
- 实时展示: 规范化的模型在 VS Code 图形界面内部渲染为交互式树状结构。
- 执行动作: 通过内置命令,该工具允许用户选择树节点,并以统一的方式将终止信号发送到底层进程,而无需考虑所使用的操作系统。
技术分析:架构与模型
为确保软件生命周期支持可持续增长、隔离的可测试性以及跨多个操作系统(Linux, macOS, Windows)的横向适应性,我在 干净架构 (Clean Architecture) 和 领域驱动设计 (Domain-Driven Design - DDD) 原则下设计了项目结构。
代码在概念上分为四个同心层,其中依赖关系严格向内流动(指向领域中心)。
graph TD
subgraph ext["外部层 (基础设施与呈现)"]
VS[VS Code Extension API]
CLI[OS Binaries: netstat, lsof, ss]
end
subgraph pres["呈现层"]
PTD[ProcessTreeDataProvider]
Cmds[KillProcessCommand]
end
subgraph app["应用层"]
SP[UseCase: SearchProcesses]
KP[UseCase: KillProcess]
end
subgraph dom["领域层"]
Model[Aggregate Root: Process]
RepoInt[Interface: ProcessRepository]
end
VS --> PTD
VS --> Cmds
PTD --> SP
Cmds --> KP
SP --> RepoInt
KP --> RepoInt
CLI -.->|实现| RepoInt
classDef domain fill:#14b8a6,fill-opacity:0.3,stroke:#0d9488,stroke-width:2px;
classDef app fill:#14b8a6,fill-opacity:0.12,stroke:#0d9488,stroke-width:2px;
classDef pres fill:#737373,fill-opacity:0.14,stroke:#737373,stroke-width:2px;
classDef external fill:#737373,fill-opacity:0.06,stroke:#737373,stroke-width:1px,stroke-dasharray:4 4;
class Model,RepoInt domain;
class SP,KP app;
class PTD,Cmds pres;
class VS,CLI external;
1. 领域核心 (Domain)
领域模型与任何外部依赖、框架或操作系统细节隔离。其主要目标是建模网络进程的抽象。
聚合 Process
核心实体是 Process,它充当 Aggregate Root。它由多个值对象 (Value Objects) 组成,这些值对象在实例化时保证数据的完整性:
ProcessId:唯一标识符 (PID)。ProcessProgram:可执行文件或守护进程的名称。Protocol:传输协议 (TCP, UDP)。Address:由AddressHost(本地/远程 IP 或主机名)和AddressPort(数字端口)组成。ProcessStatus:连接的当前状态(例如 LISTEN, ESTABLISHED)。
classDiagram
class Process {
+ProcessId id
+ProcessProgram program
+Protocol protocol
+Address local
+Address remote
+ProcessStatus status
+String icon
+String label
+String description
+String tooltip
}
class Address {
+AddressHost host
+AddressPort port
}
class ProcessRepository {
<<Interface>>
+search() Promise~Process[]~
+kill(process: Process) Promise~void~
}
Process "1" *-- "2" Address : local/remote
ProcessRepository --> Process : 管理
此外,领域定义了契约 ProcessRepository,该契约建立了系统所需的基础搜索和终止操作,而无需规定 如何 实现它们。
2. 应用层 (Application)
应用层利用领域的契约编排业务规则。它是围绕特定的用例 (Use Cases) 构建的:
SearchProcesses:向仓库发出查询 (Query),以检索Process对象数组。KillProcess:向仓库发出命令 (Command),以删除特定的Process对象。
这些用例是无状态的,并且依赖于运行时注入的具体仓库。
3. 基础设施层 (Infrastructure)
这里存在 ProcessRepository 接口的技术实现。依赖注入在应用的入口点 (extension.ts) 进行管理,评估环境变量 process.platform 以实例化正确的适配器。
LinuxProcessRepository:优先通过netstat进行提取。如果前者不存在,它拥有向二进制文件ss的平滑降级机制 (graceful fallback)。此外,它评估配置portman.linux.asRootUser,以便在需要时通过pkexec或sudo提升权限。MacProcessRepository:以原生方式与lsof -i -P -n交互。WindowsProcessRepository:调用基于 PowerShell 的实现 (Get-NetTCPConnection) 或继承的 MS-DOS 命令 (netstat -a -b -n -o)。
每个仓库都将标准输出的语法分析委托给特定的转换器类(例如 MacNetstatProcessTransformer),这些类解析原始文本字符串,验证结构并把数据映射到领域对象 Process。
4. 呈现层 (Presentation)
该层将业务逻辑与 Visual Studio Code 的扩展 API 隔离开来。
主要组件是 ProcessTreeDataProvider,它实现了 VS Code 的 TreeDataProvider 接口。其职责是订阅 IDE 事件,消费 SearchProcesses 用例,并将 Process 实体数组转换为 ProcessTreeItem(或 ProcessQuickPickItem)实例,以便在用户界面中最终渲染视觉树。它还通过集成在编辑器中的对话框管理操作错误的捕获与展示。
技术栈
为本项目选择的工具链反映了构建优化打包、强类型且与 Node.js (Electron) 运行环境紧密绑定的产物的需求。
| 技术 / 工具 | 在架构中的角色 | 功能目的 |
|---|---|---|
| TypeScript (v5.x) | 主要开发语言 | 提供静态分析和强类型,这对于对领域层实体建模和确保层间契约至关重要。 |
| VS Code Extension API | 运行环境 | 提供程序化接口 (vscode.*) 用于原生 UI 的渲染、命令注册和侧边栏访问。 |
| Webpack (v5.x) | 打包工具 / Bundler | 组合、转译并压缩源码为优化的 extension.js 文件,减少插件的激活延迟。 |
总结
在 IDE 扩展(一种特征上与视图高度耦合的环境)项目中严格采用干净架构,实现了职责的严格分离。解决网络依赖关系的逻辑核心完全忽略了底层操作系统和 VS Code 的呈现框架。这种抽象确保了原生集成可以被修改、优化或替换(例如,从 CLI 命令过渡到 C++ 内核调用),而不会对端口终止工作流的业务规则或呈现逻辑产生副作用。
拖动可移动图示。按 + 和 − 缩放,方向键移动,0 重置视图。
一起合作
软件工程师、高级全栈开发工程师,常驻玻利维亚Santa Cruz,远程办公。欢迎新的工作机会。