gongxiangp2/lib/chunk-upload-config.ts
2026-05-27 18:16:30 +08:00

189 lines
5.3 KiB
TypeScript
Raw Permalink 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.

/**
* 分片上传配置
* 按网络环境选择分片大小和并发数。
*
* 策略:
* - 内网:20MB 分片,2 并发
* - 公网:10MB 分片,1 并发
* - 公网链路经过 nginx + FRP,过大的单请求会放大连接抖动和 499/502 风险
*/
import { getNetworkStatus, initializeNetwork } from './network'
/**
* 获取最优分片大小
* @returns 分片大小(字节)
*/
export async function getOptimalChunkSize(): Promise<number> {
let network = getNetworkStatus()
if (!network.lastChecked) {
try {
network = await initializeNetwork()
} catch (error) {
console.warn('[ChunkConfig] 网络检测失败,使用公网保守分片(10MB):', error)
return 10 * 1024 * 1024
}
}
const chunkSize = network.isInternal
? 20 * 1024 * 1024
: 10 * 1024 * 1024
console.log(`[ChunkConfig] 检测到${network.isInternal ? '内网' : '公网'}访问,使用 ${formatChunkSize(chunkSize)} 分片`)
return chunkSize
}
/**
* 同步获取分片大小
* @returns 分片大小(字节)
*/
export function getChunkSizeSync(): number {
const network = getNetworkStatus()
const chunkSize = network.isInternal
? 20 * 1024 * 1024
: 10 * 1024 * 1024
console.log(`[ChunkConfig] 使用 ${formatChunkSize(chunkSize)} 分片(同步)`)
return chunkSize
}
/**
* 获取超时时间(根据分片大小动态调整)
* 假设最低速度 300KB/s,加上100%缓冲(更保守)
* @param chunkSize 分片大小(字节)
* @returns 超时时间(毫秒)
*/
export function getChunkTimeout(chunkSize: number): number {
const minSpeed = 300 * 1024 // 300KB/s(更保守的最低速度)
const baseTimeout = (chunkSize / minSpeed) * 1000
// 加上100%缓冲,确保不会轻易超时
const timeout = Math.ceil(baseTimeout * 2)
// 最小5分钟,最大10分钟
return Math.max(300000, Math.min(600000, timeout))
}
/**
* 获取分片大小的可读字符串
* @param chunkSize 分片大小(字节)
* @returns 可读字符串(如 "10MB")
*/
export function formatChunkSize(chunkSize: number): string {
return `${(chunkSize / 1024 / 1024).toFixed(0)}MB`
}
/**
* 获取最优并发数(根据网络环境和系统负载动态调整)
*
* 动态调整策略:
* - 内网用户:2 并发
* - 公网用户:1 并发
* - 公网链路经过 FRP,优先减少大请求并发,避免上传到 100% 后等待/重试过久
*
* @returns 并发数(1-2)
*/
export async function getOptimalConcurrency(): Promise<number> {
// 获取当前网络状态
let network = getNetworkStatus()
// 如果还没检测过,先初始化网络检测
if (!network.lastChecked) {
try {
network = await initializeNetwork()
} catch (error) {
console.warn('[ChunkConfig] 网络检测失败,使用默认并发数(2):', error)
return 2
}
}
// 基础并发数
let concurrency: number
if (network.isInternal) {
// 内网用户:2并发
concurrency = 2
} else {
// 公网用户:1并发
concurrency = 1
}
// 确保在1-2范围内
concurrency = Math.max(1, Math.min(2, concurrency))
console.log(`[ChunkConfig] 检测到${network.isInternal ? '内网' : '公网'}访问,使用 ${concurrency} 个并发(1~2范围)`)
return concurrency
}
/**
* 同步获取并发数(如果网络状态已缓存)
* 如果未检测过,返回默认值(2并发)
* @returns 并发数(1-3)
*/
export function getConcurrencySync(): number {
const network = getNetworkStatus()
// 如果还没检测过,返回公网保守值(1并发)
if (!network.lastChecked) {
console.warn('[ChunkConfig] 网络状态未检测,使用默认并发数(1)')
return 1
}
// 同步版本:使用固定策略(不依赖异步检测)
let concurrency: number
if (network.isInternal) {
concurrency = 2 // 内网:2并发
} else {
concurrency = 1 // 公网:1并发
}
// 确保在1-2范围内
concurrency = Math.max(1, Math.min(2, concurrency))
return concurrency
}
/**
* 根据实际上传速度动态调整并发数
*
* 调整策略:
* - 速度 > 5MB/s:降低并发(避免浪费资源)
* - 速度 2-5MB/s:保持当前并发
* - 速度 < 2MB/s:增加并发(最高3)
*
* @param currentConcurrency 当前并发数
* @param currentSpeed 当前上传速度(字节/秒)
* @returns 调整后的并发数(1-3)
*/
export function adjustConcurrencyBySpeed(currentConcurrency: number, currentSpeed: number): number {
const speedMBps = currentSpeed / (1024 * 1024)
let newConcurrency = currentConcurrency
if (speedMBps > 5) {
// 速度很快,降低并发(节省资源)
newConcurrency = Math.max(1, currentConcurrency - 1)
} else if (speedMBps < 2 && currentConcurrency < 3) {
// 速度慢,增加并发(最多3,避免服务器过载)
newConcurrency = Math.min(3, currentConcurrency + 1)
}
if (newConcurrency !== currentConcurrency) {
console.log(`[ChunkConfig] 动态调整并发: ${currentConcurrency} → ${newConcurrency} (当前速度: ${speedMBps.toFixed(2)}MB/s)`)
}
return newConcurrency
}
/**
* 获取并发数的可读字符串
* @param concurrency 并发数
* @returns 可读字符串(如 "2个并发")
*/
export function formatConcurrency(concurrency: number): string {
return `${concurrency}个并发`
}