前言
- 本地上傳大文件內存溢出 Direct buffer memory
- 附件下載服務端傳流給前端需要將流緩存完畢才可以下載,導致大文件下載系統(tǒng)崩潰
一、Direct buffer memory
后端服務采用nio本地上傳到服務器指定目錄,通過nginx代理提供下載
1.1 原因分析
Java8出現(xiàn)了NIO,緩存,通道,選擇器。在 寫NIO程序的時候,經常使用ByteBuffer來讀或者寫入數據,這是一種基于通道(
Channel)與緩沖區(qū)( Buffer)的I/0方式.它可以使用 Native函數庫直接分配堆外內存,然后通過一個存做在Java里面的
DirectByteBuffer對作為這塊內存的引用進行操作??梢蕴岣咝阅?因為避免了在Java堆和 Native堆中來回復制數據。
ByteBuffer. allocate(capability)第一種方式是分配java堆內存,屬于GC管理范圍,由于需要進行拷貝,所以比較慢。
ByteBuffer. allocteDirect (capability)第一種方式是分配操作系統(tǒng)的本地內存,不屬于GC管轄范圍,由于不需要內存拷貝所以速度相對較快。但如果不斷分配本地內存,堆內存很少使用,那么java虛擬機就不需要進行GC,
DirectByteBuffer對象們就不會被回收,這個時候堆內存充足,但本地內存可能已經使用光了,在嘗試分配本地內存就會出0ut0fMemory
Error,那程序就直接奔潰了。
linux 清理內存命令echo 1 > /proc/sys/vm/drop_caches
1.2 解決方案
拋棄nio,特別是對大數據流場景,這里改用普通io操作數據流(后續(xù)引發(fā)問題分析)
二、附件下載
2.1 問題分析
告別Direct buffer memory的后續(xù)問題
無論是本地下載還是minio下載都存在一個隱形問題。
前端blog請求后,要等服務端將整個附件流傳輸完畢后才可以下載,對大文件(600M)不友好,大概率卡死。
F12 可以看到接口響應的大小一直在上升,直到整個文件傳輸完畢瀏覽器才會響應。
我們可以看到互聯(lián)網下載都是在瀏覽器下載管理器下載,那么我們如何實現(xiàn)?
實際上本地下載我們通過nginx代理后的地址可以直接訪問文件地址進行下載,滿足上述要求。但是這樣的操作會導致附件下載繞過后端直接請求,無法防止盜鏈。minio上傳無法通過nginx實現(xiàn)(不考慮下載到服務器nginx代理,這樣不合理)。
2.2 解決方案
2.2.1 本地下載
nginx + 重定向X-Accel-Redirect
這個功能允許你在后端處理權限,日志或任何你想干的,Nginx提供內容服務給終端用戶從重定向后的路徑,因此可以釋放后端去處理其他請求(直接由Nginx提供IO,而不是后端服務)。這個功能類似 X-Sendfile 。
具體步驟篇幅太長請移步鏈接
2.2.1 minio下載
-
minio管理界面可以截取到下載路徑,模擬他的下載即可
-
連接地址發(fā)現(xiàn)需要傳入minio的登錄token,下一步想辦法獲取token
-
高版本支持多用戶,可創(chuàng)建臨時用戶獲取token(8.4.3)
-
高版本管理界面下載路徑變了,
【minio ip port】+/api/v1/buckets/+【bucket】+/objects/download?prefix=【Base64.*encode(附件路徑)】+\&token=* + *token*
-
直接訪問上一步的url即可下載
https://github.com/minio/minio-java/tree/release/examples文章來源:http://www.zghlxwxcb.cn/news/detail-468665.html
public Credentials getCredentials() {
int durationSeconds = 360000;//秒
//創(chuàng)建簽名對象
AssumeRoleProvider provider = new AssumeRoleProvider(
properties.getUrl(),
properties.getAccessKey(),
properties.getSecretKey(),
durationSeconds,//默認3600秒失效,設置小于這個就是3600,大于3600就實際值
"{\n" +
" \"Version\": \"2012-10-17\",\n" +
" \"Statement\": [\n" +
" {\n" +
" \"Effect\": \"Allow\",\n" +
" \"Action\": [\n" +
" \"s3:GetObject\",\n" +
" \"s3:GetBucketLocation\",\n" +
" \"s3:PutObject\"\n" +
" ],\n" +
" \"Resource\": [\n" +
" \"arn:aws:s3:::test/*\"\n" +
" ]\n" +
" }\n" +
" ]\n" +
"}",
properties.getRegion(),
"arn:aws:s3:::*/*",
"anysession",
null,
null);
Credentials credentials = provider.fetch();
return credentials;
}
public String downloadByLink(HttpServletRequest request, HttpServletResponse response, String fileId) {
AttachmentPO po = attachmentService.findById(fileId);
Credentials credentials = minioTemplate.getCredentials();
String token = credentials.sessionToken();
//為了統(tǒng)一前端訪問路徑,直接查客戶端請求Referer minio是前端ng代理的minio實際訪問
String referer = request.getHeader("Referer");
String url = referer + "minio/api/v1/buckets/" + po.getClientId() + "/objects/download?prefix=" + Base64.encode(po.getPath()) + "&token=" + token;
return url;
}
文章來源地址http://www.zghlxwxcb.cn/news/detail-468665.html
到了這里,關于springboot 本地/minio 附件下載優(yōu)化的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!