Files
storageNode/llm_readme.txt
T
us9929 80b82121a1 加固下载服务与删除路径,补测试
- 8888 下载服务:加读超时、接受循环单次失败不退出;畸形请求行回 400 且不泄漏连接;
  Range 越界按文件末端夹取或回 416,非法 Range 不再中断处理
- 删除任务改为按根目录规范化校验,拦截 ../ 与绝对路径越界删除
- MessageCodec:先序列化再写帧头(不再产生半帧),并限制单帧长度上限
- 节点绑定端口等待结果,失败即抛出而非静默不监听
- 配置加载支持指定路径并对缺文件记录告警;完成通知对消息做 URL 编码并加超时
- 测试 28 → 76:新增协议编解码、节点消息处理与上报循环、备机订阅分发、
  gid 匹配、删除边界、配置加载等用例
2026-09-23 01:04:03 +08:00

183 lines
8.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# storageNode 项目结构
## 概述
分布式存储/下载节点服务端,用于画廊网站。职责包括:从远程源下载图片集、压缩为 ZIP 归档、通过 HTTP 提供下载(支持断点续传)、管理代理订阅配置(V2Ray/Clash),并通过 Netty 自定义 TCP 协议与中心服务器通信。
- **Group ID:** `org.lion`
- **Version:** `1.0`
- **Java 版本:** 21 字节码(生产使用 GraalVM JDK 25)
- **构建工具:** Maven(单模块)
- **编译目标:** 当前以 JVM/JAR + `lib/` 运行;保留 GraalVM 原生配置但尚未完成 JDK 25 原生验证
---
## 目录结构
```
storageNode/
├── pom.xml # Maven 构建配置
└── src/
├── main/
│ ├── java/
│ │ └── lion/
│ │ ├── Main.java # 程序入口
│ │ ├── storageNode.java # Netty TCP 核心节点
│ │ ├── CustomUtil.java # 工具方法(HTTP 通知、端口查找、ObjectMapper)
│ │ ├── MultiThreadedHTTPServer.java # HTTP 文件下载服务(端口 8888)
│ │ ├── Config/
│ │ │ └── Config.java # 加载 config.properties
│ │ ├── Domain/
│ │ │ └── GalleryTask.java # 下载任务领域模型
│ │ ├── ErrorCode/
│ │ │ └── ErrorCode.java # 错误码常量
│ │ ├── Externel/
│ │ │ └── BackupSubServer.java # 代理订阅文件服务(端口 8889)
│ │ ├── Message/
│ │ │ ├── AbstractMessage.java # 消息基类 + 消息类型常量
│ │ │ ├── MessageCodec.java # Netty 编解码器(ByteBuf ↔ AbstractMessage,JSON 格式)
│ │ │ └── Main/
│ │ │ ├── AvailableCheckMessage.java # 可用性检查(type=8)
│ │ │ ├── DeleteGalleryMessage.java # 删除画廊请求(type=3)
│ │ │ ├── DownloadPostMessage.java # 提交下载任务(type=1)
│ │ │ ├── DownloadStatusMessage.java # 上报任务状态(type=2)
│ │ │ ├── IdentityMessage.java # 身份认证握手(type=6)
│ │ │ ├── MaintainMessage.java # 心跳维持(type=7)
│ │ │ └── ResponseMessage.java # 通用响应(type=0)
│ │ └── Service/
│ │ ├── DeleteService.java # 删除画廊目录
│ │ ├── DownloadCheckService.java # 下载监控与压缩服务
│ │ ├── PrimaryChannelTracker.java # 主站通道引用登记与自愈
│ │ └── RekickPolicy.java # 主动重新唤起主站的限流判定
│ └── resources/
│ ├── config.properties # DouNai 订阅地址配置
│ ├── simplelogger.properties # SLF4J 日志配置(输出到 run.out)
│ └── reflect-config.json # GraalVM 反射配置(Jackson 序列化)
└── test/
└── java/ # 协议编解码、节点消息处理与上报循环、
# 订阅快照与分发、下载/压缩恢复、
# 主站通道自愈、删除与工具方法测试
```
---
## 关键依赖
| 依赖 | 版本 | 用途 |
|---|---|---|
| `io.netty:netty-all` | 4.1.138.Final | TCP 服务端/客户端 |
| `com.fasterxml.jackson.core:jackson-databind` | 2.22.2 | JSON 序列化 |
| `org.projectlombok:lombok` | 1.18.48 | 简化样板代码(`@Data`, `@Slf4j`) |
| `ch.qos.logback:logback-classic` | 1.5.38 | 日志实现 |
| `cn.hutool:hutool-all` | 5.8.47 | 文件操作、ZIP 压缩、HTTP 请求 |
| `org.apache.commons:commons-compress` | 1.28.0 | 压缩归档 |
| `org.graalvm.buildtools:native-maven-plugin` | 1.1.8 | 原生镜像配置(待验证) |
---
## 启动流程
1. `Main.main()` → 调用 `boot()`(遗留的 Netty Bootstrap),然后执行 `Config.loadConfig()`
2. 在新线程中启动 `BackupSubServer`(端口 8889)— 提供代理订阅文件下载
3. 在新线程中启动 `MultiThreadedHTTPServer`(端口 8888)— 提供画廊 ZIP 文件下载
4. 主线程创建 `storageNode()` 实例(阻塞构造函数,永不返回):
- 从端口 26321 开始查找空闲端口,绑定 Netty TCP 服务端
- 作为 TCP 客户端连接 `lionwebsite.xyz:26322~26342`,唤醒中心服务器
- 启动 `DownloadCheckService` 和 5 秒定时任务 `mainThread()`
---
## 通信协议(Netty TCP)
自定义协议格式:`[1字节类型] + [4字节长度] + [JSON 负载]`
| 类型字节 | 消息类 | 方向 |
|---|---|---|
| 0 | `ResponseMessage` | 响应 |
| 1 | `DownloadPostMessage` | 服务端 → 节点 |
| 2 | `DownloadStatusMessage` | 节点 → 服务端 |
| 3 | `DeleteGalleryMessage` | 服务端 → 节点 |
| 6 | `IdentityMessage` | 握手 |
| 7 | `MaintainMessage` | 心跳 |
| 8 | `AvailableCheckMessage` | 可用性检查 |
| 9 | `SubscriptionSnapshotMessage` | 主站 → 节点,完整订阅备机快照 |
---
## HTTP 服务
### MultiThreadedHTTPServer(端口 8888)
- 提供压缩后的画廊 ZIP 文件下载
- 仅接受来自 `lionwebsite.xyz` IP 的连接
- 支持 HTTP 206 Partial Content(断点续传)
- 参数:`AuthCode`(管理员访问)、`gid`(画廊 ID)
### BackupSubServer(端口 8889)
- 提供 V2Ray 和 Clash 代理订阅文件
- 不直接访问上游;主站完成下载和倍率过滤后,通过 Netty 类型 9 推送完整快照
- 按公开 Key 的 SHA-256 查找用户绑定的子账号,未知 Key 返回 404
- 快照经 SHA-256 和 HMAC 校验后原子落盘,主站离线时继续分发最后成功版本
- 文件存储于 `sub/snapshots/{revision}/accounts/{accountId}/`
- 没有有效快照或快照超过最大有效期时返回 503,不回退旧共享订阅
- `GET /health/subscription` 提供不含 Key 和订阅正文的快照状态
---
## 核心服务
### DownloadCheckService
- 扫描下载目录(`/root/gallery/hentai/download/`)监控进度
- 通过检测 `galleryinfo.txt` 文件判断下载完成
- 将完成的下载任务移入压缩队列
- 后台线程每 5 秒执行 ZIP 压缩,完成后删除源目录
### DeleteService
- 按名称删除画廊目录
- 失败时返回 `ErrorCode.IO_ERROR` 或 `ErrorCode.FILE_NOT_FOUND`
### PrimaryChannelTracker
- 登记「哪条通道是主站」,供任务状态上报与心跳使用
- 只有主站会发的消息类型(身份/任务下发/探活/订阅快照)才可用于认领引用;
备机的身份消息与节点自己的出站类型都不算证据
- 首选通道断开时立即回退到其它仍可用的已认证通道;引用被清空时,
已认证通道上的下一条消息即可恢复它,避免任务状态静默停止上报
### RekickPolicy
- 判定「有待上报任务、却无可用通道」时是否应主动重新唤起主站
- 以 30 秒为最小间隔限流,既能快速自愈,又不会在主站确实离线时形成重连风暴
---
## 配置说明
### config.properties(从 `/root/gallery/storageNode/config.properties` 加载)
```properties
SubscriptionSyncEnabled=false
SubscriptionSyncSecret=
SubscriptionDataDir=/root/gallery/storageNode/sub
SubscriptionMaxStaleSeconds=604800
SubscriptionMaxPayloadBytes=52428800
SubscriptionHttpPort=8889
SubscriptionHttpWorkers=4
SubscriptionSocketTimeoutMs=10000
```
生产同步密钥优先通过 `SUBSCRIPTION_SYNC_SECRET` 环境变量提供,不得提交到仓库或写入日志。
### simplelogger.properties
- 日志级别:`info`
- 时间戳格式:`yyyy-MM-dd HH:mm:ss`
- 输出文件:`run.out`
---
## 架构说明
- 单模块 Maven 项目,现有 62 个测试用例:协议编解码(MessageCodec)、节点消息处理与
状态上报循环、备机订阅分发 HTTP(8889)、订阅快照存取、下载/压缩恢复与 gid 匹配、
主站通道自愈、删除与工具方法
- 硬编码文件系统路径(`/root/gallery/...`)→ 仅限 Linux 部署
- 外部连接:`lionwebsite.xyz`、`personal.lionwebsite.xyz`、`aaaa.gay`
- GraalVM 原生镜像编译,包含 Jackson 反射配置
- 大量使用 Lombok(`@Data`、`@Slf4j`)
- 使用 Hutool 工具库处理文件/ZIP/HTTP 操作