BigPipe(FaceBook使用的頁面加載技術)
?
理論部分:用戶輸入域名發送請求到服務端,服務端組合出需要的業務數據返回給客戶端,這一過程是現在網頁請求最基本傳統的方式了。
好處:只做了一次http請求,節省了http連接資源
壞處:如果一次請求數據量過大,會比較慢,并且如果整個業務邏輯部分有一處出問題,很可能導致請求錯誤返回,整個頁面拿不到數據甚至癱瘓。
之后局部刷新技術ajax出現了:客戶端可以根據需要去向服務端發不同的請求,加載自己所需要的數據資源,這樣請求之間互不影響。
好處:獨立請求,可以分開加載數據,是作為分離業務邏輯,模塊化的加載的好方式。
壞處:分開加載無疑增加了http請求數,特別是模塊分的較多,希望都非常獨立的時候,這一樣勢必是在浪費連接資源;要知道在單次請求數據量很小的情況下,http連接資源可能是更昂貴的代價。
?
可能這個時候為了更加有效利用資源facebook的大牛們用了設計者Changhao Jiang (研究電子電路的博士)設計的技術bigpipe,并應用到實地場景中。
毫無疑問,無論是整個頁面一次請求還是ajax都是不優雅的,沒有有效利用前端和后端之間的時間差:
這種模式有個缺陷:流程中的操作有著嚴格的順序,如果前面的一個操作沒有執行結束,后面的操作就不能執行,即操作之間是不能重疊。這樣就沒有有效利用前后端資源:
服務器生成一個頁面的內容時,瀏覽器是空閑的,顯示空白內容;而當瀏覽器加載渲染頁面內容時,服務器又是空閑的, 時間與性能的浪費由此產生。
為了在前后端空閑時,更能有效的并行處理自己要干的事情(前端通過數據渲染頁面,后端包裝數據傳給前端),一個理想的方式就這樣誕生了:
前端發了一個請求后,后端根據前端的需要分步拿不同模塊的數據,拿好一個立即丟給前端去渲染,這個時候的優勢就體現出來:前端渲染后端給的第一部分數據的同時,后端在組裝第二部分數據,以此類推,這個并行工作就這樣展開了,直到完成所有數據的組裝和渲染。對于用戶來講看到的效果就是,打開頁面立即就有可看到的內容,不會因為后端數據多大或是頁面發出的請求過多,卡死頁面的渲染。
?
?
實踐demo部分:
?在js群友的幫助下找到一個可參考的測試案例:
http://my.oschina.net/hanshubo/blog/130713
代碼如下:在使用隊列方面沒有仔細斟酌,隨便找一個過來,就用了。?
注意一點,就是不要把?PrintWriter 的實例對象拿到多線程里去用,否則會出莫名其妙的異常。?
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
?
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
?
public class BigPipeServlet extends HttpServlet {
?
? ? private static ExecutorService executor = Executors.newFixedThreadPool(50);
?
? ? public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
? ? ? ? final ArrayBlockingQueue<String> q = new ArrayBlockingQueue<String>(6);
?
? ? ? ? for (int i = 0; i < 6; i++) {
? ? ? ? ? ? final int id = i + 1;
?
? ? ? ? ? ? executor.execute(new Runnable() {
? ? ? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? ? ? Thread.sleep((int) (Math.random() * 10000));
? ? ? ? ? ? ? ? ? ? ? ? q.put(pagelet("content" + id, "Wohooo" + id));
? ? ? ? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? });
? ? ? ? }
?
? ? ? ? response.setContentType("text/html;charset=gb2312");
? ? ? ? PrintWriter out = response.getWriter();
? ? ? ? out
? ? ? ? ? ? ? ? .println("<html><head>"
? ? ? ? ? ? ? ? ? ? ? ? + "<script type=\"text/javascript\">function arrived(id,text) { var b=document.getElementById(id); b.innerHTML = text; }</script>"
? ? ? ? ? ? ? ? ? ? ? ? + "</head><body>" + "<div>Progressive Loading");
? ? ? ? content(out, "content1", "content2", "content3", "content4", "content5", "content6");
? ? ? ? out.println("</div>");
?
? ? ? ? for (int i = 0; i < 6; i++) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? out.println(q.take());
? ? ? ? ? ? ? ? out.flush();
? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
?
? ? ? ? out.println("</body></html>");
? ? }
?
? ? private void content(PrintWriter writer, String... contentIds) {
? ? ? ? for (String id : contentIds) {
? ? ? ? ? ? writer.println("<div id=\"" + id + "\">-</div>");
? ? ? ? }
? ? }
?
? ? private String pagelet(String id, String content) {
? ? ? ? return "<script>" + "arrived(\"" + id + "\", \"" + content + "\");" + "</script>";
? ? }
}
這個案例的服務端是java實現的,有多線程就是有福,不過php也不是不可以,他的模塊的擴展能輔助搞定這個問題。
簡單講一下基本做法:
后端創建一個線程池,去維護前端需要的模塊數的線程(有幾個模塊就創建幾個線程),然后每個線程response write之后立即flush,這樣每個線程的操作就立即返給了前端,
由于http管道只有一個,后端多線程就無法做到多線程并發去flush了,需要堵塞一個個操作或者加鎖。做個猜想:如果http管道中也能相應產生多個獨立的位置讓多線程并發去append到指定的位置,
這樣是不是就做到了,多個部分數據可以同時丟給前端。(可能理解有誤)
并在前端執行操作。由于不像ajax那樣單個請求前后端邏輯完全獨立,在bigpipe中,設定好每個模塊的順序就是必須的了。
struts2 自定義標簽實現的
https://www.ibm.com/developerworks/cn/java/j-lo-bigpipe/
注:nginx gzip打開時,out.flush無效,懷疑是數據量沒達到nginx默認緩存,不會輸出,只等到所有數據一起輸出,
這樣就達不到bipipe的目的了。
成功案例部分:
新浪微博:http://blog.sina.com.cn/s/blog_482611850100xpb1.html
http://v.youku.com/v_show/id_XMzUyOTgyMDY4.html
簡單介紹下:
新浪微博提到了用
HTTP協議的chunked編碼的方式來處理多個部分
?
?
淘寶:
[淺析]淘寶詳情頁的BigRender優化的最佳方式
?http://www.csdn.net/article/2011-09-27/304989
?
?
參考:http://www.cnblogs.com/mofish/archive/2011/11/03/2234858.html
?
?