跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

差量页面下载技术 - Diffable 的使用

杨 硕, 研究生, 东北大学
杨硕,东北大学信息学院研究生,热爱 Eclipse 以及 Web 技术,熟悉 Web2.0 相关技术。她的邮箱是 weiqiang.john@gmail.com。
魏 强, 研究生, 东北大学
魏强,东北大学软件学院硕士研究生,现在主要从事 Eclipse 插件的开发,同时热爱着 Web 技术,尤其对 Java Web 相关技术,更是情有独钟。他的邮箱是:neuswc20063500@gmail.com。
刘 强, 工程师, 中科院沈阳计算所
刘强,就职于中科院沈阳计算所,擅长并专注于数据库设计,对网络安全方面感兴趣。

简介: Google Maps 的开发人员提出了一项新的技术,名为 Diffable,该技术使得浏览器加载 Web 页面时,当浏览器发现本地缓存与服务器的时间不一致时,浏览器不会完全下载服务器端的文件,而是比较服务器与客户端相关文件(HTML, JavaScript)的不同,只下载(或删除)二者的差量。Google Maps 利用该技术,减少了页面加载速度的 25%。本文使用具体的开发实例,展示了使用该技术开发前后的效率对比。

发布日期: 2011 年 8 月 04 日
级别: 高级
访问情况 : 3554 次浏览
评论: 


引言

对于大型 Web 应用程序,比如 Google Map 这样的项目,它所用到的 JavaScript(后面简称 JS)文件会非常大,很多都在 500K 以上。庆幸的是我们拥有浏览器的缓存技术,在服务器端 JS 文件没有修改的情况下,客户端可以不用重复的向服务器请求下载。但是,随着业务需求的变化,这些大型的 JS 文件会经常做局部的修改或完善,可能只是很少的修改,但是也会强制客户端更新缓存,重新下载整个的 JS 文件,而对于像 Google Map 这样访问量巨大的网站,这是不得不考虑的性能问题。聪明的程序员想到了,如果修改很少,则我们只下载增加或减少的那一部分不就可以嘛?如果可以实现这样的话,就可以大大的提高性能了。Google 的开发人员实现了基于 Java EE 的实现版本,名为 Diffable。基于该架构可以很好的实现静态资源的增量(delta)下载,大大提高性能,Google Map 利用该技术提高了页面下载的 25% 的速度。本文介绍了 Java EE 版本的 Diffable 技术的使用,详细分析了使用该技术前后的不同。本文将逐步介绍 Diffable 的使用方法和实现原理,详细介绍了资源修改前后服务器与客户端的变化。附件部分是该文章使用的示例项目,读者可以下载下来导入开发环境运行查看。另外,本文所使用的静态资源文件都是 JavaScript。


Diffable 的原理

官方网站中给出了原理的架构图,如图 1 所示。


