首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

基于 Struts2 标签的 BigPipe 技术实现(5)

基于 Struts2 标签的 BigPipe 技术实现(5)

多线程实现从单线程 BigPipe 的实现方式可以看出,单线程并不能解决总时间加载慢的问题,它更适合对文档内容显示按照优先级排序的需求。 而要是总体时间过慢,就要考虑多线程的实现方式。Java 中提供了 Concurrent 框架可以很轻松的实现多线程技术。先将页面内容分为多个的 PageLet,这里我们将每个单元格的内容定义为一个 PageLet。多线程的实现原理图如图 11 所示。
图 11. 多线程原理图服务器接受到网页请求时,就开始按照文档流顺序处理 PageLet。每处理到一个 PageLet,服务器端程序会将其交给线程池里的线程处理,线程池处理完请求后,就将内容包含在 JavaScript 代码里,写回客户端。客户端执行这段代码将内容插入到正确的位置(这和单线程是一样的),由于借助 JavaScript 执行内容的插入,因此只要网页结构先加载,那么线程池处理的内容在任何时候返回,都可以执行正确的插入,无需关心 JavaScript 代码的位置。
从技术实现上,由于将内容交给其他线程处理,那么处理页面的主线程在所有 PageLet 处理完之前不能结束,因为只要主线程处理结束,那么网页输出流就会被关闭,线程池的处理结果也就无法被写回。这里可以采用 Concurrent 框架里的 CountDownLatch,这个类就像比赛结束的哨声,处理 PageLet 的线程就像赛跑员,当所有的赛跑员都跑到终点线(countDown),那么裁判就可以吹响结束的哨声(await),也就是当所有的 PageLet 都生成完毕时,主线程就可以结束。
另一方面,由于现有的 JSP 标签扩展机制,使得我们无法将标签里的内容直接丢到线程池里执行。因此标签里的内容,需要使用新的模板文件,程序将模板生成的内容插入到主页面对应的位置,生成内容的过程在单独的线程中执行,模板就使用 Struts2 支持的 FreeMarker 模板。
使用多线程实现的代码如清单 6 所示。
清单 6. 多线程使用代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<%@ taglib prefix="b" uri="/WEB-INF/bigpipe.tld"%>
<%long pstart = System.currentTimeMillis();%>
<b:multiThread pageLetNum="6" bigPipeJSPath="js/bigpipeMulti.js">
<table border="1" width="100%" height="500">
<caption> 多线程例子 </caption>
<tr>
<td><b:pageLet dealClass="com.bigpipe.tag.Index1" name="index1">
    编号:1<img src="images/loading.gif"/>
    </b:pageLet></td>
<td><b:pageLet dealClass="com.bigpipe.tag.Index2" name="index2">
    编号:2<img src="images/loading.gif"/>
    </b:pageLet></td>
<td><b:pageLet dealClass="com.bigpipe.tag.Index3" name="index3">
    编号:3<img src="images/loading.gif"/>
    </b:pageLet></td>
</tr>
<tr>
<td><b:pageLet dealClass="com.bigpipe.tag.Index4" name="index4">
    编号:4<img src="images/loading.gif"/>
    </b:pageLet></td>
<td><b:pageLet dealClass="com.bigpipe.tag.Index5" name="index5">
    编号:5<img src="images/loading.gif"/>
    </b:pageLet></td>
<td><b:pageLet dealClass="com.bigpipe.tag.Index6" name="index6">
    编号:6<img src="images/loading.gif"/>
    </b:pageLet></td>
</tr>
</table>
</b:multiThread>
<%long secs = System.currentTimeMillis() - pstart;%>
整个页面加载耗费了:<%=secs%> 毫秒




使用 multiThread 标签包围所有的 pageLet 标签 , 在 multiThread 里的所有 PageLet 是并行加载的。multiThread 标签有一个 pageLetNum 属性,它代表 multiThread 包围的 pageLet 标签数,它必须与实际包围的 pageLet 标签数一致。每个 pageLet 标签都有一个 dealClass,它是类的全路径,该类实现 IPageLetDealer 接口,该接口只有一个方法:public PageAndModel<String, Object> deal(ValueStack vs, HttpServletRequest request, HttpServletResponse response) throws Exception,这个接口返回的是一个 PageAndModel<String, Object> 对象,String 代表 FreeMarker 模板的地址(WEB-INFO 下 template 文件夹的相对地址),Object 代表这个模板的模型对象,它的实例是根据业务逻辑由程序员实现。查看 multiThread 标签对应类 MultiThread 类的代码,它的关键代码如清单 7 所示。
清单 7. MultiThread 关键代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Override
public boolean start(Writer writer) {
    boolean start = super.start(writer);
    try {
    writer.write("<script type='text/javascript' src='" + bigPipeJSPath
        + "'></script>");
    } catch (IOException e) {
    e.printStackTrace();
    }
        return start;
}

@Override
public boolean end(Writer writer, String body) {
    boolean end = super.end(writer, body);
    CountDownLatch c =
       (CountDownLatch)request.getAttribute(MultiThreadTag.COUNT_DOWN);
    try {
    //等待所有的PageLet结束
    c.await();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    return end;
}

返回列表