- 浏览: 3470133 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
wanglf1207:
EJB的确是个不错的产品,只是因为用起来有点门槛,招来太多人吐 ...
weblogic-ejb-jar.xml的元素解析 -
qwfys200:
总结的不错。
Spring Web Flow 2.0 入门 -
u011577913:
u011577913 写道也能给我发一份翻译文档? 邮件437 ...
Hazelcast 参考文档-4 -
u011577913:
也能给我发一份翻译文档?
Hazelcast 参考文档-4 -
songzj001:
DbUnit入门实战
摘要
Spring Web Flow 是Spring Framework 中的web应用组件,它提供了一种编写有状态和基于会话的web应用的简便手段。Spring Web Flow 使得逻辑流程成为Web应用中的一等公民,它能让你定义为自包含模块,以独立于应用的其它部分来配置和重用。
Spring Web Flow 引入了几种有状态数据域:request、flash、flow和conversation 等,这让你能用新的方式来开发有状态Web应用。它也提供了定制应用状态管理的扩展点。
Terracotta for Spring 是通过在多个JVM集群来给基于Spring的应用提供高可用性的运行时。它给Spring Web Flows 的所有域都提供了透明的声明式集群服务(普通的Spring beans同样适用 )。
在这篇文章中我们会首先给你一个Spring Web Flow 和Terracotta for Spring 的总体介绍。然后会向你展示如何联合使用这些技术来进入构建有状态、基于会话、可扩展和高可用的Web应用的新领域。
什么是Spring Web Flow?
Spring Web Flow 是Spring Framework 中的web应用组件,它提供了一种编写有状态和基于会话的web应用的简便手段。Spring Web Flow 使得逻辑流程成为web应用中的一等公民,它能让你定义为自包含模块,以独立于应用的其它部分来配置和重用。它不依赖于框架从而能够方便的与可选的web应用框架一同使用,比如Spring MVC、Struts 或者JSF 等。
页面流转使用一种领域定义语言(DSL)来配置,这个语言专门开发用来定义和组合页面流转。目前的实现方式是XML和Java.
Spring Web Flow引入了能满足多种用户案例和需求的几种有状态数据域:request、flash、flow和conversation ,这给你开发有状态web应用提供了很大的灵活性和能力。
这里是1.0 release中最有趣特性的快速概要。(来自release notes on InfoQ ):
- 在一个地方而不是把逻辑分散在很多地方来定义应用任务的所有控制逻辑,比如一个搜索流程。
- 把简单的流转组合在一起来创建富控制模块。
- 使用自然和面向对象的线性编程模型,而不是冗长的的if/else块来定义严格的用户导航规则。
- 但流转结束或过期时自动清除你在流转执行中分配的内存。
- 在使用你选择的基础web框架的Servlet环境中Deploy一个可执行的流转。
- 改变web框架(比如Struts、Spring MVC、JSF及其它)而不用修改流转定义。
- 和环境一起改变而不需要修改你的流转定义, 比如从JUnit测试到Portlet。
- 开发时在不重启容器的情况下不断完善你的应用导航规则。
- 自动正确响应浏览器按钮(后退、前进、刷新)而不需要定制编程。
- 在4个受管理域中存储任务数据:request、flash、flow、和、conversation等,每个都有自己的独特语义。
- 脱离容器单独测试流转。能在部署前确保应用控制逻辑能正常运作。
- 使用Spring IDE 2.0进行可视化编辑你的流转导航逻辑图
听上去很有趣?到目前为止还仅仅是概念和理论,但我们很快会看到这些都能在实践中应用。所以请多等一会。
企业对扩展性和高可用性的需求
集群在企业应用开发中变得越来越重要,开发人员经常会碰到这样的问题:
- 我们如何在一个节点上扩展来提高应用的可扩展性?
- 如果保证高可用性和消除单点故障,如何确保我们能满足客户的SLAs(Service Level Agreement)?
为了支撑业务,可预测的扩展性和高可用性是一个生产应用必须展示的运行特性。一些企业需要超过99.9999%的正常运行时间,另一些不要求这么 高,但所有的应用都需要保证SLA规定的可运行性。从预测的角度看,开发一个系统在99.9%和99.9999%级别是一样困难的。
集群一直以来都是难以解决的问题。在Spring Web Flow和普通web应用上下文中,这尤其意味着保证用户状态的高可用性和故障恢复能力是满足性能且值得信赖的。在一个节点出现故障(应用服务器或JVM 崩溃),使用session粘滞(这是配置负载均衡最通用的首选方式)是一个开始,但我们需要一个有效手段来无缝的将用户状态从一个节点迁移到另一个节 点。
当我们说集群时意味着什么,它和缓存有什么区别?我们使用的集群定义是: 在多个JMV间共享应用状态,而缓存可以被定义为: 让应用状态靠近执行上下文。从这个角度看,缓存是集群的一个子集。
我们所认为的企业级Web/企业集群最小解决方案集至少需要包括:
- 可扩展性
- 高可用性
- fail-over
- 性能
- 对已有代码的最小影响
- 简单的部署和配置
- 可见运行时(监控)
让我们关注一个能解决所有这些看上去无关的需求的解决方案。通常解决一个问题有很多方案,而且市场上也有很多宣称能给web应用提供高可用性的产品。Terracotta就提供了这样一个解决方案。
什么是Terracotta for Spring?
Terracotta for Spring 是基于Spring 应用的运行时,它为Spring 应用提供了透明的高性能集群支持,对应用代码和部署及配置流程影响都很小。它通过在应用下面的堆级别进行集群而不是直接集群应用。
这让开发者能够开发与无状态方式不同的单节点有状态 Spring 应用。这使得在需要扩展的应用开始设计时不考虑集群。而在应用需要扩展或者要保证搞可用性和故障恢复时,他们只需要在Terracotta 配置文件中定义哪些Spring 应用上下文 中的beans需要进行集群。Terracotta for Spring 使得应用能够被自动和透明的集群,还保证在集群间的语义和单节点一样。
对于Spring Web Flow 来说,这实际上更简单。用户为了获得web应用的状态和持续仓库的集群能力, 只需要在Terracotta 配置文件中把特定的web应用声明为启用“session-support”。(详细内容见下面的章节“声明式配置”)
从宏观上看,Terracotta for Spring提供了:
- HTTP session状态的集群 。保证Spring Web Flow 中的用户状态和扩展仓库或放入HTTP session的其它状态的高可用性和故障恢复能力。
- Spring bean的集群 。Spring bean的生命周期语义和域在集群间被保存,它们在“逻辑”上相同的ApplicationContext中。目前能被集群的bean类型是singleton 和session scoped . 用户可以声明式配置哪个application contexts 中的哪个bean需要被集群。
- 透明集群POJO 。 不需要修改已有的代码,甚至不需要源代码。应用基于很少的声明式XML 配置文件,在载入期透明的生效。Terracotta for Spring不需要实现Serializable, Externalizable或其它接口的类。能这样实现的原因它并没有使用序列化,而只是将实际的差量和已经改变了的数据传输给当前需要的节点 (lazily)。
- 虚拟内存管理 。它也提供分布式垃圾收集和虚拟堆功能。比如,由于物理内存在需要时被换入换出,它能在一个4G RAM的机器上运行需要200G堆的Web应用。 这也意味着你不需要关心Spring Web Flow 会话数据的大小是否超过了物理堆大小。
堆层次集群
Terracotta for Spring 使用aspect-oriented技术来在类载入时适配应用。在这个阶段它扩展了应用以保证Java语义在集群间被正确的维护,包括对象引用,进程调用和垃圾收集等
我们在前面提到Terracotta 并没有使用serialization。这意味着任何Spring Web Flow 维护的会话都可以在集群间共享。这也意味着Terracotta并没有把会话状态的全部对象图发给所有节点,而是把图分解成纯粹的数据,并在网络间传输实际的“差量”和改变--数据的“原始信息”在其它节点上。
因为有一个记录节点间相互引用的中心调度器(见下文),它能以lazy的方式工作,而只把改变传送到引用了这些“dirty”数据的节点。这需要使 用局部引用。如果负载均衡配置为使用"session粘滞" 就更有效率,因为这意味着很多数据可能永远不会脱离实际的session而不用复制到其它节点。
这个架构是中心辐射的,也就是有一个管理客户端的中心调度器。在这里客户端就是你配有Terracotta for Spring 运行时的普通应用。调度器不是单点失败的,但你可以配置一组备用调度器,并在主调度器崩溃时选择一个来接替。你也可以独立于客户端,对调度器进行集群扩展。
构建一个高可用的有状态web应用
这里我们使用一个叫Sellitem 的示例应用来推动讨论并展示给大家:
- 如何使用Spring Web Flow来构建一个有状态基于会话的web应用。
- 如何使用Terracotta for Spring来声明式集群有状态应用。
Sellitem 示例应用可以在Spring Web Flow的发布版本中找到。(更多信息见文章末尾的“Resources” 章节)
使用Spring Web Flow实现一个有状态Web应用: Sellitem
Sellitem是展示结合了有条件转移、会话域、流程执行转向和延续性的示例应用。用户在几个页面间导航,可以定义货物的价格、可以销售的货物数量、折扣比率、送货详情(如果需要)和最后查看所有信息。
我们不会通读应用的所有源码,而是主要介绍一些关键概念,如怎么配置Spring Web Flow 输出的不同标准服务(Bean)和怎么定义页面流转(使用针对页面流转的Spring Web Flow DSL的XML版本)。
在Spring MVC中配置DispatcherServlet
应用的入口是一个标准的Spring MVC DispatcherServlet,它在web.xml中注册并在web application context中映射到*.htm。
<servlet> <servlet-name>sellitem</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/sellitem-servlet-config.xml /WEB-INF/sellitem-webflow-config.xml </param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>sellitem</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping>
DispatcherServlet配置在Spring的配置文件sellitem-servlet-config.xml和sellitem- webflow-config.xml中。 sellitem-servlet-config.xml中有一个映射到"/pos.htm"的控制器,它将所有该URL的请求转发到Spring Web Flow系统(它的入口是一个流程执行器):
<bean name="/pos.htm" class="org.springframework.webflow.executor.mvc.FlowController"> <property name="flowExecutor" ref="flowExecutor" /> </bean>
配置flow executor和flow registry beans
Spring flowExecutor bean配置使用一个flowRegistry bean来执行"/WEB-INF/flows/"目录中的基于XML的流转定义。
<flow:executor id="flowExecutor" registry-ref="flowRegistry"/> <flow:registry id="flowRegistry"> <flow:location path="/WEB-INF/flows/**-flow.xml" /> </flow:registry>
定义页面流转
剩余的逻辑在我们已经注册的flowRegistry bean的流转定义中。(参照前面的'配置flow executor和flow registry beans章节)。
在深入流转实现细节前,我们先看一下页面流转的状态图(如下图)。
从上面我们可以看到流转在结束前经过了几个步骤,在决定销售是否需要送货时有一个决策状态。
一个很好的针对上面导航规则的初始流转定义实现如下:
<flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd"> <var name="sale" class="org.springframework.webflow.samples.sellitem.Sale"/> <start-state idref="enterPriceAndItemCount"/> <view-state id="enterPriceAndItemCount" view="priceAndItemCountForm"> <transition on="submit" to="enterCategory"/> </view-state> <view-state id="enterCategory" view="categoryForm"> <transition on="submit" to="requiresShipping"/> </view-state> <decision-state id="requiresShipping"> <if test="${flowScope.sale.shipping}" then="enterShippingDetails" else="finish"/> </decision-state> <view-state id="enterShippingDetails" view="shippingDetailsForm"> <transition on="submit" to="finish"/> </view-state> <end-state id="finish" view="costOverview"/> </flow>
我们从上面的定义可以看到,实际状态与状态图中的状态对应,状态转换与图中的箭头对应。"sale" bean是流转开始时分配的流转变量实例。它持有了Sale相关的属性。
上面的定义展示了所有的导航逻辑,但还没有实现任何应用行为。特别是在用户提交时更新Sale Bean的逻辑还没有实现。另外后台的sale处理逻辑还没有定义。
实现了所有必需行为的完整Spring Web Flow 定义如下:
<flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd"> <var name="sale" class="org.springframework.webflow.samples.sellitem.Sale"/> <start-state idref="enterPriceAndItemCount"/> <view-state id="enterPriceAndItemCount" view="priceAndItemCountForm"> <render-actions> <action bean="formAction" method="setupForm"/> </render-actions> <transition on="submit" to="enterCategory"> <action bean="formAction" method="bindAndValidate"> <attribute name="validatorMethod" value="validatePriceAndItemCount"/> </action> </transition> </view-state> <view-state id="enterCategory" view="categoryForm"> <transition on="submit" to="requiresShipping"> <action bean="formAction" method="bind"/> </transition> </view-state> <decision-state id="requiresShipping"> <if test="${flowScope.sale.shipping}" then="enterShippingDetails" else="processSale"/> </decision-state> <view-state id="enterShippingDetails" view="shippingDetailsForm"> <transition on="submit" to="processSale"> <action bean="formAction" method="bind"/> </transition> </view-state> <action-state id="processSale"> <bean-action bean="saleProcessor" method="process"> <method-arguments> <argument expression="flowScope.sale"/> </method-arguments> </bean-action> <transition on="success" to="finish"/> </action-state> <end-state id="finish" view="costOverview"> <entry-actions> <action bean="formAction" method="setupForm"/> </entry-actions> </end-state> <import resource="sellitem-beans.xml"/> </flow>
在定义导航逻辑之外,也定义了适时调用恰当应用行为的action。这包括处理用户提交事件和调用后台处理器来处理sale的逻辑。
Form绑定和验证
当进入展示表单的视图状态时,流转调用一个FormAction command bean来进行表单的装配和提交逻辑。在提交时,FormAction把用户的请求参数绑定到相应的sale属性上并同时验证它们。
<bean id="formAction" class="org.springframework.webflow.action.FormAction"> <property name="formObjectName" value="sale"/> <property name="validator"> <bean class="org.springframework.webflow.samples.sellitem.SaleValidator"/> </property> </bean>
更多信息
Spring Web Flow全部的代码、文档和10个示例应用(包括sellitem)都可以在Spring网站上找到http://www.springframework.org/webflow 。
集群Sellitem应用
现在我们已经看过了如何使用Spring Web Flow 来实现一个有状态web应用。接下来让我们更高兴的看如何给我们的示例应用启用高可用性和故障恢复,如何使用Terracotta for Spring进行集群来提供透明容错性和在节点间共享状态。
听上去很难? 你会发现这实际上很简单。
声明式配置
Sellitem 示例应用使用一个Sale类的实例来保存当前销售的会话数据;同样Spring Web Flow executor repository 也使用HTTP session 来保存所有的会话数据。
要使用Terracotta for Spring,我们需要确保为给定的web应用启用了HTTP session集群,包括所有可能被保存在 HTTP session中(或者能被保存在HTTP session中的实例引用)的类,以便于检测。这里是Terracotta for Spring的tc-config.xml配置文件的一个例子:
<application> <spring> <jee-application name="swf-sellitem" > <session-support>true</session-support> <instrumented-classes> <include> <class-expression> org.springframework.webflow.samples.sellitem.Sale </class-expression> </include> </instrumented-classes> </jee-application> </spring> </application>
这里我们为swf-sellitem WAR文件启用了HTTP session集群并配置了Sale类。
就是这样,我们已经做了很多了。
启用Terracotta
我们唯一需要做的就是在应用中启用Terracotta for Spring运行时。这可以通过修改Tomcat web服务器的启动脚本并在脚本最前面加入下面的环境变量完成:
set JAVA_OPTS=-Xbootclasspath/p:"%DSO_BOOT_JAR%" set JAVA_OPTS=%JAVA_OPTS% -Dtc.install-root="%TC_INSTALL_DIR%" set JAVA_OPTS=%JAVA_OPTS% -Dtc.config="%LOCAL_DIR%\tc-config.xml"
这里面:
- DSO_BOOT_JAR环境变量指向jar的根目录(能在Terracotta for Spring安装的根目录的common/lib/dso-boot下找到)。
- TC_INSTALL_DIR环境变量指向Terracotta for Spring安装的根目录。
- LOCAL_DIR指向包含tc-config.xml的目录.。
Sellitem应用预配置了Terracotta for Spring 集群的代码可以在下面的'Resources'章节找到。也包括了开箱即用的Tomcat集群配置。
注解: Spring 应用上下文中的集群bean可以在服务(bean)级别配置,并依赖于Terracotta for Spring 的 auto-include检测机制.例如,大多数情况下我们不需要关心引入哪个类,而只需要在tc-config.xml文件中定义希望集群的bean的名称。
总结
Spring Web Flow 给包括文章中看到的这种简单应用到有很多页面流转的大型企业应用,都提供了构建基于会话的有状态应用的有力手段。 Terracotta for Spring 给你的Spring Web Flow提供了高可用性。
简而言之, Terracotta for Spring 提供了:
- 给基于Spring Web Flow的应用包括普通Spring的应用提供容错性 。
- 不需要实现java.lang.Serializable ,在多个节点间分布的应用中透明共享状态 。
- 在多个分布式节点进行资源调用。
- 在多个分布式节点间保持了Pass-by-reference语义。
- 声明式配置基本上不用修改现有代码 (除了以前是无状态Spring应用,现在需要变成有状态)
Spring Web Flow和Terracotta for Spring结合在一起,给你提供了构建有状态、基于会话、可扩展和高可用性web应用的新方式。
资源
Spring Web Flow
主页、下载和文档:http://www.springframework.org/webflow
。
示例应用:在文章中使用的Sellitem
应用包含在Spring Web Flow
发布版本中。
Sellitem应用在线演示:http://spring.ervacon.com/swf-sellitem/
。
Terracotta for Spring
Terracotta for Spring是提供JVM级集群开源项目Open Terracotta的一部分: http://terracotta.org/
。
下载:http://terracotta.org/confluence/display/orgsite/Download
示例应用:Sellitem应用包含了Terracotta for Spring简化版本的发布包,它已经预配置了且能直接运行(甚至打包了Tomcat和负载均衡)。
文档:http://terracotta.org/confluence/display/docs1/Spring+Quick+Start
。
查看英文原文:
Web Applications with Spring Web Flow and Terracotta for Spring
作者简介: Jonas Bonér 在Terracotta Inc.工作,他关注于战略、产品开发与架构和传播技术。他是AspectWerkz AOP框架的创始人,也是Eclipse AspectJ 5 项目和很多其它开源项目的贡献者。他经常做AOP、JVM-level集群和其它新兴技术的发言。
Eugene Kuleshov 是一个独立顾问。他有12年的软件设计和开发经验,专注于应用安全、企业集成(EAI)和面向消息中间件。他积极参与了很多开源社区的项目。
译者简介: 张俊,网名Pesome,上海交通大学软件工程硕士,多年JavaEE开发经验,积极参与国内开源组织。个人用Spring、Hibernate等开源软件搭建www.openfans.net 网站,以Web 2.0的形式介绍开源软件,希望为中国的开源软件事业做点贡献。与InfoQ中文站分享内容,请邮件至china-editorial@infoq.com 。
发表评论
-
字符串分割--java中String.split()用法
2013-03-06 14:25 74091在java.lang包中有String.sp ... -
用 HttpServletResponseWrapper 实现 Etag 过滤器
2012-07-09 16:58 3699原文出处:http://blog.chenlb.com/200 ... -
Fitnesse使用
2012-05-05 13:27 23419Fitnesse 的使用 一,介绍 Fitnesse是一种 ... -
Customizing the new FitNesse parser
2012-05-05 13:13 2087FitNesse began its life using ... -
java application中内嵌ActiveX控件
2011-11-14 15:57 5466我这里用的是SWT/JFace开发application,SW ... -
Google Java Developer Tools Downloads
2011-08-09 00:04 2303WindowBuilder Pro原来叫WindowB ... -
Jalita
2011-08-06 00:49 1516Jalita (Java light terminal ada ... -
【转】用Java写字符终端界面
2011-07-29 13:13 2075终端界面GUI开源项目charva。 这个框架让你可以用开发 ... -
[转]mybatis下的分页,支持所有的数据库
2011-07-21 13:21 14783大 家都知道,mybatis的自带分页方法只是逻 ... -
Java framework for text- & console-based forms?
2011-07-21 01:06 1667charva jcurses JNA , ... -
JNA(Java Native Access)学习入门
2011-07-21 01:04 22511Java Native Access 项目 在 ... -
JAVA上加密算法的实现用例
2011-06-25 12:38 4834来源:www.ibm.com ... -
如何将GlassFish作为Windows服务运行
2011-05-18 23:21 2313本文档来自GlassFish官方网站,详细介绍了将 G ... -
JAVA UDP打洞必备知识点---NAT
2011-05-05 12:56 8601一、引言 RFCl631 ... -
Keystore概念,Keytool工具使用
2011-04-28 16:20 2860近来由于项目需要做Single Sign On, 研究了一 ... -
利用Eclipse Profile Plugin监控分析Tomcat性能
2011-04-18 16:14 3666目前新版本的Eclipse在启动应用服务器的时候有一个新的选 ... -
m2eclipse: Eclipse is running in a JRE, but a JDK is required
2011-02-04 23:43 2506Eclipse 安装了Maven插件,启动Eclipse ... -
利用JNative实现Java调用动态库
2010-10-18 00:43 2056由于项目要求,需要用J ... -
RHEL5支持大内存
2010-10-08 16:19 2949安装 RHEL 5 ,硬件为 4G 内存,安装完成 ... -
Windows Server 2003 和 Windows 2000 提供大内存支持
2010-10-08 16:19 1814本文介绍物理地址扩展 ...
相关推荐
Terracotta实现基于Tomcat的Web应用集群
本文介绍如何配置Tomcat和Terracotta服务器将普通的Web应用部署到集群中,实现跨Tomcat节点的session复制,以达到负载均衡、提高系统吞吐量和灾难恢复的效果。
* Spring web: Spring MVC, Spring Web Flow 2, Spring Roo, other dynamic scripting, integration with popular Grails Framework (and Groovy), REST/web services, and more. This book guides you step by ...
NULL 博文链接:https://dutianzhao.iteye.com/blog/2154040
7.1 用Spring Web Flow管理简单的UI流程 238 7.1.1 问题 238 7.1.2 解决方案 239 7.1.3 工作原理 239 7.2 用不同状态类型建立Web流程模型 246 7.2.1 问题 246 7.2.2 解决方案 246 7.2.3 工作原理 ...
7.1 用Spring Web Flow管理简单的UI流程 238 7.1.1 问题 238 7.1.2 解决方案 239 7.1.3 工作原理 239 7.2 用不同状态类型建立Web流程模型 246 7.2.1 问题 246 7.2.2 解决方案 246 7.2.3 工作原理 ...
Terracotta
Terracotta的基本原理是对于集群间共享的数据,当在一个节点发生变化的时候,Terracotta只把变化的部分发送给...在对比测试中,采用Terracotta搭建Tomcat集群,节点达到8个时候,整个集群的吞吐量还一直是线性增长的。
java ehcache terracotta 集群, 分布式缓存配置
terracotta集群负载,是个好东西
Terracotta 3.2.1 英文文档
terracotta-ee-3.5.2破解版
terracotta license.key ,
其实terracotta集群tomcat这方面的资料也算不少了··但还是会很容易出现错误·· 希望我这个文档能帮助大家!
使用terracotta实现session共享的证实实验
ehcache的terracotta方案的比较.docx
记载了terracotta如何与tomcat、jetty等服务器的集群,解释了tc-config.xml中各个配置的作用
本学习文档主要介绍了Terracotta基本原理和如何配置Tomcat和Terracotta服务器将普通的Web应用部署到集群中,实现跨Tomcat节点的session复制,以达到负载均衡、提高系统吞吐量和灾难恢复的效果。
terracotta-eclipse-plugin-3.7.7-2013-08-19_16-03-48.tar(terracotta的Eclipse插件) 发现官网挺卡的,有时候下不了,先传上来吧,供国内用户下载,这个算最新的吧。2014-02-17下载的。