关于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的意义在于流水化,从而使用户尽可能快的得到页面响应。
以上是个人浅显的理解,希望以后能继续深入:)