深度优化OSS上传性能:多线程分片上传 vs 断点续传实战对比

1 卸载开头

对象存储服务(OSS)已成为现代应用架构的核心组件,但随着业务规模扩大,文件上传性能问题日益凸显。本文将深入探讨两种核心优化技术:多线程分片上传断点续传,通过理论分析、代码实现和性能测试,揭示它们在不同场景下的表现差异与最佳实践。

2 理论基础与性能瓶颈分析

2.1 上传性能关键指标

指标计算公式影响因素
上传吞吐量文件大小/总耗时网络带宽、并发数、IO性能
资源利用率(CPU使用率+内存使用率)/2线程管理、缓冲区大小
任务完成时间T = T_connect + T_transfer网络延迟、分片策略
失败恢复成本重传数据量/总数据量检查点频率、错误处理机制

2.2 单线程上传瓶颈模型

def single_thread_upload(file, endpoint):start = time.time()connection = create_connection(endpoint)  # 建立连接耗时 T_connectupload_data(connection, file)             # 数据传输耗时 T_transferconnection.close()return time.time() - start

性能瓶颈分析

  • 网络延迟放大效应:RTT(往返时延)对小型文件影响显著
  • TCP拥塞窗口限制:单连接无法充分利用可用带宽
  • 无故障恢复机制:网络中断导致整个上传失败

3 多线程分片上传深度优化

3.1 技术原理与架构设计

源文件
分片切割
分片1
分片2
分片...
线程池
并发上传
OSS服务端
分片存储
合并请求
完整文件

关键优化点

  • 分片策略:动态分片 vs 固定分片
  • 线程管理:有界队列 vs 无界队列
  • 流量控制:令牌桶算法实现

3.2 核心代码实现

// 分片上传核心逻辑
public class MultipartUploader {private static final int PART_SIZE = 5 * 1024 * 1024; // 5MB分片public void upload(File file, String bucketName) {// 初始化分片上传InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(bucketName, file.getName());InitiateMultipartUploadResult initResponse = ossClient.initiateMultipartUpload(initRequest);// 创建线程池ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);List<Future<PartETag>> futures = new ArrayList<>();// 分片并提交任务long fileLength = file.length();int partCount = (int) (fileLength / PART_SIZE);if (fileLength % PART_SIZE != 0) partCount++;for (int i = 0; i < partCount; i++) {long startPos = i * PART_SIZE;long curPartSize = Math.min(PART_SIZE, fileLength - startPos);UploadPartTask task = new UploadPartTask(initResponse.getUploadId(), bucketName, file.getName(), file, startPos, curPartSize, i + 1);futures.add(executor.submit(task));}// 等待所有分片完成List<PartETag> partETags = new ArrayList<>();for (Future<PartETag> future : futures) {partETags.add(future.get());}// 合并分片CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(bucketName, file.getName(), initResponse.getUploadId(), partETags);ossClient.completeMultipartUpload(compRequest);}
}// 分片上传任务
class UploadPartTask implements Callable<PartETag> {// 实现分片上传细节@Overridepublic PartETag call() throws Exception {// 读取文件分片// 创建UploadPartRequest// 执行分片上传// 返回PartETag}
}

3.3 性能优化策略

分片大小自适应算法

def calculate_part_size(file_size):# 根据文件大小动态调整分片if file_size <= 50 * 1024 * 1024:   # <50MBreturn 1 * 1024 * 1024          # 1MB分片elif file_size <= 5 * 1024 * 1024 * 1024: # <5GBreturn 5 * 1024 * 1024          # 5MB分片else:return 10 * 1024 * 1024         # 10MB分片

线程池优化配置

// 基于带宽的动态线程池
int maxThreads = (int) (NetworkMonitor.getAvailableBandwidth() / (PART_SIZE / 1024.0));
executor = new ThreadPoolExecutor(corePoolSize, maxThreads, 60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1000),new ThreadPoolExecutor.CallerRunsPolicy());

3.4 性能测试数据

测试环境:AWS S3,100MB文件,100Mbps带宽

分片大小线程数上传时间(s)CPU使用率(%)内存占用(MB)
1MB3212.385120
5MB169.86585
10MB811.54560
单线程182.41530

结论:5MB分片大小配合16线程在此环境下达到最优平衡