图 1. 示例项目结构图(查看大图
图 1. 示例项目结构图

文章针对 Java EE 这个特定实现讲解 架构图,首先客户端请求 JSP 文件,服务器返回 JSP 的执行结果(该结果无需缓存,并且结果中包含了目标资源的最新版本号 X,该目标资源由 Diffable 管理);浏览器解析 HTML,请求下载 Secondary Resource(官方叫法,在这里我们可以理解为控制资源,它的作用是请求增量、合并目标资源),该 Secondary Resource 包含了客户端缓存的目标资源版本号 Y(如果客户端没有缓存,Secondary Resource 请求 X 版本的整个资源)。Secondary Resource 负责比较 X 与 Y,如果二者相等,则直接使用本地缓存,如果二者不相等,则 Secondary Resource 负责请求 X 与 Y 的增量(delta)文件(Y_X.diff),并且将增量合并到本地缓存,更新本地缓存的资源号为 X。从上面的叙述可以看出,要想使用 Diffable,需要开启浏览器缓存、管理的必须是 JS 或 CSS 这样的静态文本资源。

如果您还不太理解,那么请记住几个关键字继续往后看,它们分别是:

目标资源(即客户端浏览器实际需要的资源文件,本文都是指 JavaScript 文件)、控制资源(Secondary Resource,它是一个 JavaScript 文件,目的是管理目标资源)、增量(delta, 对应后面的 .diff 文件)、版本(对应后面的 .version 文件)。


开发环境配置

JavaEE eclipse 3.4+ 下载链接

Tomcat5.0+ 下载链接

Diffable 环境:

以下的 JAR 包是保证 Diffable 能够运行的基础包。如果读者想进一步查看其余 JAR 包的下载和配置方法,请参照 官方网址

diffable-0.1.jar:下载链接

Google Guice-2.0.jar +: 下载链接

aopalliance1.0.jar +: 下载链接

commons-io-1.4.jar +: 下载链接

log4j-1.2.16.jar +: 下载链接


Diffable 的配置

第一步:在 JEE Eclipse 中新建 Web 项目,导入 开发环境配置罗列的所有 JAR 包。示例项目完整的目录结构如图 2 所示。


图 2. 示例项目结构图
图 2. 示例项目结构图

第二步:配置 web.xml。


清单 1. web.xml 中与 Diffable 有关的配置
				
 <context-param>      
    <description> 
 ResourceFolders 存放 Diffable 管理的资源 ( 只支持 JavaScript、CSS 等静态文件 ),
 该配置是必须的,Diffable 会监视该资源文件夹里的资源变化。如果有多个资源文件夹,
 使用逗号隔开,如 WEB-INF/javascript,WEB-INF/CSS。
   </description>  
  <param-name>ResourceFolders</param-name> 
  <param-value>WEB-INF/javascript</param-value> 
 </context-param> 
   
 <listener> 
 <display-name>DiffableListener</display-name> 
 <listener-class>com.google.diffable.listeners.DiffableListener</listener-class> 
 </listener> 
    
 <context-param>      
 <description>Diffable 属性配置文档,是一个 properties 文件,在此处可以配置 Diffable 
 的一些高级属性,例如 KeepResourcesInMemory=[true/false] 该属性决定是否将资源保存在内存中以提高效率。
 该配置不是必须的。
 </description> 
 <param-name>DiffableConfigProperties</param-name> 
 <param-value>WEB-INF/diffable.properties</param-value> 
 </context-param> 

 <context-param> 
 <description> 该配置是必须的,param-value 的值是绝对路径,格式是 
 /{web-app-name}/{servlet-url – pattern}. 
 如示例项目的名为 DiffableSample,后面配置的 DiffableServlet 的 url pattern 是 /diffable/*,
  所以 ServletPrefix 就是 /DiffableSample/diffable。
 </description> 
 <param-name>ServletPrefix</param-name> 
 <param-value>/DiffableSample/diffable</param-value> 
 </context-param> 
 <servlet> 
 <description>DffiableServlet 配置是必须的 , 这个 servlet 负责调度资源,
 解析资源请求 </description> 
 <servlet-name>DiffableServlet</servlet-name>  
 <servlet-class> 
 com.google.diffable.servlets.DiffableServlet 
 </servlet-class> 
 </servlet> 
 <servlet-mapping> 
 <description>url-pattern 的值必须与 ServletPrefix 对应,比如 url-pattern 改成 /myDiffable/*,
 那么 ServletPrefix 的值就要改成 /DiffableSample/myDiffable 
 </description> 
 <servlet-name>DiffableServlet</servlet-name> 
 <url-pattern>/diffable/*</url-pattern> 
 </servlet-mapping> 

上面的配置对应着 示例项目的结构,这里需要提及一点,之所以要将资源文件夹放在 WEB-INF 目录下,目的是为了防止用户通过路径直接访问被 Diffable 管理的资源,因为我们知道 WEB-INFO 下的内容是不允许这样访问的。

配置完 web.xml 后,第三步就是使用 Diffable 了。传统上,我们在 JSP 中引用 JavaScript 是使用 script 标签,如:<script src=”…”></script>,然而当我们将资源交给 Diffable 管理时,我们就不能这么直接访问了(再说了,资源在 WEB-INFO 下也不允许我们那么访问)。Diffable 给我们提供了资源标签 diff:resource 来引用资源,标签库文件 .tld 已经包含在了 diffable-0.1.jar 当中,因此无需配置。示例项目结构图中的 sample.jsp 的源码如清单 2 所示。


清单 2. 使用 Diffable 标签
				
 <%@ page language="java" contentType="text/html;"    
 pageEncoding="UTF-8" %> 
 <! —引入 Diffable 标签库 --> 
 <%@ taglib prefix="diff" uri="http://www.google.com/j2ee/diffable" %> 
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
 "http://www.w3.org/TR/html4/loose.dtd"> 
 <html> 
 <head>  
 <!-- 注意:此处的 servletPrefix 必须与 web.xml 文件中配置的 ServletPrefix 完全一致;
 通过 resource 标签引入资源文件夹下的文件,区别于 src="javascript/testsample.js" -->  
 <diff:resource servletPrefix="/DiffableSample/diffable" resource="testsample.js"/>  
 </head> 
 <body> 
 <button onclick="popUP()"></button> 
 </body> 
 </html> 

在 testsample.js 文件中声明了一个 popUP 方法,方法的代码块部分只有一句简单的 alert("Hello Diffable!") 作为例子。

第四步:启动服务器,使用浏览器访问 sample.jsp,点击 清单 2,使用 Diffable 标签中声明的按钮,如果网页此时弹出 Hello Diffable! 的提示框,就说明您已经会使用 Diffable 了。

以上介绍的,可以说读者很容易就会使用 Diffable 了,但是究竟 Diffable 在底层都做了些什么呢?继续往后看。


首次部署运行

为了直观的让读者可以感受 Diffable 的不同,以及可以更好的介绍 Diffable 的原理和内部执行过程,本文将使用 Firefox 浏览器插件 Firebug 的网络监控功能,读者可以从监控中看出 Diffable 的资源请求过程。

将示例 Web 项目部署运行,查看 TOMCAT 的 webapps 目录里示例程序所在的目录,除了 WEB-INFO 和 META-INF 目录外会多出一个 .diffable 文件夹,diffable 文件夹里则包含一个 diffable.manifest 文件和资源 hash 目录(存放目标资源,用 hash 值命名的目录),这些是 Diffable 启动时创建的。结果如图 3 中的上下部分所示。


图 3. diffable 文件目录
图 3. diffable 文件目录

这个 Manifest 包含 Diffable 所需要的一些资源控制变量。而我们程序员关注的是那个资源 hash 目录,它以一个 hash 值命名,这个 hash 值与 清单 1,web.xml 中与 Diffable 有关的配置中配置的 ResourceFolders 里的资源对应 , 目前管理的资源只有一个 testsample.js,也就是说 273 … 85b 这个 hash 值对应 javascript/testsample.js。而我们打开这个资源 hash 目录,存放了一个 .version 文件,如图 4 所示。


图 4. 资源 hash 目录的内容
图 4. 资源 hash 目录的内容

这个 .version 的文件名也是一个 hash 值,对应的是 javascript/testsample.js 文件的当前版本号,使用文本文件形式打开 .version 文件,可以发现该 version 文件的内容和 testsample.js 的内容完全一致,实际上 Diffable 是将目标资源文件的当前版本内容进行了拷贝,并拷贝到了 .version 文件里。

使用安装有 Firebug 的 Firefox 访问 sample.jsp 文件,打开网络监控功能。当访问结束时,我们查看网络监控的结果,如图 5 所示。


图 5. 首次访问的网络监控结果
图 5. 首次访问的网络监控结果

可以看到首次访问 sample.jsp,由于浏览器没有相关缓存,所以 2.8KB 的数据都来自于服务器端。客户端发出了两个请求,分别是 sample.jsp 的请求(GET DiffableSample 的原因是 web.xml 里的 welcome file 是 sample.jsp,因此默认就是访问 sample.jsp),以及资源文件 testsample.js 的请求。testsample.js 的请求具体地址如图 6 所示。


图 6. 第二个请求的地址
图 6. 第二个请求的地址

可以看到该地址的组成包含了 http://{host}:{port}/{Diffable 配置的 ServletPrefix}/{.diffable 目录下的资源 hash 目录名,与 图 3,diffable 目录中的资源 hash 目录名对应,实际就是访问 testsample.js 文件 },这个请求就是 <diff:resource servletPrefix="/DiffableSample/diffable" resource="testsample.js"/> 作用的效果。

查看 GET DiffableSample 这个请求的返回内容,如图 7 所示。


图 7.GET DiffableSample 的返回内容
图 7.GET DiffableSample 的返回内容

上面正文内容的第八行,显示了 window[ ‘ deltajs ’ ][ ‘ { 资源 hash 目录名 } ’ ][ ‘ cv ’ ]= ’ { 对应 图 4,资源 hash 目录下的版本号 } ’,cv 代表的是 Current Version,显示的服务器端的最新资源版本号。

查看 testsample.js 请求的响应,就是 图 5中第二个 GET 的请求响应,它的响应内容就是在 Diffable 的原理中介绍的控制资源(Secondary Resource),它的内容大致如清单 3 所示。


清单 3. 控制资源(Secondary Resource)
				
 function DJSBootstrap(identifier, code) { 
 this.code_ = code; 
 this.identifier_ = identifier; 
 } 
……
 var djs = new DJSBootstrap('2738950c7ada0e700bf33c48fccf585b', 
 "function popUP()\r\n{\r\n\talert(\"Hello Diffable!\");\r\n}"); 
 djs.bootstrap('bf72f6fd3c0e70bd83ac2a0647d77173', '/DiffableSample/diffable/');

我们关注最后三行,DJSBootstrap 对象构造函数的第一个参数是资源 hash 目录名,第二个参数则是本地缓存的资源内容,由于首次没有用到缓存,所以该部分内容就是从服务器端下载的资源最新内容。第三行调用了 djs 的 bootstrap 方法,该方法的第一个参数是缓存中的资源版本号,第二个是 ServletPrefix 的值。使用 Firebug 的调试功能单步调试 bootstrap 方法可以发现,控制资源(Secondary Resource)的 bootstrap 方法中对比了 图 7中响应的 cv 值(服务器最新的版本)和 bootstrap 构造方法的首个参数(缓存的资源版本),由于二者一致,所以没有请求增量。

我们在 Firefox 的地址栏按下回车,重新访问(不是按 F5,因为 F5 会清空缓存),再看网络监控。结果如图 8 所示。


图 8. 第二次加载(查看大图
图 8. 第二次加载

上图中,URL 是灰色的代表来至于缓存(2.2KB 来自缓存)。由于服务器端的资源文件没有做任何修改,所以包含 testsample.js 的控制资源(Secondary Resource)也没有任何改变。


修改服务器端 JS 文件

我们修改服务器端的 testsample.js 文件,将 alert 语句改为 alert("Hello Diffable! New Change!")。保存后,我们再看 图 3中的资源 hash 目录下的版本文件,如图 9 所示。


图 9. 第一次修改后的资源版本
图 9. 第一次修改后的资源版本

我们看到,除了旧版本的 bf7 … 173.version,还多了新的版本文件 196 … 8ee.version,这是 Diffable 自动生成的。打开 bf7 … 173.version 发现它的内容与旧版本 testsample.js 的相同, 196 … 8ee.version 的内容则是 alert("Hello Diffable! New Change!"),是最新版本的内容。还有一个文件,是以 { 旧版本号 }_{ 新版本号 }.diff 命名的增量(delta)文件。打开该 delta 文件,查看结果如图 10 所示。


图 10. delta 的文件内容
图 10. delta 的文件内容

可以看到,该文件存放了新旧版本的增量部分。

使用 Firefox 再次使用地址栏回车的方式访问 sample.jsp 文件,查看网络监控结果,如图 11 所示。


图 11. 第一次修改资源后访问(查看大图
图 11. 第一次修改资源后访问

我们看到这次访问发送了三个请求,前两个请求与 图 8 中的请求的一样,第三个的 GET 请求地址如图 12 所示。


图 12. 第三个请求地址(查看大图
图 12. 第三个请求地址

可以看到,第三次 GET 请求的地址是新旧版本的增量文件。它的 URL 格式是 { 资源 hash 目录名 }_{ 旧版本号 }_{ 新版本号 }.diff。访问的文件其实就是 图 9 中的增量文件。查看这个 GET 请求的返回内容,如图 13 所示。


图 13. 第三个 GET 返回内容
图 13. 第三个 GET 返回内容

可以看到,返回的响应内容就是 图 10 增量文件中的内容。

我们再看前两个 GET 请求,第一个请求的返回内容如清单 4 所示。


清单 4. 修改后 GET DiffableSample 的内容
				
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
 "http://www.w3.org/TR/html4/loose.dtd"> 
 <html> 
 <head>    
 <script type='text/javascript'> 
 if(!window['deltajs']) { window['deltajs'] = {}; } 
 window['deltajs']['2738950c7ada0e700bf33c48fccf585b']={}; 
 window['deltajs']['2738950c7ada0e700bf33c48fccf585b']['cv'] = 
 '196db4d5d5e5326534440c0d79fc48ee'; 
 </script> 
 <script type='text/javascript' 
 src='/DiffableSample/diffable/2738950c7ada0e700bf33c48fccf585b'></script> 
  
 </head> 
 <body> 
 <button onclick="popUP()" value="显示"></button> 
 </body> 
 </html> 

清单 4 获得最新版本号 CV 是 196 … 8ee,再查看第二个 GET 请求的返回内容,与 清单 3中的内容完全相同。也就是说控制资源用到了缓存,这也是 图 11中显示 2.2KB 来自缓存的原因。再次使用 Firebug 调试 bootstrap 方法,可以发现 DJSBootstrap 对比了缓存的资源版本号(bf7 … 173)与服务器最新版本号 CV(196 … 8ee),发现服务器的最新版本号与本地的缓存版本号不相同,则会发出第三个请求去下载二者的增量文件 .diff,并且由控制资源进行合并更新。这也是 图 11,修改后的请求次数比 图 5,修改前的请求次数多一次的原因。最后修改后的访问结果如图 14 所示。


图 14. 修改后结果
图 14. 修改后结果

从修改前后的请求可以看出,控制资源的访问会使用缓存,更新的仅仅就是新旧版本的增量文件。这对于大型文件的更新,效率的提高是显而易见的。


第二次修改服务器端 JS 文件

我们再一次修改服务器端的 testsample.js 文件,将 alert 语句改为 alert("Hello Diffable! New Change! Again!")。保存后,我们再看资源 hash 目录下的版本、增量列表。结果如图 15 所示。


图 15. 第二次修改后
图 15. 第二次修改后

可以看到除了上面的提到的两个旧版本外,又多了一个 b28 … 355 的版本,打开这个版本文件,内容是 alert("Hello Diffable! New Change! Again!"),可以知道 b28 … 355 是服务器端 testsample.js 的最新版本。再看增量文件,原先 图 9,第一次修改后的 bf7 … 173_196 … 8ee.diff 增量文件已经被删除,转变为各个旧版本到最新版本的增量。保留这些旧版本,同时创建各个旧版本到最新版本的增量,显而易见是考虑到各个客户端可能存在的不同缓存版本,使得他们都可以实现增量下载。


结果分析

上面共分为三个部分讲解了 Diffable 的内部执行过程和原理,分别是 首次部署运行修改服务器端 JS 文件第二次修改服务器端 JS 文件。首次部署时,Diffable 为每个管理的资源创建资源 hash 目录,并且在目录里创建了 .version 的版本文件,文件内容是资源的拷贝。第一次访问 sample.jsp 文件,发送了两个请求,一个是包含最新版本号 cv 的 HTML,一个是包含了最新资源(bootstrap 的第二个参数就是最新资源内容)的控制资源(Secondary Resource)。刷新会发现,控制资源已经被缓存;修改 testsample.js 资源文件的内容,出现了新的 version 文件以及新旧版本的增量 diff 文件。请求 sample.jsp,会发现客户端发出了三个请求,第一个是包含最新版本号 cv 的 HTML,这个请求得到了与缓存不同的 cv,所以不能用到缓存。第二个请求用到了缓存,仍然是上一次请求的控制资源,里面包含了上一版本缓存的 testsample.js 的内容和版本号。通过比较缓存和最新版本的版本号判断是否下载二者增量,由于服务器的文件进行了修改,所以会发出第三个请求,下载缓存与最新版本间的增量,并且由控制资源负责整合增量与缓存,更新缓存;之后我们又修改了资源文件,资源 hash 目录下共出现了前后三个版本的 version 文件,以及两个 diff 文件。事实上为了保证客户端存在的不同缓存版本都可以进行增量下载,diff 文件始终是各个旧版本到最新版本的增量。


该技术的利与弊

前面提及了 Diffable 的优点,但是使用一项技术时得知道它的适用环境,而不是随便滥用。对于一个大资源文件做少量修改的需求,Diffable 的使用可以大大的提高性能。但是如果文件不大,或者修改的频率非常低,该技术的使用反而会减少效率,因为每次请求都会进行对比、执行资源,相当于在目标资源和浏览器之间增加了一层(就是控制资源),同时服务器端的 DiffableServlet 还需要进行一些验证、选择处理等,这些都是消耗资源的操作。因此,当资源大、修改频繁时,可以使用 Diffable,而对于那些资源不大、修改频率很少的资源,就得用普通的引用资源方法。


小结

本文通过简单的例子开发,讲解了 Diffable 的使用方法和基本原理,对 JS 文件进行多次修改后,服务器端文件以及客户端请求的变化,展示了 Diffable 技术的优越性,同时分析了该技术的利与弊,供读者分析并合理的选择该技术。由于本人知识水平有限,文章中倘若有错误的地方,欢迎联系我批评指正。



下载

描述名字大小下载方法
示例代码DiffableSample.zip122KBHTTP

关于下载方法的信息


参考资料

学习

讨论

  • 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

作者简介

杨硕,东北大学信息学院研究生,热爱 Eclipse 以及 Web 技术,熟悉 Web2.0 相关技术。她的邮箱是 weiqiang.john@gmail.com。

魏强,东北大学软件学院硕士研究生,现在主要从事 Eclipse 插件的开发,同时热爱着 Web 技术,尤其对 Java Web 相关技术,更是情有独钟。他的邮箱是:neuswc20063500@gmail.com。

刘强,就职于中科院沈阳计算所,擅长并专注于数据库设计,对网络安全方面感兴趣。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development
ArticleID=750774
ArticleTitle=差量页面下载技术 - Diffable 的使用
publish-date=08042011
author1-email=weiqiang.john@gmail.com
author1-email-cc=
author2-email=neuswc20063500@gmail.com
author2-email-cc=
author3-email=liuqiangneu@gmail.com
author3-email-cc=

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。