{T}

scan-thread-pool 技术文档

该项目是用于扫描文件系统的 Node.js 工具,其主要目标是高效地查找并计算指定目录下所有 node_modules 文件夹的大小。项目利用多线程技术来并发执行扫描任务,从而显著提高在大型项目或磁盘上的扫描性能

  • 并发扫描: 利用 Node.js 的 worker_threads 模块,将文件扫描任务分配到多个线程中,避免阻塞主线程
  • 响应式数据流: 使用 RxJS 的 Subject 来处理和传递扫描结果,实现了模块间的解耦
  • 动态任务分配: 采用轮询调度算法,将扫描任务动态、均匀地分配给线程池中的工作线程
  • 目录大小计算: 集成了 get-folder-size 库,用于准确计算目录的磁盘占用空间

整体架构图和技术栈说明:

text
+-----------------+      +-----------------+
|   Main Thread   |----->|   ScanService   |
|   (index.ts)    |      |    (scan.ts)    |
+-----------------+      +-----------------+
        ^                      |
        |                      | 1. Distributes tasks
        |                      v
+-----------------+      +-----------------+
| RxJS Subject    |<-----|  Worker Thread  |
| (Data Stream)   |      | (scan.worker.ts)|
+-----------------+      +-----------------+
                                 |
                                 | 2. Scans filesystem
                                 v
                         +-----------------+
                         |   FileWalker    |
                         | (file-walker.ts)|
                         +-----------------+

各组件/模块的职责划分:

  • index.ts (主线程): 应用程序的入口,负责初始化 ScanService,创建 RxJS Subject,并订阅最终的扫描结果
  • ScanService (扫描服务): 核心协调器,负责创建和管理工作线程池,通过 MessageChannel 与工作线程通信,并使用轮询算法分发扫描任务
  • scan.worker.ts (工作线程): 在独立的线程中执行文件扫描任务。每个工作线程包含一个 FileWalker 实例,并通过 MessagePort 与主线程进行通信
  • FileWalker (文件遍历器): 封装了文件系统遍历的底层逻辑,使用 fs.promises.opendir 异步读取目录内容,并通过 EventEmitter 发出扫描结果

数据流向和接口设计:

  1. 初始化: index.ts 启动 ScanServiceScanService 根据 CPU 核心数创建相应数量的工作线程,并为每个线程建立一个 MessageChannel
  2. 任务分发: ScanService 将初始扫描路径作为一个任务,通过轮询算法选择一个工作线程,并通过 MessagePort 将任务发送出去
  3. 文件扫描: 工作线程接收到任务后,驱动 FileWalker 开始遍历文件系统。FileWalker 采用广度优先策略,将发现的子目录作为新的结果发出
  4. 结果回传: 工作线程监听到 FileWalkernewResult 事件后,将结果通过 MessagePort 回传给 ScanService
  5. 结果处理: ScanService 接收到结果后,判断路径是否为 node_modules。如果是,则通过 RxJS Subject 将路径推送到最终结果流;如果不是,则将其作为新的扫描任务,重新分发给工作线程
  6. 最终输出: index.ts 中的订阅者接收到 node_modules 路径后,调用 getSize() 计算其大小并输出到控制台

3. 详细实现

  • 核心功能的技术实现细节:

    • 线程池管理: ScanServiceinitWorkers 方法利用 os.cpus().length 来动态创建与 CPU 核心数相等的工作线程,以最大化并行处理能力。
    • 高效通信: 主线程与工作线程之间使用 MessageChannel 进行通信。通过在 postMessage 时传递 [port2]MessagePort 的所有权被转移,避免了数据序列化和反序列化的开销。
    • 异步文件I/O: FileWalker 使用 fs.promises.opendirdir.read() 进行全异步的目录读取,确保了高吞吐量和非阻塞的执行。
  • 关键算法或业务流程说明:

    • 广度优先扫描 (BFS): 项目通过任务队列实现了广度优先的文件系统遍历。这种策略确保了扫描是逐层进行的,有助于平均分配扫描负载。
    • 轮询调度 (Round-Robin): ScanService 中的 addJob 方法通过一个简单的索引递增和取模操作,实现了轮询调度,确保了扫描任务在工作线程之间均匀分布。

5. API 文档

  • ScanService 类:
    • startScan(stream$: Subject<string>, path: string): 启动扫描过程
      • stream$: 用于接收 node_modules 路径的 RxJS Subject
      • path: 初始扫描目录
  • FileWalker 类:
    • enqueueTask(path: string): 将一个目录路径加入到内部任务队列以供处理
    • events (EventEmitter):
      • newResult 事件: 当一个目录被扫描完成时触发,负载为 { results: Array<{ path: string; isTarget: boolean }> }