4 断点续传技术深度解析

4.1 技术原理与故障恢复机制

Client OSS Server Metadata DB 1. 发起上传请求 2. 返回Upload ID 3. 上传分片N 4. 返回ETag 5. 保存进度(UploadID+PartNum+ETag) loop [分片上传] 6. 查询未完成的分片 7. 返回缺失分片列表 8. 续传缺失分片 alt [网络中断] 9. 完成上传 10. 返回最终ETag Client OSS Server Metadata DB

4.2 断点续传核心实现

// 断点续传管理器
type ResumeUploader struct {uploadID    stringpartTracker *PartTracker // 分片状态跟踪器
}func (u *ResumeUploader) Upload(file *os.File) error {// 尝试加载进度if err := u.loadProgress(); err != nil {// 初始化上传u.initUpload()}// 获取待上传分片parts := u.partTracker.GetPendingParts()var wg sync.WaitGroupfor _, part := range parts {wg.Add(1)go func(p Part) {defer wg.Done()// 上传分片etag := u.uploadPart(file, p)// 更新进度u.partTracker.CompletePart(p.Number, etag)u.saveProgress()}(part)}wg.Wait()// 完成上传return u.completeUpload()
}// 分片状态跟踪
type PartTracker struct {parts map[int]PartStatus // 分片号->状态
}type PartStatus struct {Start    int64End      int64ETag     stringComplete bool
}

4.3 断点恢复优化策略

智能进度保存策略

def save_upload_progress(upload_id, part_num, etag):# 高频小分片:每完成5个分片保存一次# 低频大分片:每个分片完成后立即保存# 超时分片:每30秒强制保存if part_num % 5 == 0 or part_size > 10*1024*1024:persist_to_db(upload_id, part_num, etag)else:cache_in_memory(upload_id, part_num, etag)

分片校验机制

// 恢复上传时校验分片完整性
public boolean verifyPart(String uploadId, int partNumber, String expectedEtag) {ListPartsRequest listPartsRequest = new ListPartsRequest(bucket, key, uploadId);PartListing partListing = ossClient.listParts(listPartsRequest);for (PartSummary part : partListing.getParts()) {if (part.getPartNumber() == partNumber) {return part.getETag().equals(expectedEtag);}}return false;
}

4.4 故障恢复性能测试

测试场景:500MB文件上传,人为在50%进度时中断网络

恢复策略恢复时间(s)重复上传数据量(MB)最终一致性
无断点续传45.2500可能损坏
基础断点续传22.7250可靠
智能进度保存18.3250可靠
分片校验+智能保存19.10(仅校验)高可靠

5 多线程分片上传 vs 断点续传实战对比

5.1 性能对比测试

测试环境:阿里云OSS,1Gbps带宽,8核16GB内存

文件大小技术方案平均上传时间(s)失败恢复成本CPU峰值(%)内存峰值(MB)
100MB单线程82.4100%1530
100MB多线程分片(8线程)9.8100%6585
100MB断点续传11.225%4060
1GB多线程分片38.5100%85220
1GB断点续传45.730%55180
10GB多线程分片315.2100%90520
10GB断点续传348.615%65450

5.2 技术特性对比

特性多线程分片上传断点续传
主要优势极致吞吐性能高可靠性和故障恢复能力
适用场景稳定网络环境、大型文件不稳定网络、关键业务数据
资源消耗高(CPU/内存/网络连接)中等
实现复杂度中等高(需状态管理)
小文件性能差(管理开销大)
最大文件限制无(OSS支持最大48.8TB)
网络中断恢复成本高(通常需重传整个文件)低(仅需重传未完成分片)
客户端存储需求需存储上传状态

5.3 决策树:技术选型指南

小于10MB
10MB-1GB
大于1GB
稳定
不稳定
开始
文件大小
单次上传
网络稳定性
多线程分片+断点续传
多线程分片
断点续传
结束

6 混合方案设计与实战

6.1 架构设计:分片上传+断点续传

客户端
分片管理器
线程池控制器
上传工作线程
OSS服务器
状态存储器
本地数据库
云数据库
故障检测器

6.2 混合方案核心实现

