1. 程式人生 > >使用jetty做為server提供多執行緒檔案下載

使用jetty做為server提供多執行緒檔案下載

背景

  最近在做的一個專案,兩個java程序之間會涉及一個大資料量的傳遞過程,基本都是圖片檔案,(做了壓縮後還是會比較大,最大的有超過600MB)。其次這兩個java程序是在跨機房,比如中國和美國機房,網路待框也就幾百kB。

  這就是本文的專案背景

分析

1.  600MB的檔案,都是A程序執行時根據需要生成的(下載需要的圖片檔案)。所以無法預先處理,而且公司總圖片檔案都是以TB計算,所以全量同步的方案也不靠譜

2.  從A程序到B程序的資料傳遞,首先想到用socket進行傳遞,但單socket的資料同步無法滿足需求,多執行緒資料傳遞會涉及資料的切片和資料的合併等,程式碼相對會比較複雜

老的專案實現:

  • A先臨時儲存檔案到一指定目錄
  • A程序機器上啟動一個http服務(比如nginx,lighttpd)
  • B程序外部呼叫一個多執行緒下載客戶端,下載資料到一臨時目錄
  • B程序等下載完成後,再操縱臨時目錄的資料
專案的工程程式碼都是以java,引入了多執行緒服務和下載客戶端之後,增加了專案的部署和維護成本。所以在專案重構時,想的一個辦法是用嵌入式的jetty,去替換nginx提供http服務。

過程

涉及到多執行緒下載和斷點續載,首先得了解一個Http協議的內容。

大致的內容,可以在Http的Header頭中進行新增,可以指定檔案下載byte的start和end的位置,幾個例子:

Java程式碼  收藏程式碼
  1. bytes=100-499    
  2. bytes=-300  
  3. bytes=100-  
  4. bytes=1-2,2-3,6-,-2  

最後,通過tcpdump進行資料抓包分析,多執行緒下載客戶端的Http協議的內容。基本的思路是根據執行緒數,計算出每個執行緒的bytes-range,然後每個執行緒發起一個獨立的請求。後端每個處理執行緒單獨處理bytes-range的資料下載。

至於斷點續傳,其實也相對比較簡單了。就是記錄好分出去的每個bytes-range成功與否,失敗的重新再做,已經做完的可以直接跳過。

抓取的Http協議內容:

Java程式碼  收藏程式碼
  1. GET /source.tar.gz HTTP/1.1
      
  2. User-Agent: aria2/1.13.0  
  3. Accept: */*  
  4. Host: 10.20.156.49:8080  
  5. Pragma: no-cache  
  6. Cache-Control: no-cache  
  7. Range: bytes=258998272-387973119  

java版的多執行緒下載支援

一提到做java版的多執行緒下載,首先就會想到jetty和tomcat。tomcat只能以外部server的方式進行啟動,和nginx沒有太多的區別。

最後我選擇了jetty,並在專案中做為嵌入式進行啟動,提供http多執行緒下載服務。

按照前面的分析,要做多執行緒下載,無非就是要實現一個bytes-range的處理。還好我用的jetty版本(7.0.1)已經解析了bytes-range,具體解析類:InclusiveByteRange

再仔細翻了下它的程式碼,發現jetty已經預設提供了一個servlet支援多執行緒下載,就是DefaultServlet,甚喜。

最後,按照我專案的需求適當的裁剪了一些程式碼,最後完成了:DownloadServlet,具體程式碼見附件。


將jetty引入做為嵌入式啟動的步驟

1. 引入相關的jar

Xml程式碼  收藏程式碼
  1. <dependency>  
  2.   <groupId>com.alibaba.external</groupId>  
  3.   <artifactId>server.jetty.jetty-servlet</artifactId>  
  4.   <version>${jetty_verion}</version>  
  5. </dependency>  
  6. <dependency>  
  7.   <groupId>com.alibaba.external</groupId>  
  8.   <artifactId>server.jetty.jetty-xml</artifactId>  
  9.   <version>${jetty_verion}</version>  
  10. </dependency>  
  11. <dependency>  
  12.   <groupId>com.alibaba.external</groupId>  
  13.   <artifactId>server.jetty.jetty-server</artifactId>  
  14.   <version>${jetty_verion}</version>  
  15. </dependency>  

2.  配置jetty.xml (我選擇了xml的配置方式,但沒有使用war包,我只需要一個Http服務功能即可)

Java程式碼  收藏程式碼
  1. <Configure id="Server" class="org.eclipse.jetty.server.Server">  
  2.     <!-- =========================================================== -->  
  3.     <!-- Server Thread Pool                                          -->  
  4.     <!-- =========================================================== -->  
  5.     <Set name="ThreadPool">  
  6.       <!-- Default queued blocking threadpool -->  
  7.       <New class="org.eclipse.jetty.util.thread.QueuedThreadPool">  
  8.         <Set name="minThreads">10</Set>  
  9.         <Set name="maxThreads">250</Set>  
  10.       </New>  
  11.     </Set>  
  12.     <!-- =========================================================== -->  
  13.     <!-- Set connectors                                              -->  
  14.     <!-- =========================================================== -->  
  15.     <!-- -->  
  16.     <Call name="addConnector">  
  17.       <Arg>  
  18.           <New class="org.eclipse.jetty.server.bio.SocketConnector">  
  19.             <Set name="port"><Property name="jetty.bio.port" default="8080"/></Set>  
  20.             <Set name="forwarded">true</Set>  
  21.             <Set name="forwardedHostHeader">ignore</Set>  
  22.             <Set name="forwardedServerHeader">ignore</Set>  
  23.             <Set name="acceptQueueSize">256</Set>  
  24.             <Set name="statsOn">false</Set>  
  25.             <Set name="maxIdleTime">600000</Set>  
  26.             <Set name="lowResourcesMaxIdleTime">5000</Set>  
  27.             <Set name="requestHeaderSize">8192</Set>  
  28.         <Set name="responseHeaderSize">8192</Set>  
  29.           </New>  
  30.       </Arg>  
  31.     </Call>  
  32.     <!--   
  33.     <Call name="addConnector">  
  34.       <Arg>  
  35.           <New class="org.eclipse.jetty.server.nio.SelectChannelConnector">  
  36.             <Set name="host"><Property name="jetty.host" /></Set>  
  37.             <Set name="port"><Property name="jetty.port" default="8080"/></Set>  
  38.             <Set name="forwarded">true</Set>  
  39.             <Set name="forwardedHostHeader">ignore</Set>  
  40.             <Set name="forwardedServerHeader">ignore</Set>  
  41.             <Set name="maxIdleTime">600000</Set>  
  42.             <Set name="Acceptors">2</Set>  
  43.             <Set name="acceptQueueSize">256</Set>  
  44.             <Set name="statsOn">false</Set>  
  45.             <Set name="confidentialPort">8443</Set>  
  46.             <Set name="lowResourcesConnections">2000</Set>  
  47.             <Set name="lowResourcesMaxIdleTime">5000</Set>  
  48.             <Set name="requestHeaderSize">8192</Set>  
  49.             <Set name="responseHeaderSize">8192</Set>  
  50.           </New>  
  51.       </Arg>  
  52.     </Call>  
  53.      -->  
  54.     <!-- =========================================================== -->  
  55.     <!-- Set handler Collection Structure                            -->  
  56.     <!-- =========================================================== -->  
  57.     <Set name="handler">  
  58.       <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">  
  59.         <Set name="handlers">  
  60.          <Array type="org.eclipse.jetty.server.Handler">  
  61.            <Item>  
  62.              <New id="ServletHandler" class="org.eclipse.jetty.servlet.ServletContextHandler">  
  63.                 <Set name="contextPath">/</Set>  
  64.                 <Call name="addServlet">  
  65.                     <Arg>com.alibaba.otter.task.biz.common.jetty.DownloadServlet</Arg>  
  66.                     <Arg>/*</Arg>  
  67.                 </Call>  
  68.                 <Get name="initParams">  
  69.                     <Put name="org.eclipse.jetty.servlet.Default.resourceBase">/tmp/</Put>  
  70.                     <Put name="org.eclipse.jetty.servlet.Default.gzip">false</Put>  
  71.                 </Get>  
  72.              </New>  
  73.            </Item>  
  74.          </Array>  
  75.         </Set>  
  76.       </New>  
  77.     </Set>  
  78.     <!-- =========================================================== -->  
  79.     <!-- extra options                                               -->  
  80.     <!-- =========================================================== -->  
  81.     <Set name="stopAtShutdown">true</Set>  
  82.     <Set name="sendServerVersion">false</Set>  
  83.     <Set name="sendDateHeader">true</Set>  
  84.     <Set name="gracefulShutdown">1000</Set>  
  85. </Configure>  

說明: 主要的配置見handler,配置了對應的DownloadServlet

3. 啟動入口 (使用了xml配置後就灰常的簡潔了)

Java程式碼  收藏程式碼
  1. Resource jetty_xml = Resource.newSystemResource("jetty/jetty.xml");  
  2. XmlConfiguration configuration = new XmlConfiguration(jetty_xml.getInputStream());  
  3. Server server = (Server) configuration.configure();  
  4. server.start();  

測試

aria2c測試

引數:

Java程式碼  收藏程式碼
  1. --no-conf -x 10 -s 10 -j 10 --timeout=600 --max-tries=5 --stop=1800 --allow-overwrite=true --enable-http-keep-alive=true --log-level=warn  

下載1.1GB的檔案:

   apache : 28s

   nginx  : 27s

   jetty   : 27s

axel測試 引數: Java程式碼  收藏程式碼
  1. -n 10 -a -v <span style="white-space: normal;"> </span>  

下載1.1GB的檔案: 

   apache : 87s
   nginx  : 87s

   jetty : 88s

總結

並沒有做非常詳盡的效能測試,不過從幾次跑的結果來看,基本上也有數了。

  1. jetty實現的servlet效能基本和nginx,apache下載接近。而且測試過程中瓶頸已經不在應用本身,基本都在網路頻寬上了,我是百MB網絡卡,基本可以滿負荷運轉。
  2. jetty的nio和bio版本,nio在context switch切換上會相對比較多(因為有大量的READ/WRITE事件響應,執行緒切換反而不如bio來得少),建議部署bio模式
  3. 多執行緒下載aria2c工具的確不錯,推薦使用
後續,會嘗試使用java寫一個多執行緒的客戶端,如果效能還ok的話,可以直接替換aria2c,到時候就是一些jar包,沒有了外部軟體的依賴,部署和維護也會相對比較簡單。

相關推薦

使用jettyserver提供執行檔案下載

背景   最近在做的一個專案,兩個java程序之間會涉及一個大資料量的傳遞過程,基本都是圖片檔案,(做了壓縮後還是會比較大,最大的有超過600MB)。其次這兩個java程序是在跨機房,比如中國和美國機房,網路待框也就幾百kB。   這就是本文的專案背景 分析 1.  600MB的檔案,都是A程序執行

基於HTTP的執行檔案下載功能實現

思想 檔案資訊獲取的獲取方式與單執行緒的方式一樣 與單執行緒相比不同的是將遠端檔案分塊併發獲取,然後再併發寫入到本地暫存檔案中 遠端檔案分塊的實現依據是:connection.setRequestProperty(“Range”,”bytes=”+start

Android實現網路執行檔案下載

實現原理 (1)首先獲得下載檔案的長度,然後設定本地檔案的長度。 (2)根據檔案長度和執行緒數計算每條執行緒下載的資料長度和下載位置。 如:檔案的長度為6M,執行緒數為3,那麼,每條執行緒下載的資料長度為2M,每條執行緒開始下載的位置如下圖所示: (網上找的圖)  例如1

java 執行檔案下載,斷點續傳

1,把阿里旺旺傳到伺服器上 2,分3個執行緒,分別下載不同位置的檔案 3,用3個檔案記錄每次下載的位置,停止後再次下載時,直接從已下載的位置開始繼續下載,當檔案下載完成後刪除記錄的檔案 測試成功,下面是實現程式碼: package com.zhuyu.utils; i

一起面試題--Java執行交替列印

這道面試題的內容是,要求兩個執行緒交替列印,打印出"12A34B56C78D910E1112F1314G1516H1718I1920J2122K2324L2526M2728N2930O3132P3334Q3536R3738S3940T4142U4344V4546W4748X

socket C/C++程式設計(8)server執行處理clients佇列

1. 採用C++11的標準執行緒庫(linux之pthread為例)實現多執行緒(test.cpp) #include <stdio.h> #include <string.h> #ifdef WIN32 #include

Qt:基於TCP的執行檔案傳輸工具

FTP (File transfer protocol)是一個古老實用的檔案傳輸協議,方便在客戶端和伺服器之間進行檔案的傳輸,我們可以在Linux作業系統上使用vsftpd這個軟體來搭建 FTP 伺服器並建立專有的 FTP 登入賬戶保障伺服器安全,但是它對於非專業使用者來說,使用命令列來

android執行斷點下載

多執行緒斷電xia下載,通過設定執行緒的數量,動態獲取下載檔案執行緒的個數,這是本人用於練習所寫demo,註釋很詳細,用於初學者參考使用。 MainActivity.java頁面   package com.dahui.download; import java.io.Buf

Java 執行分段下載原理分析和實現

多執行緒下載介紹   多執行緒下載技術是很常見的一種下載方案,這種方式充分利用了多執行緒的優勢,在同一時間段內通過多個執行緒發起下載請求,將需要下載的資料分割成多個部分,每一個執行緒只負責下載其中一個部分,然後將下載後的資料組裝成完整的資料檔案,這樣便大大加快了下載效率。常見的下載器,迅

ios開發網路-大檔案執行斷點下載

說明:本文介紹多執行緒斷點續傳。專案中使用了蘋果自帶的類,實現了同時開啟多條執行緒下載一個較大的檔案。 因為實現過程較為複雜, 實現思路:下載 開始,建立一個與要下載檔案大小相同的檔案(如果要下載100M,那麼就在沙盒建立一個100M的檔案,然後計算每一段的下載量,開啟多條執行緒下載各段的資料,分

android執行暫停下載-HttpURLConnection

android多執行緒暫停下載-HttpURLConnection private EditText et_path; private LinearLayout ll_pbs; @Override protected void onCreate(Bundle sa

OpenMP: 執行檔案操作

OpenMP:多執行緒檔案操作 簡介 具體實現 1. OpenMP常用函式 2. 並行區域 3. for 迴圈並行化基本用法 3.1 資料不相關性 3.2 for迴圈並行化的幾

從CSV檔案中讀取jpg圖片的URL地址並執行批量下載

很多時候,我們的網站上傳圖片時並沒有根據內容進行資料夾分類,甚至會直接儲存到阿里雲的OSS或是七牛雲等雲端儲存上。這樣,當我們需要打包圖片時,就需要從資料庫找尋分類圖片,通過CURL進行下載。我最近剛剛完成了一個這樣的任務,覺得會比較常用,就把程式放到了github上分享給大家,希望大家能夠喜歡。 do

執行斷點下載工具

多執行緒斷點下載工具需要完成以下幾個功能:         * 1.通過提供的網路檔案地址,下載該檔案         * 2.要求使用多個執行緒同時下載,並且實時更新下載進度(0%~100%)  &n

[文件和原始碼分享] 基於QT和websocket協議的執行檔案傳輸

做兩個程式,實現檔案收發 傳送端放兩個按鈕,點選後開啟電腦目錄選擇所要傳輸的檔案,選好以後,把檔名和路徑顯示在介面上,點選第二個按鈕,把檔案傳到遠端機器(或者虛擬機器)上由接收端接收 編寫一接收端,把檔案接收下來,存進指定的某個目錄裡 要能測試通過三個傳送端同時發100M的檔案,接收端能分別

java 使用RandomAssessFile類執行切片下載檔案之伺服器如何實現

因為之前寫的都是客戶端,不需要去管服務端,直接把檔案放伺服器裡面,直接訪問,伺服器(tomcat之類得)就會自動幫我們切片,之類的。然後我自己想測試一些直接訪問檔案和使用控制器io讀寫返回檔案哪個快一些(肯定是io)https://blog.csdn.net/yali_a

Java -- 執行檔案複製

題目描述: 實現檔案複製功能 多執行緒實現檔案從一個目錄複製到另一個目錄 @param sourceFile : 給定原始檔路徑名 @param desPath : 複製點檔案路徑 程式碼: package fileio20180924; import

OkHttp實現執行併發下載的筆記

         打個廣告,不瞭解OkHttp的話可以先看下  http://blog.csdn.net/brycegao321/article/details/51830525             需求:  手機拍攝若干張照片, 在wifi連線下上傳到伺服器。  

IOS網路、執行、shareSDK-使用EDG執行技術下載圖片

使用EDG中央排程多執行緒技術實現圖片的非同步下載 // //  ViewController.swift //  Dome2test // //  Created by 郭文亮 on 2018/11

java 執行批量下載美女圖片

今天無聊想寫個java程式,就寫了個下載圖片的程式,從 www.meizitu.com批量下載圖片 package test; import java.io.File; import java.io.FileOutputStream; import java.io.IOE