LMCache 源码解析:LMCacheEngine 如何用分块 KV 缓存复用压低 LLM 首字延迟
2026/9/14 5:20:31
作为山西IT行业软件公司的项目负责人,我分析了公司产品部门提出的大文件传输功能需求,该需求具有以下关键点:
经过市场调研,现有开源组件存在以下问题:
建议采用商业解决方案或自主研发方案。
[客户端] ←HTTPS→ [Web服务器] ←→ [应用服务器] ←→ [存储服务] ↑ ↓ [数据库]// 文件上传组件核心代码示例(Vue2)exportdefault{data(){return{files:[],folders:[],uploadProgress:{},isUploading:false,chunkSize:10*1024*1024,// 10MB分片maxRetries:3}},methods:{asyncuploadFile(file,relativePath=''){constfileId=this.generateFileId(file);consttotalChunks=Math.ceil(file.size/this.chunkSize);// 检查服务器已上传分片const{uploadedChunks}=awaitthis.checkUploadStatus(fileId);for(letchunkIdx=0;chunkIdx<totalChunks;chunkIdx++){if(uploadedChunks.includes(chunkIdx))continue;constchunk=file.slice(chunkIdx*this.chunkSize,Math.min(file.size,(chunkIdx+1)*this.chunkSize));letretry=0;while(retry<this.maxRetries){try{awaitthis.uploadChunk(fileId,chunkIdx,totalChunks,chunk,relativePath);break;}catch(error){retry++;if(retry===this.maxRetries)throwerror;}}}awaitthis.completeUpload(fileId,file.name,file.size,relativePath);},asynchandleFolderUpload(folder){constentries=[...folder.webkitEntries||folder.items];for(constentryofentries){if(entry.isFile){constfile=awaitnewPromise(resolve=>entry.file(resolve));awaitthis.uploadFile(file,this.getRelativePath(entry));}elseif(entry.isDirectory){awaitthis.processDirectory(entry);}}}}}// 文件上传处理Servlet示例@WebServlet("/api/upload")publicclassUploadServletextendsHttpServlet{privatestaticfinalintBUFFER_SIZE=10*1024*1024;// 10MBprotectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse){StringfileId=request.getHeader("X-File-Id");intchunkIndex=Integer.parseInt(request.getHeader("X-Chunk-Index"));inttotalChunks=Integer.parseInt(request.getHeader("X-Total-Chunks"));StringrelativePath=request.getHeader("X-Relative-Path");try(InputStreamin=request.getInputStream();FileOutputStreamout=newFileOutputStream(getChunkPath(fileId,chunkIndex))){byte[]buffer=newbyte[BUFFER_SIZE];intbytesRead;while((bytesRead=in.read(buffer))!=-1){out.write(buffer,0,bytesRead);}// 更新数据库记录FileUploadDAO.updateChunkStatus(fileId,chunkIndex);// 如果所有分片已完成,合并文件if(FileUploadDAO.isUploadComplete(fileId,totalChunks)){mergeChunks(fileId,totalChunks,relativePath);encryptAndStoreToOSS(fileId);}response.setStatus(HttpServletResponse.SC_OK);}catch(Exceptione){response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);}}privatevoidmergeChunks(StringfileId,inttotalChunks,StringrelativePath){// 合并分片逻辑}privatevoidencryptAndStoreToOSS(StringfileId){// 加密并上传到OSS逻辑}}CREATETABLEfile_uploads(idVARCHAR(64)PRIMARYKEY,file_nameVARCHAR(255)NOTNULL,file_sizeBIGINTNOTNULL,relative_pathVARCHAR(1024),statusTINYINTNOTNULLDEFAULT0,created_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP,updated_atTIMESTAMPDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP);CREATETABLEfile_chunks(idINTAUTO_INCREMENTPRIMARYKEY,file_idVARCHAR(64)NOTNULL,chunk_indexINTNOTNULL,statusTINYINTNOTNULLDEFAULT0,created_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP,FOREIGNKEY(file_id)REFERENCESfile_uploads(id),UNIQUEKEY(file_id,chunk_index));CREATETABLEfile_transfers(idVARCHAR(64)PRIMARYKEY,source_pathVARCHAR(1024)NOTNULL,destination_pathVARCHAR(1024),transfer_typeENUM('UPLOAD','DOWNLOAD')NOTNULL,statusENUM('PENDING','PROCESSING','COMPLETED','FAILED')NOTNULLDEFAULT'PENDING',created_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP,updated_atTIMESTAMPDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP);// 加密服务接口publicinterfaceEncryptionService{InputStreamencrypt(InputStreaminputStream,Stringkey)throwsEncryptionException;InputStreamdecrypt(InputStreaminputStream,Stringkey)throwsEncryptionException;StringgetAlgorithm();}// SM4实现publicclassSM4EncryptionServiceimplementsEncryptionService{@OverridepublicInputStreamencrypt(InputStreaminputStream,Stringkey){// SM4加密实现}@OverridepublicInputStreamdecrypt(InputStreaminputStream,Stringkey){// SM4解密实现}@OverridepublicStringgetAlgorithm(){return"SM4";}}// AES实现publicclassAESEncryptionServiceimplementsEncryptionService{@OverridepublicInputStreamencrypt(InputStreaminputStream,Stringkey){// AES加密实现}@OverridepublicInputStreamdecrypt(InputStreaminputStream,Stringkey){// AES解密实现}@OverridepublicStringgetAlgorithm(){return"AES";}}根据公司需求,推荐考虑以下商业解决方案:
这些商业方案的优势:
| 项目 | 自主研发预算 | 商业方案预算 | 时间估算 |
|---|---|---|---|
| 需求分析 | 5万 | 0 | 2周 |
| 技术选型 | 3万 | 5万 | 2周 |
| 开发实施 | 60万 | 30万 | 3-6个月 |
| 测试验收 | 15万 | 10万 | 1-2个月 |
| 维护支持 | 15万/年 | 10万/年 | 持续 |
| 总计 | 98万+ | 55万+ | 6-9个月 |
考虑到公司200+项目/年的规模,商业买断方案(预算98万以内)更具性价比,可节省长期授权费用和开发维护成本。
导入到Eclipse:点南查看教程
导入到IDEA:点击查看教程
springboot统一配置:点击查看教程
NOSQL示例不需要任何配置,可以直接访问测试
选择对应的数据表脚本,这里以SQL为例
up6/upload/年/月/日/guid/filename
支持离线保存文件进度,在关闭浏览器,刷新浏览器后进行不丢失,仍然能够继续上传
支持上传文件夹并保留层级结构,同样支持进度信息离线保存,刷新页面,关闭页面,重启系统不丢失上传进度。
支持文件批量下载
文件下载支持离线保存进度信息,刷新页面,关闭页面,重启系统均不会丢失进度信息。
支持下载文件夹,并保留层级结构,不打包,不占用服务器资源。
点击下载完整示例