class HybridUploader {private uploadId: string;private partTracker: PartTracker;private pauseSignal = false;async startUpload(file: File) {// 初始化或恢复上传if (!this.uploadId) {this.uploadId = await this.initOSSMultipartUpload();}// 加载或初始化分片状态this.partTracker = await PartTracker.load(file, this.uploadId) || new PartTracker(file, this.uploadId);// 创建智能线程池const threadPool = new AdaptiveThreadPool();// 上传任务处理while (!this.partTracker.isComplete()) {if (this.pauseSignal) {await this.saveProgress();throw new UploadPausedException();}const parts = this.partTracker.getNextParts(threadPool.availableSlots());parts.forEach(part => {threadPool.submit(async () => {try {const etag = await this.uploadPart(part);this.partTracker.completePart(part.number, etag);this.autoSaveProgress();} catch (err) {this.partTracker.failPart(part.number);this.handleError(err);}});});await sleep(100); // 避免CPU空转}// 完成上传await this.completeUpload();}pause() { this.pauseSignal = true; }resume() { this.pauseSignal = false; this.startUpload(); }
}

6.3 自适应线程池实现

public class AdaptiveThreadPool {private ThreadPoolExecutor executor;private NetworkMonitor networkMonitor;public AdaptiveThreadPool() {this.networkMonitor = new NetworkMonitor();this.executor = new ThreadPoolExecutor(4, // 核心线程数32, // 最大线程数60, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1000));// 启动监控线程new Thread(this::monitorAndAdjust).start();}private void monitorAndAdjust() {while (true) {// 基于网络状况调整double packetLoss = networkMonitor.getPacketLossRate();if (packetLoss > 0.1) {executor.setCorePoolSize(4); // 高丢包时减少并发} else {int suggested = (int)(NetworkMonitor.getAvailableBandwidth() / (5 * 1024));executor.setCorePoolSize(Math.min(32, Math.max(4, suggested)));}// 基于队列深度调整if (executor.getQueue().size() > 500) {executor.setMaximumPoolSize(Math.min(64, executor.getMaximumPoolSize() + 4));}Thread.sleep(5000); // 每5秒调整一次}}
}

6.4 混合方案性能对比

测试场景:1GB文件上传,模拟3次网络中断

方案总耗时(s)有效吞吐(Mbps)重传数据比例客户端资源占用
纯多线程分片失败-100%
纯断点续传78.5104.318%
混合方案(基础)42.7191.512%中高
混合方案(自适应)38.2214.29%
混合方案+智能分片36.8222.47%

7 进阶优化策略

7.1 分片策略优化算法

动态分片算法

def calculate_dynamic_part_size(file_size, network_quality):"""基于文件大小和网络状况的动态分片算法:param file_size: 文件大小(bytes):param network_quality: 网络质量评分(0-1):return: 最优分片大小(bytes)"""# 基础分片大小base = 5 * 1024 * 1024  # 5MB# 根据文件大小调整if file_size > 10 * 1024 * 1024 * 1024:  # >10GBbase = 20 * 1024 * 1024elif file_size > 1 * 1024 * 1024 * 1024:  # >1GBbase = 10 * 1024 * 1024# 根据网络质量调整if network_quality < 0.3:  # 差网络return max(1 * 1024 * 1024, base / 2)elif network_quality > 0.8:  # 优质网络return min(100 * 1024 * 1024, base * 2)return base

7.2 智能重试机制

public class SmartRetryPolicy {private static final int MAX_RETRIES = 5;private static final long BASE_DELAY = 1000; // 1spublic void executeWithRetry(Runnable task) {int retryCount = 0;while (retryCount <= MAX_RETRIES) {try {task.run();return;} catch (NetworkException e) {retryCount++;long delay = calculateBackoff(retryCount);Thread.sleep(delay);} catch (NonRetriableException e) {throw e;}}throw new MaxRetriesExceededException();}private long calculateBackoff(int retryCount) {// 指数退避+随机抖动long expDelay = (long) Math.pow(2, retryCount) * BASE_DELAY;long jitter = (long) (Math.random() * 1000);return expDelay + jitter;}
}

7.3 客户端资源优化

内存管理策略

type MemoryPool struct {pool chan []byte
}func NewMemoryPool(blockSize int, maxBlocks int) *MemoryPool {return &MemoryPool{pool: make(chan []byte, maxBlocks),}
}func (p *MemoryPool) Get() []byte {select {case buf := <-p.pool:return bufdefault:return make([]byte, blockSize)}
}func (p *MemoryPool) Put(buf []byte) {select {case p.pool <- buf:default: // 池已满,丢弃缓冲区}
}

8 真实场景性能测试

8.1 测试环境配置

组件配置
OSS服务阿里云标准型OSS
客户端主机AWS EC2 c5.4xlarge
网络环境跨区域(北京OSS vs 东京EC2)
测试工具自研压力测试框架
测试文件集混合大小(1MB-10GB)

8.2 大规模测试数据

测试规模:1000个并发客户端,总计上传100TB数据

技术方案总耗时(小时)平均吞吐(Gbps)失败率(%)恢复时间(avg)
单线程上传38.25.812.5N/A
多线程分片6.733.28.3>5min
断点续传8.925.01.228s
混合方案5.242.80.712s
混合方案+优化4.549.40.38s

9 结论与最佳实践

9.1 技术选型决策矩阵

场景特征推荐技术方案配置建议
小文件(<10MB)直接上传单次请求
大文件(>100MB)+稳定网络多线程分片分片5-10MB, 线程数=核心数×2
大文件+不稳定网络断点续传检查点间隔=10分片
超大文件(>10GB)混合方案自适应分片+智能线程池
关键业务数据混合方案+增强校验MD5分片校验+进度持久化
移动端环境精简断点续传大分片+低频保存

9.2 性能优化检查清单

  1. 分片策略优化

    • ☑ 根据文件大小动态调整分片
    • ☑ 网络质量差时减小分片尺寸
    • ☑ 限制最小分片大小(>1MB)
  2. 并发控制

    • ☑ 基于可用带宽动态调整线程数
    • ☑ 实现有界队列防止内存溢出
    • ☑ 添加网络拥塞检测机制
  3. 故障恢复

    • ☑ 实现原子化的进度保存
    • ☑ 添加分片完整性校验
    • ☑ 设计指数退避重试策略
  4. 资源管理

    • ☑ 使用内存池复用缓冲区
    • ☑ 限制最大并发连接数
    • ☑ 实现上传速率限流

9.3 优化方向

  1. AI驱动的参数调优

    class AITuner:def optimize_parameters(self, file_size, network_stats, hw_spec):# 使用强化学习模型预测最优参数model = load_model("upload_optimizer.h5")return model.predict([file_size, network_stats.latency, network_stats.bandwidth,hw_spec.cpu_cores,hw_spec.memory])
    
  2. 跨区域分片上传

    最优区域
    备用区域
    客户端
    区域选择器
    分片上传器1
    分片上传器2
    区域1 OSS
    区域2 OSS
    全局合并服务
    最终存储
  3. UDP加速传输协议

    +---------------------+---------------------+
    | 传统TCP上传         | QUIC加速上传        |
    +---------------------+---------------------+
    | 3次握手建立连接     | 0-RTT快速启动       |
    | 队头阻塞问题        | 多路复用无阻塞      |
    | 拥塞控制反应慢      | 改进的拥塞算法      |
    | 移动网络切换中断    | 连接迁移支持        |
    +---------------------+---------------------+
    

附录:性能优化工具包

10.1 OSS性能测试脚本

#!/bin/bash
# oss_benchmark.sh
FILE_SIZES=("10m" "100m" "1g" "10g")
THREADS=(4 8 16 32)
METHODS=("single" "multipart" "resumable")for size in "${FILE_SIZES[@]}"; dofor thread in "${THREADS[@]}"; dofor method in "${METHODS[@]}"; doecho "Testing ${size} file with ${thread} threads (${method})"./upload_tool --size $size --threads $thread --method $method --output report_${size}_${thread}_${method}.jsondonedone
done# 生成可视化报告
python analyze_results.py

10.2 监控指标采集

def collect_metrics():return {"timestamp": time.time(),"network": {"bandwidth": get_available_bandwidth(),"latency": measure_latency("oss-endpoint"),"packet_loss": get_packet_loss_rate()},"system": {"cpu_usage": psutil.cpu_percent(),"memory_usage": psutil.virtual_memory().percent,"io_wait": psutil.cpu_times().iowait},"upload": {"progress": current_progress,"current_speed": calculate_instant_speed(),"active_threads": threading.active_count()}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/news/911661.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

doris_工作使用整理

文章目录 前言一、doris整体情况二、doris的存储过程情况1.分类2. 同步物化视图3. 异步物化视图三,分区相关1.分区建的过多前言 提示:doris使用版本3.x 提示:以下是本篇文章正文内容,下面案例可供参考 一、doris整体情况 细节放大 二、doris的存储过程情况 1.分类 按…

左神算法之单辅助栈排序算法

目录 1. 题目2. 解释3. 思路4. 代码5. 总结 1. 题目 请编写一个程序&#xff0c;对一个栈里的整型数据&#xff0c;按升序进行排序&#xff08;即排序前栈里的数据是无序的&#xff0c;排序后最大元素位于栈顶&#xff09;。要求最多只能使用一个额外的栈存放临时数据&#xf…

使用Trae编辑器与MCP协议构建高德地图定制化服务

目录 一、使用Trae编辑器配置高德MCP Server 1.1 Trae介绍 1.2 从mcp.so中获取配置高德地图mcp server配置信息 1.3 高德地图开发者配置 1.4 添加Filesystem 到Trae 1.5 使用结果展示 1.6 MCP常见命令行工具和包管理说明 1.7 Function Call工具和MCP技术对比 二、本地…

【LLaMA-Factory 实战系列】三、命令行篇 - YAML 配置与高效微调 Qwen2.5-VL

【LLaMA-Factory 实战系列】三、命令行篇 - YAML 配置与高效微调 Qwen2.5-VL 1. 引言2. 为什么从 WebUI 转向命令行&#xff1f;3. 准备工作&#xff08;回顾&#xff09;4. 核心&#xff1a;创建并理解训练配置文件4.1 选择并复制基础模板4.2 逐一解析与修改配置文件4.3 参数详…

推荐:ToB销售B2B销售大客户营销大客户销售培训师培训讲师唐兴通讲销售技巧数字化销售销AI销售如何有效获取客户与业绩

站在AI浪潮之巅&#xff0c;重塑销售之魂 在AI时代&#xff0c;普通销售人员&#xff08;TOB、TOC&#xff09;除了传统的销售动作之外&#xff0c;还能做什么&#xff1f;怎么做&#xff1f; 这是《AI销冠》这本书想探讨的核心问题。 特别喜欢编辑老师总结的&#xff1a; 读者…

爬取小红书相关数据导入到excel

本期我们来进行实战,爬取小红书的相关数据导入到excel中,后续可进行些数据分析,今后或者已经在运营小红书的小伙伴应该比较喜欢这些数据。今天我们的主角是DrissionPage,相对于之前介绍的selenium省去了很多的配置,直接安装了就能使用。 DrissionPage 是一个基于 python …

c++面试题每日一学记录- C++对象模型与内存对齐深度原理详解

一、C++对象模型核心原理 1. 对象内存布局基础原理 设计哲学: 零开销原则:不为未使用的特性付出代价(如无虚函数则无vptr)兼容性:C结构体在C++中保持相同内存布局多态支持:通过虚函数表实现运行时动态绑定内存布局实现机制: 编译器处理步骤: 成员排列:严格按声明顺序…

Kafka 监控与调优实战指南(二)

五、Kafka 性能问题剖析 5.1 消息丢失 消息丢失是 Kafka 使用过程中较为严重的问题&#xff0c;可能由多种原因导致。在生产者端&#xff0c;如果配置不当&#xff0c;比如将acks参数设置为0&#xff0c;生产者发送消息后不会等待 Kafka broker 的确认&#xff0c;就继续发送…

Linux下SVN报错:Unable to connect to a repository at URL ‘svn://XXX‘

一、问题描述 Linux下通过SVN执行提交&#xff08;commit&#xff09;操作时报错&#xff1a;Unable to connect to a repository at URL svn://XXX&#xff1a; 二、解决方法 导致该问题的一个可能原因是远程仓库的URL发生变化了&#xff0c;即svn服务器的ip变更了。这时可…

Modbus 扫描 从站号、波特率

下载链接&#xff1a;https://pan.quark.cn/s/533ceb8e397d 下载链接: https://pan.baidu.com/s/1PQHn-MwfzrWgF2UrXQDoGg 提取码: 1111

Docker 容器通信与数据持久化

目录 简介 一、Docker 容器通信 1. Docker 网络模式 2. Bridge 模式 3. Host 模式 4. Container 模式 5. Overlay 模式 6. 端口映射&#xff1a;容器与外部的桥梁 7. 容器互联&#xff1a;从 --link 到自定义网络 二、Docker 数据持久化 1. 数据卷&#xff1a;Docke…

【教学类-89-08】20250624新年篇05——元宵节灯笼2CM黏贴边(倒置和正立数字 )

背景需求&#xff1a; 【教学类-89-06】20250220新年篇05——元宵节灯笼2CM黏贴边&#xff08;3边形到50边形&#xff0c;一页1图、2图、4图&#xff0c;适合不同水平&#xff0c;适合不同阶段&#xff09;-CSDN博客文章浏览阅读1.6k次&#xff0c;点赞35次&#xff0c;收藏27…

【DB2】SQL0104N An unexpected token “OCTETS“ was found following “……

db2创建表时报标题的错误&#xff0c;建表语句如下 db2 "CREATE TABLE YS.TEST_1(ID VARCHAR(64 OCTETS))"去掉octets就好了 经过测试&#xff0c;在9.7版本报错&#xff0c;在10.5.11没问题&#xff0c;怀疑版本差异导致 在官网查找资料&#xff0c;应该是10.5才…

暴雨以信创委员会成员单位身份参与南京专题活动

6月19日&#xff0c;中国电子工业标准化技术协会信息技术应用创新工作委员会&#xff08;简称信创工委会&#xff09;联合南京市工业和信息化局共同举办的“智启未来&#xff1a;AI赋能信息技术应用创新办公新势力”专题活动在南京成功举办。南京市工业和信息化局副局长代吉上、…

基于keepalived、vip实现高可用nginx (centos)

基于keepalived、vip实现高可用nginx &#xff08;centos&#xff09; 1、安装keepalived yum install keepalived2、选同一局域网空置ip作vip 我这里测试是&#xff1a; 主&#xff1a;192.168.163.134 副&#xff1a;192.168.163.135 vip&#xff1a;192.168.163.1403、ke…

使用 launch 启动 rviz2 并加载机器人模型

视频资料&#xff1a;《ROS 2机器人开发从入门到实践》6.2.2 在RViz中显示机器人_哔哩哔哩_bilibili 1、创建工作空间 chapt6_ws/src&#xff0c;创建包 fishrobot_description ros2 create fishrobot_description --build-type ament_cmake --license Apache-2.0 2、创建机器…

华为云Flexus+DeepSeek征文 | 基于CCE容器的AI Agent高可用部署架构与弹性扩容实践

华为云FlexusDeepSeek征文 | 基于CCE容器的AI Agent高可用部署架构与弹性扩容实践 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 总有一行代码&#xff0c;能点亮万千星辰。 &#x1f50d; 在技术的宇宙中&#xff0c;我愿做永不停歇的探索者。 …

Python学习Day41

学习来源&#xff1a;浙大疏锦行 知识回顾 数据增强卷积神经网络定义的写法batch归一化&#xff1a;调整一个批次的分布&#xff0c;常用与图像数据特征图&#xff1a;只有卷积操作输出的才叫特征图调度器&#xff1a;直接修改基础学习率 卷积操作常见流程如下&#xff1a; …

数组题解——最长回文子串【LeetCode】

5. 最长回文子串 一、向右拓展 算法思路 你用res记录当前找到的最长回文子串。每次遍历到s[i]时&#xff0c;尝试找到以s[i]结尾的、比当前res更长的回文子串。 先尝试长度为len(res)2&#xff08;即起点i-len(res)-1&#xff09;的子串&#xff0c;看是不是回文。如果不是&…

✨从零搭建 Ubuntu22.04 + Python3.11 + PyTorch2.5.1 GPU Docker 镜像并上传 Docker Hub

&#x1f680; 从零搭建 Ubuntu22.04 Python3.11 PyTorch2.5.1 GPU Docker 镜像并上传 Docker Hub 在 AI 项目开发中&#xff0c;构建统一的运行环境是一件非常重要的事情。使用 Docker 可以极大地提升部署效率、保证环境一致性。本文将手把手带你&#xff1a; ✅ 构建一个…