`
agapple
  • 浏览: 1583011 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

使用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协议的内容。

 

Byte Ranges: 文档http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1

 

大致的内容,可以在Http的Header头中进行添加,可以指定文件下载byte的start和end的位置,几个例子:

bytes=100-499  
bytes=-300
bytes=100-
bytes=1-2,2-3,6-,-2

 

最后,通过tcpdump进行数据抓包分析,多线程下载客户端的Http协议的内容。基本的思路是根据线程数,计算出每个线程的bytes-range,然后每个线程发起一个独立的请求。后端每个处理线程单独处理bytes-range的数据下载。

至于断点续传,其实也相对比较简单了。就是记录好分出去的每个bytes-range成功与否,失败的重新再做,已经做完的可以直接跳过。

 

抓取的Http协议内容:

GET /source.tar.gz HTTP/1.1
User-Agent: aria2/1.13.0
Accept: */*
Host: 10.20.156.49:8080
Pragma: no-cache
Cache-Control: no-cache
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,具体代码见附件。

 

类中使用了java版的sendfile,推荐看一下:http://stackoverflow.com/questions/1605332/java-nio-filechannel-versus-fileoutputstream-performance-usefulness

 


将jetty引入做为嵌入式启动的步骤

1. 引入相关的jar

 

<dependency>
  <groupId>com.alibaba.external</groupId>
  <artifactId>server.jetty.jetty-servlet</artifactId>
  <version>${jetty_verion}</version>
</dependency>
<dependency>
  <groupId>com.alibaba.external</groupId>
  <artifactId>server.jetty.jetty-xml</artifactId>
  <version>${jetty_verion}</version>
</dependency>
<dependency>
  <groupId>com.alibaba.external</groupId>
  <artifactId>server.jetty.jetty-server</artifactId>
  <version>${jetty_verion}</version>
</dependency>

 

2.  配置jetty.xml (我选择了xml的配置方式,但没有使用war包,我只需要一个Http服务功能即可)

<Configure id="Server" class="org.eclipse.jetty.server.Server">

    <!-- =========================================================== -->
    <!-- Server Thread Pool                                          -->
    <!-- =========================================================== -->
    <Set name="ThreadPool">
      <!-- Default queued blocking threadpool -->
      <New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
        <Set name="minThreads">10</Set>
        <Set name="maxThreads">250</Set>
      </New>
    </Set>

    <!-- =========================================================== -->
    <!-- Set connectors                                              -->
    <!-- =========================================================== -->
    <!-- -->
    <Call name="addConnector">
      <Arg>
          <New class="org.eclipse.jetty.server.bio.SocketConnector">
            <Set name="port"><Property name="jetty.bio.port" default="8080"/></Set>
            <Set name="forwarded">true</Set>
            <Set name="forwardedHostHeader">ignore</Set>
            <Set name="forwardedServerHeader">ignore</Set>
            <Set name="acceptQueueSize">256</Set>
            <Set name="statsOn">false</Set>
            <Set name="maxIdleTime">600000</Set>
            <Set name="lowResourcesMaxIdleTime">5000</Set>
            <Set name="requestHeaderSize">8192</Set>
	    <Set name="responseHeaderSize">8192</Set>
          </New>
      </Arg>
    </Call>
    <!-- 
    <Call name="addConnector">
      <Arg>
          <New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
            <Set name="host"><Property name="jetty.host" /></Set>
            <Set name="port"><Property name="jetty.port" default="8080"/></Set>
            <Set name="forwarded">true</Set>
            <Set name="forwardedHostHeader">ignore</Set>
            <Set name="forwardedServerHeader">ignore</Set>
            <Set name="maxIdleTime">600000</Set>
            <Set name="Acceptors">2</Set>
            <Set name="acceptQueueSize">256</Set>
            <Set name="statsOn">false</Set>
            <Set name="confidentialPort">8443</Set>
            <Set name="lowResourcesConnections">2000</Set>
            <Set name="lowResourcesMaxIdleTime">5000</Set>
            <Set name="requestHeaderSize">8192</Set>
			<Set name="responseHeaderSize">8192</Set>
          </New>
      </Arg>
    </Call>
     -->
    <!-- =========================================================== -->
    <!-- Set handler Collection Structure                            -->
    <!-- =========================================================== -->
    <Set name="handler">
      <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
        <Set name="handlers">
         <Array type="org.eclipse.jetty.server.Handler">
           <Item>
             <New id="ServletHandler" class="org.eclipse.jetty.servlet.ServletContextHandler">
             	<Set name="contextPath">/</Set>
             	<Call name="addServlet">
             		<Arg>com.alibaba.otter.task.biz.common.jetty.DownloadServlet</Arg>
             		<Arg>/*</Arg>
             	</Call>
             	<Get name="initParams">
             		<Put name="org.eclipse.jetty.servlet.Default.resourceBase">/tmp/</Put>
             		<Put name="org.eclipse.jetty.servlet.Default.gzip">false</Put>
             	</Get>
             </New>
           </Item>
         </Array>
        </Set>
      </New>
	</Set>

    <!-- =========================================================== -->
    <!-- extra options                                               -->
    <!-- =========================================================== -->
    <Set name="stopAtShutdown">true</Set>
    <Set name="sendServerVersion">false</Set>
    <Set name="sendDateHeader">true</Set>
    <Set name="gracefulShutdown">1000</Set>
</Configure>

说明: 主要的配置见handler,配置了对应的DownloadServlet

 

3. 启动入口 (使用了xml配置后就灰常的简洁了)

 

Resource jetty_xml = Resource.newSystemResource("jetty/jetty.xml");
XmlConfiguration configuration = new XmlConfiguration(jetty_xml.getInputStream());
Server server = (Server) configuration.configure();
server.start();

 

测试

 

  最后选择了几个多线程下载的客户端进行了测试,我这里选择了aria2c(http://aria2.sourceforge.net/)  和 axel(http://www.axel.com/uk2/)

 

aria2c测试

参数:

--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测试
参数:
-n 10 -a -v  

下载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包,没有了外部软件的依赖,部署和维护也会相对比较简单。
  • 大小: 32 KB
3
1
分享到:
评论
6 楼 linde 2013-01-25  
agapple 写道
linde 写道
博主,NioUtils这个类能否提供下?
toogoot#gmail.com谢谢!


https://gist.github.com/4633039



感谢!
5 楼 agapple 2013-01-25  
linde 写道
博主,NioUtils这个类能否提供下?
toogoot#gmail.com谢谢!


https://gist.github.com/4633039
4 楼 linde 2013-01-25  
博主,NioUtils这个类能否提供下?
toogoot#gmail.com谢谢!
3 楼 agapple 2012-03-30  
elvishehai 写道
这个能够同时起两个server吗

端口不同,当然可以
2 楼 elvishehai 2012-03-30  
这个能够同时起两个server吗
1 楼 agapple 2011-10-11  
测试过程需要清空下linux缓存: echo 3 > /proc/sys/vm/drop_caches

操作系统层面会对文件流有cache,上一次下载后会有cache,会音像测试结果。

相关推荐

    jetty-server-8.1.8-API文档-中英对照版.zip

    赠送原API文档:jetty-server-8.1.8-javadoc.jar; 赠送源代码:jetty-server-8.1.8-sources.jar; 赠送Maven依赖信息文件:jetty-server-8.1.8.pom; 包含翻译后的API文档:jetty-server-8.1.8-javadoc-API文档-...

    jetty-server-8.1.8-API文档-中文版.zip

    赠送原API文档:jetty-server-8.1.8-javadoc.jar; 赠送源代码:jetty-server-8.1.8-sources.jar; 赠送Maven依赖信息文件:jetty-server-8.1.8.pom; 包含翻译后的API文档:jetty-server-8.1.8-javadoc-API文档-...

    jetty-server-8.1.8.v20121106-API文档-中英对照版.zip

    赠送原API文档:jetty-server-8.1.8.v20121106-javadoc.jar; 赠送源代码:jetty-server-8.1.8.v20121106-sources.jar; 赠送Maven依赖信息文件:jetty-server-8.1.8.v20121106.pom; 包含翻译后的API文档:jetty-...

    jetty-server-8.1.8.v20121106-API文档-中文版.zip

    赠送原API文档:jetty-server-8.1.8.v20121106-javadoc.jar; 赠送源代码:jetty-server-8.1.8.v20121106-sources.jar; 赠送Maven依赖信息文件:jetty-server-8.1.8.v20121106.pom; 包含翻译后的API文档:jetty-...

    jetty-server-9.4.8.v20171121-API文档-中文版.zip

    赠送原API文档:jetty-server-9.4.8.v20171121-javadoc.jar; 赠送源代码:jetty-server-9.4.8.v20171121-sources.jar; 赠送Maven依赖信息文件:jetty-server-9.4.8.v20171121.pom; 包含翻译后的API文档:jetty-...

    jetty-server-9.3.19.v20170502-API文档-中英对照版.zip

    赠送原API文档:jetty-server-9.3.19.v20170502-javadoc.jar; 赠送源代码:jetty-server-9.3.19.v20170502-sources.jar; 赠送Maven依赖信息文件:jetty-server-9.3.19.v20170502.pom; 包含翻译后的API文档:jetty...

    jetty嵌入式Httpserver

    NULL 博文链接:https://kanpiaoxue.iteye.com/blog/2110496

    shell脚本启动jetty嵌入式server

    shell脚本启动jetty嵌入式server,shell脚本启动jetty嵌入式server

    jetty-server-9.4.8.v20171121-API文档-中英对照版.zip

    赠送原API文档:jetty-server-9.4.8.v20171121-javadoc.jar; 赠送源代码:jetty-server-9.4.8.v20171121-sources.jar; 赠送Maven依赖信息文件:jetty-server-9.4.8.v20171121.pom; 包含翻译后的API文档:jetty-...

    jetty相关的全部jar包

    jar包版本9.4.8.v20171121,包含jetty-server-9.4.8.v20171121.jar,jetty-servlet-9.4.8.v20171121.jar,jetty-http-9.4.8.v20171121.jar,jetty-webapp-9.4.8.v20171121.jar,jetty-servlets-9.4.8.v20171121.jar...

    jetty-server-9.3.19.v20170502-API文档-中文版.zip

    赠送原API文档:jetty-server-9.3.19.v20170502-javadoc.jar; 赠送源代码:jetty-server-9.3.19.v20170502-sources.jar; 赠送Maven依赖信息文件:jetty-server-9.3.19.v20170502.pom; 包含翻译后的API文档:jetty...

    jetty各个版本下载

    jetty各个版本下载

    jetty源代码下载

    jetty源代码下载 jetty源代码下载 jetty源代码下载 jetty源代码下载

    jetty-server-8.1.0.RC5

    jetty-server-8 ,超级小的web服务器,可以方便快捷的用于开发!

    jetty-server-6.1.26

    jetty-server-6.1.26,JFinal软件所需要的包

    Jetty中文手册

    欢迎访问Jetty文档 Wiki. Jetty是一个开源项目,提供了http服务器、http客户端和java servlet容器。 这个wiki提供jetty的入门教程、基础配置、功能特性、优化、安全、JavaEE、监控、常见问题、故障排除帮助等等。它...

    通过Jetty实现文件上传下载的小工具

    NULL 博文链接:https://vista-rui.iteye.com/blog/1386427

    最新virgo-jetty-server

    用于Jetty的Virgo服务器、用于Jetty的Virgo服务器、用于Jetty的Virgo服务器

    jetty相关所有jar包

    jetty相关所有jar包,...jetty-continuation-8.1.15.v20140411,jetty-http-8.1.15.v20140411,jetty-io-8.1.15.v20140411,jetty-security-8.1.15.v20140411,jetty-server-8.1.15.v20140411,jetty-util-8.1.15.v20140411

    jetty-server-7.4.2.v20110526.jar

    jar jetty-server-7.4.2.v20110526.jar

Global site tag (gtag.js) - Google Analytics