BigPipe是一个重新设计的基础动态网页服务体系。大体思路是,分解网页成叫做Pagelets的小块,然后通过Web服务器和浏览器建立管道并管理他们在不同阶段的运行。这是类似于大多数现代微处理器的流水线执行过程:多重指令管线通过不同的处理器执行单元,以达到性能的最佳。
使用JAVA实现BigPipe服务端的例子在网上很难寻觅,笔者经过多次尝试,在Servlet3.0和Servlet2.5规范下成功实现了BigPipe的分段输出效果。好东西不敢独享,在这里与大家分享。
要搭建BigPipe服务端程序,首先我们必须了解BigPipe服务端的工作原理。BigPipe的最终目标是通过一次请求向浏览器输出页面框架和若干个Pagelet。浏览器在接收完页面框架后立即进行展现,然后并行的接收后面的Pagelets,从而加快页面的渲染速度。Pagelet是通过调用前端的一个JS方法向页面输出的。
为了达到一次请求多次输出的效果,服务端程序必须采用分段输出的技术(chunked)。这里所说的chunked是指HTTP 1.1规范中定义的一个特殊的HTTP头信息,下面的一段HTTP响应头包含了chunked定义:
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
通过在HTTP响应头中增加“Transfer-Encoding: chunked”标识,我们通知浏览器后面的响应内容是分段输出的,每段输出使用一个16进制的数字来声明段长度,然后紧跟一个回行换行符,后面是分段的内容体,最后使用一个0长度的分段来结束整个的chunked输出。下面是一个用Socket模拟的chunked分段输出:
public static void main(String[] args) throws IOException, InterruptedException {
StringBuffer content1 = new StringBuffer("<html><body>");
content1.append("<div id='a1' style='width:100px;height:100px;border:1px solid black'></div>");
content1.append("<div id='a2' style='width:100px;height:100px;border:1px solid black'></div>");
content1.append("<div id='a3' style='width:100px;height:100px;border:1px solid black'></div>");
content1.append("<div id='a4' style='width:100px;height:100px;border:1px solid black'></div>");
content1.append("</body></html>");
String content2 = "<script type='text/javascript'>document.getElementById('a1').innerHTML='<h1>1</h1>'</script>";
String content3 = "<script type='text/javascript'>document.getElementById('a2').innerHTML='<h1>2</h1>'</script>";
String content4 = "<script type='text/javascript'>document.getElementById('a3').innerHTML='<h1>3</h1>'</script>";
String content5 = "<script type='text/javascript'>document.getElementById('a4').innerHTML='<h1>4</h1>'</script>";
ServerSocket ss = new ServerSocket(80);
Socket s;
while (true) {
s = ss.accept();
s.getInputStream().read();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(s.getOutputStream(), "UTF-8"));
//输出响应头
pw.println("HTTP/1.1 200 OK");
pw.println("Content-Type: text/html;charset=UTF-8");
//声明分段输出
pw.println("Transfer-Encoding: chunked");
pw.println();
//第一段
//先输出段长度,注意必须是16进制,加2是因为后面有个回车换行符
pw.println(Integer.toHexString(content1.length() + 2));
//输出分段内容
pw.println(content1.toString());
//输出回车换行符结束本段输出
pw.println();
//立即清空输出缓冲区,通知浏览器立即处理本段内容
pw.flush();
//暂停1秒模拟后台处理过程
Thread.sleep(1000);
//第二段
pw.println(Integer.toHexString(content2.length() + 2));
pw.println(content2);
pw.println();
pw.flush();
Thread.sleep(1000);
//第三段
pw.println(Integer.toHexString(content3.length() + 2));
pw.println(content3);
pw.println();
pw.flush();
Thread.sleep(1000);
//第四段
pw.println(Integer.toHexString(content4.length() + 2));
pw.println(content4);
pw.println();
pw.flush();
Thread.sleep(1000);
//第五段
pw.println(Integer.toHexString(content5.length() + 2));
pw.println(content5);
pw.println();
pw.flush();
//输出一个0长段结束整个输出
pw.println(0);
pw.println();
pw.close();
//最后别忘了关闭输出流
s.close();
}
运行上面的代码,在浏览器中输入"http://localhost"应该就可以看到分段输出的效果了,注意和传统的AJAX异步请求不同,这里只有一次请求!
通过上面使用Socket模拟的分段输出响应,我想大家已经了解了Bigpipe服务端工作的原理,下面我们通过Servlet来实现分段输出的效果。查找了好久,网上只能找到使用Servlet3.0实现分段输出的样例,得益于Servlet3.0规范新增的异步处理接口,我们可以使用很幽雅的方式来完成分段输出的效果,但我相信使用旧的Servlet规范一定也能实现分段输出的效果,事实上的确如此,我们先来看使用Servlet3.0实现的代码:
@WebServlet(value = { "/syncServlet" }, asyncSupported = true, loadOnStartup = 1)
public class SyncTestServlet extends HttpServlet {
private static final long serialVersionUID = -126107068129496624L;
private final BlockingQueue<Runnable> quere = new ArrayBlockingQueue<Runnable>(10);
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.HOURS, quere);
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
final AsyncContext sc = request.startAsync(request, response);
final StringBuffer content = new StringBuffer("<html><body>");
content.append("<div id='a1' style='width:100px;height:100px;border:1px solid black'></div>");
content.append("<div id='a2' style='width:100px;height:100px;border:1px solid black'></div>");
content.append("<div id='a3' style='width:100px;height:100px;border:1px solid black'></div>");
content.append("<div id='a4' style='width:100px;height:100px;border:1px solid black'></div>");
content.append("</body></html>");
final PrintWriter pw = response.getWriter();
pw.println(content.toString());
pw.flush();
executor.execute(new Job(sc, 1));
executor.execute(new Job(sc, 2));
executor.execute(new Job(sc, 3));
}
}
class Job implements Runnable {
private static int count;
private AsyncContext syncContext;
private int no;
public Job(AsyncContext syncContext, int no) {
this.syncContext = syncContext;
this.no = no;
count++;
}
public void run() {
HttpServletResponse resp = (HttpServletResponse) syncContext.getResponse();
try {
Thread.sleep(no * 1000);
PrintWriter out = resp.getWriter();
out.println("<script type='text/javascript'>document.all.a" + no + ".innerHTML='<h1>" + no + "</h1>'</script>");
out.flush();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
if (count == 0) {
syncContext.complete();
}
}
}
下面是使用旧的Servlet规范实现分段输出的代码,由于旧的Servet规范没有包含异步处理功能,我们只能自己处理线程的同步和页面输出流的抢占问题。
public class BigpipeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
final PrintWriter pw = response.getWriter();
final StringBuffer content = new StringBuffer("<html><body>");
content.append("<div id='a1' style='width:100px;height:100px;border:1px solid black'></div>");
content.append("<div id='a2' style='width:100px;height:100px;border:1px solid black'></div>");
content.append("<div id='a3' style='width:100px;height:100px;border:1px solid black'></div>");
content.append("<div id='a4' style='width:100px;height:100px;border:1px solid black'></div>");
content.append("</body></html>");
pw.println(content.toString());
pw.flush();
final Paglet paglet1 = new Paglet(pw, 1);
final Paglet paglet2 = new Paglet(pw, 2);
final Paglet paglet3 = new Paglet(pw, 3);
paglet1.start();
paglet2.start();
paglet3.start();
try {
paglet3.join();
paglet2.join();
paglet1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
pw.close();
}
class Paglet extends Thread {
private int id;
private PrintWriter out;
public Paglet(PrintWriter out, int id) {
this.out = out;
this.id = id;
}
@Override
public void run() {
try {
sleep(id * 1000);
final String content = "<script>document.all.a" + id + ".innerHTML='<h1>" + id + "</h1>'</script>";
synchronized (out) {
out.println(content);
out.println();
out.flush();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
原文地址:http://www.coolfancy.com/log/26.html
更多精彩原创文章请关注笔者的原创博客:http://www.coolfancy.com
分享到:
相关推荐
Bigpipe命令_V45_comref2
BigPipe技术java源代码,实现页面的单线程加载,和多线程加载,减少页面的加载时间
已调试成功,并做了优化,类似新浪微博BIGPIPE
bigpipe 基于struts2标签实现
bigpipe-example, 使用koa和组件的[DEPRECATED] BigPipe BigPipe示例 使用 koa 和组件插件构建的应用程序,实现了facebook的 BigPipe 。给定 subreddit,这里应用程序执行以下操作:一个基于 horizontal-grid-...
NULL 博文链接:https://secondhrc.iteye.com/blog/1837984
Koa Bigpipe演示生成基础koa工程使用koa-bigpipe作为bigpipe库默认使用pug模板引擎 jQuery + bigpipe.js $ nvm use 7Now using node v7.9.0 (npm v4.2.0)$ npm start
用asp.net mvc2.0 实现bigbipe技术,这只有C#部份代码,javascript部分还未实现,以后再补吧。
bigpipe对应的tmsh命令及linux对应的tmsh命令,TMSH命令行操作bigpipe对应的tmsh命令及linux对应的tmsh命令
NET BIGPIPE 实现,前后台代码。
bigpipe-demo1 简单的 bigpipe 演示
net bigpipe 例子,调试成功
自己实现的bigpipe,C# 实现,比较简单,可以正常运行
BigPipe 是 Facebook 开发的优化网页加载速度的技术。网上几乎没有用 node.js 实现的文章,实际上,不止于 node.js,BigPipe 用其他语言的实现在网上都很少见。以至于这技术出现很久以后,我还以为就是整个网页的...
NULL 博文链接:https://liaoke0123.iteye.com/blog/2312795
用C#实现的仿新浪微博首页数据的加载方式。测试了下谷歌和IE8都支持,同样的数据处理量:BigPipe加载方式耗时1.22秒,传统方式加载3.01秒。
其思路是使用多个可重用的网页部件来组合整个网页。这些部件称为 Pagelets ,然后通过在服务器和浏览器上进行不同的执行方法来组合。这使得前端页面可以按进度进行内容的渲染,提升了前端的速度。Most web ...
测试我自己编写的koa-bigpipe-middlewary, 模拟bigpipe渲染技术 环境 node >= 8.x 执行 npm install node app.js 打开localhost:9000
用asp.net mvc2.0 实现bigbipe技术,该版修改了异步线程flush html错乱的bug与加入了bigpipe js框架。非常感谢http://my.csdn.net/SeaSunk提供的js框架。
外部的 外部是一个双重用途的库。 它附带了一个客户端框架,以尽...客户端组件由各种微型模块组成,可以使用进行构建。 只需在客户端代码中使用external模块,即可将其内置到其他浏览器化组件中。 可以通过npm安装此框