分散式檔案上傳導致服務假死了?

java金融發表於2022-03-04

引言

記得以前剛開始學習web專案的時候,經常涉及到需要上傳圖片啥的,那時候都是把圖片上傳到當前專案資料夾下面,每次專案一重啟圖片就丟了。雖然可以通過修改/tomcat/conf/server.xml配置檔案,配置一個上傳圖片的本地資料夾,即配置一個工程配置虛擬路徑,這樣可以避免專案重啟圖片丟失。自從參加工作以來基本就沒有遇到使用這種方式來儲存圖片了。一般要麼自己搭建檔案伺服器,要麼使用付費的檔案服務。比如七牛雲、阿里雲、騰訊雲等。今天我們就一起來聊聊如何使用阿里雲OSS檔案上傳。

oss 檔案上傳

使用OSS檔案上傳,阿里雲提供瞭如下幾種方式,大家可以選擇適合自己的方式。

Web端上傳

Web端常見的上傳方法是使用者在瀏覽器或App端上傳檔案到應用伺服器,應用伺服器再把檔案上傳到OSS。具體流程如下圖所示。
在這裡插入圖片描述
這種方式肯定不可取它有如下去缺點:

  • 上傳慢:使用者資料需先上傳到應用伺服器,之後再上傳到OSS,網路傳輸時間比直傳到OSS多一倍。如果使用者資料不通過應用伺服器中轉,而是直傳到OSS,速度將大大提升。而且OSS採用BGP頻寬,能保證各地各運營商之間的傳輸速度。
  • 擴充套件性差:如果後續使用者數量逐漸增加,則應用伺服器會成為瓶頸。本來就已經採用了OSS上傳了,然後還要在佔用自己伺服器。
  • 費用高:需要準備多臺應用伺服器。由於OSS上行流量是免費的,如果資料直傳到OSS,將節省多臺應用伺服器的費用。

    JavaScript客戶端簽名直傳

    這種方式採用純前端直接上傳,不經過應用伺服器,不過這種方式阿里雲給到的一些關於OSS上傳的一些核心引數(AccesssKey ID和AccessKey Secret相當於我們在阿里雲那邊申請的賬號和密碼)也需要寫在前端程式碼裡面,這樣就容易導致我們核心引數被洩漏。存在安全隱患。這種方式也不推薦。

    服務端簽名後直傳

    前面直接在前端簽名上傳會有安全隱患,存在引數洩漏。我們可以把引數放在服務端,然服務端和阿里雲去互動,這樣就不存在核心引數的洩漏。
    在這裡插入圖片描述

如何接入

引入依賴
  • 因為本人是從事java開發的,所以直接引入官方提供最新的maven依賴。

    <!-- https://mvnrepository.com/artifact/com.aliyun.oss/aliyun-sdk-oss -->
    <dependency>
      <groupId>com.aliyun.oss</groupId>
      <artifactId>aliyun-sdk-oss</artifactId>
      <version>3.14.0</version>
    </dependency>

    為什麼要引入最新的依賴。因為如果遇到什麼問題需要找阿里雲的人幫忙解決的時候,別人大多數都會問你什麼版本的sdk,然後如果遇到那種一時半會比較難解決的問題,人家會推薦你升級最新版本試試。因為可能在最新版本修復了你所遇到的bug。有人可能會說,引入最新版本不就是幫別人踩坑嗎?萬一解決一個bug又引入兩個bug列?這種情況也不是沒有可能的。

    服務端構建簽名

    在這裡插入圖片描述
    上圖是官網提供的入門例子,程式碼是一大坨,我們可以看看稍微優化後的程式碼:
    建立一個單例的ossClient,可以複用執行緒,不需要每次都去new ossClient().

          String host = String.format("https://%s.%s", ossPropertoooies.getBucketName(), ossPropertoooies.getEndpoint());
          long expiredTime = System.currentTimeMillis() + fileOssProperties.getUploadSignatureTtl();
          Date expiration = new Date(expiredTime);
    
          // 根據檔名和檔案型別設定儲存路徑,可以按照檔案型別+日期格式+UUID檔名 進行分割
          String filepath = getFilePath(request.getCategory(), request.getFilename());
    
          PolicyConditions policyConditions = new PolicyConditions();
          policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, fileOssProperties.getUploadSizeLimit());
          policyConditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, filepath);
    
          String postPolicy = ossClient.generatePostPolicy(expiration, policyConditions);
          byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
          String encodedPolicy = BinaryUtil.toBase64String(binaryData);
          String postSignature = ossClient.calculatePostSignature(postPolicy);
    
          SignatureDTO signature = new SignatureDTO();
          signature.setAccessId(ossPropertoooies.getAccessKeyId());
          signature.setPolicy(encodedPolicy);
          signature.setSignature(postSignature);
          signature.setFilepath(filepath);
          signature.setHost(host);
          signature.setExpire(fileOssProperties.getUploadSignatureTtl() / 1000);
          signature.setReqFilename(request.getFilename());

    接入起來還是非常簡單的,一個後端簽名,前端上傳前後分離的檔案上傳就已經完成了。這裡我們使用postman模擬下前端上傳,當然這裡可以改為前端使用ajax,或者其他方式都可以。上傳的url是由我們自己申請的bucketnameendpoint組成的
    在這裡插入圖片描述
    但是其實這裡面也是有許多坑的我們還是需要稍微注意下。

    頻寬限制

    上傳和下載都會有頻寬的限制,如果我們是採用外網直傳到阿里雲oss的話,需要注意下我們的外網頻寬是否夠用,以及應對大檔案的上傳是不是會把頻寬打滿。如果頻寬被打滿我們上傳就gg了。同樣的下載也有頻寬限制的,需要避免大檔案的下載,如果遇到這種大檔案下載我們可以採用其他的方式,比如使用oss的客戶端。所以我們需要合理的考慮我們伺服器的頻寬。如果我們的應用直接是部署在阿里雲上面的話,我們可以採用內網的上傳和下載。這樣的話就不會有頻寬的限制。

    API使用需要注意點

    當我們使用OSSclient提供的一些api使用的時候需要仔細去看看裡面是怎麼實現的,或者看看它的文件有沒有特殊交代的。
    比如使用OSSclient提供的processObject方法我們最後需要關閉輸入流,如果流不關閉,連結不被釋放。應用連結馬上就會被佔滿,然後服務就會成為一個假死的狀態,這個問題我們在生產環境就遇到一次。如下圖所示執行緒一直沒有被釋放。
    在這裡插入圖片描述
    像這種為什麼需要我們手動去關閉流,為什麼不直接api幫我們關閉,阿里雲的回覆是因為這裡返回的流可能業務方自己需要複製、或者讀什麼的。所以需要呼叫方主動關閉下,在這個很隱祕的文件中我們也有找到這個答案。
    在這裡插入圖片描述

    結束

  • 由於自己才疏學淺,難免會有紕漏,假如你發現了錯誤的地方,還望留言給我指出來,我會對其加以修正。
  • 如果你覺得文章還不錯,你的轉發、分享、讚賞、點贊、留言就是對我最大的鼓勵。
  • 感謝您的閱讀,十分歡迎並感謝您的關注。
    站在巨人的肩膀上摘蘋果:
    https://help.aliyun.com/docum...
    https://gosspublic.alicdn.com...

相關文章