关于web应用,从html的产生,到客户端浏览器的渲染,有3个重要的组成部分:
1.html在服务端生成
2.网络传输
3.浏览器渲染
在html生成的过程中,可能会用到cache,可能会链接数据库等等,对于负责的html页面,都要经过很多业务流程
facebook的做法是使html的生成变成多个步骤,每生成一小部分html(facebook给这种编程模型起了个名字叫做PageLet),就发送到网络上,浏览器就先展示最先发送的一部分,使整个页面传输的过程流水化,提高页面呈现速度。
经过流水化的页面生成后,整个过程变为:
可以很直观的看到,经过流水化处理后,整体速度快了1倍。
下边是一个java实现的简易BigPipeDemo
package com.opencfg.web;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Facebook BigPipe Demo
*
* @author haitao.tu
* @date 2010-01-03
*/
public class BigPipeServlet extends HttpServlet {
private static final long serialVersionUID = 1344983665902790319L;
@Override
protected void service(HttpServletRequest req,
final HttpServletResponse resp) throws ServletException,
IOException {
PrintWriter writer = resp.getWriter();
String doctype = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">";
String head = "<html>"
+ "<head>"
+ "<meta http-equiv='Content-type' content='text/html; charset=utf-8' />"
+ "<meta http-equiv=\"Content-language\" content=\"zh\" />";
writer.write(doctype);
writer.write(head);
writer.write(build_script_function());
writer.write("</head><body><div>loading...");
build_frame(writer, "id1", "id2", "id3", "id4", "id5", "id6");
writer.write("</div>");
pagelet(writer, "id1", "PageLet1");
sleep(4000);
pagelet(writer, "id2", "PageLet2");
sleep(4000);
pagelet(writer, "id3", "PageLet3");
sleep(4000);
pagelet(writer, "id4", "PageLet4");
sleep(4000);
pagelet(writer, "id5", "PageLet5");
sleep(4000);
pagelet(writer, "id6", "PageLet6");
writer.write("</body></html>");
writer.close();
}
/**
* 构造页面原型
*/
private void build_frame(PrintWriter writer, String... ids) {
for (String id : ids) {
writer.write("<div id='" + id + "'>-</div>");
}
}
/**
* 构造javascript方法
*/
private String build_script_function() {
return "<script type='text/javascript'>function show(id,text){document.getElementById(id).innerHTML = text;}</script>";
}
/**
* 构造javascript调用
*/
private String build_script_call(String id, String content) {
return "<script>show('" + id+ "','" + content + "')</script>";
}
/**
* 发送facebook pagelet html包
*/
private void pagelet(PrintWriter writer, String id, String content) {
if(writer.checkError())
return;
writer.write(build_script_call(id, content));
writer.flush();
}
private void sleep(int times) {
try {
Thread.sleep(times);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
下边用wireshark抓包,看下是否产生想要的流水效果:
图中可以看出"TCP segment",至于收到一个报文后如何确定它是一个"TCP segment"?
如果有几个报文的ACK序号都一样,并且这些报文的Sequence Number都不一样,并且后一个Sequence Number为前一个Sequence Number加上前一个报文大小再加上1的话,肯定是TCP segment了。
在硬件上有很多网络设备可以在收到TCP大报文后切分成小报文发送出去,但是BigPipe的意义在于流水化,从而使用户尽可能快的得到页面响应。
以上是个人浅显的理解,希望以后能继续深入:)