Facebooks BigPipe Done in Java

the blog for developers

Facebooks BigPipe Done in Java

BigPipe is a way of thinking for web pages. It introduces the concept of pagelets, small parts of the website. BigPipe was implemented at Facebook:

BigPipe is a fundamental redesign of the dynamic web page serving system. The general idea is to decompose web pages into small chunks called pagelets, and pipeline them through several execution stages inside web servers and browsers. This is similar to the pipelining performed by most modern microprocessors: multiple instructions are pipelined through different execution units of the processor to achieve the best performance.

BigPipe renders the structure of a webpage and then adds the content via JavaScript, a kind of inlined AJAX. The result is that users see content earlier and progressivly. Just reload a Facebook page and see how different parts are loaded. This reduces latency and especially changes the perception of users:They think the website is faster. A side effect of splitting the page into pagelets is how each pagelet can be rendered in parallel with asynchronous IO on the server or with an IO thread pool.

With threads, message piping or a worker model very long running service calls do not stop the page from loading, it will only prevent showing the pagelet that is slow. Each thread can have a timeout and you can kill long running or blocking service calls – a SLA for page calls.

Sam Pullaras mustache.java follows the same principles, breaking rendering into parts each with it’s own thread to reduce latency.

I’ve implemented a proof of concept of BigPipe in Java (should run as-is in every servlet container):

       
       
public class BigPipeServlet extends HttpServlet {

     private static ExecutorService executor = Executors . newFixedThreadPool ( 500 , new ThreadFactory () {
         public Thread newThread ( Runnable r ) {
             Thread t = new Thread ( r );
             t . setName ( "Service Thread " + t . getId ());
             t . setDaemon ( true );
             return t ;
         }
     });

     protected void service ( HttpServletRequest req , final HttpServletResponse resp ) throws ServletException , IOException {
         final PrintWriter writer = resp . getWriter ();

         String doctype = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n" +
                 " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" ;

         String head = "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"de\" lang=\"de\"> \n" +
                 "<head> \n" +
                 "<meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" /> \n" +
                 "<meta http-equiv=\"Content-language\" content=\"de\" />\n" ;

         writer . write ( doctype );
         writer . write ( head );
         writer . write ( "<script type=\"text/javascript\">function arrived(id,text) { var b=document.getElementById(id); b.innerHTML = text; }</script>" );
         writer . write ( "</HEAD><BODY><div>Progressive Loading" );

         content ( writer , "content1" , "content2" , "content3" , "content4" , "content5" , "content6" );
         writer . write ( "</div>\n" );

         final Random random = new Random ();

         List < Callable < Boolean >> tasks = new ArrayList < Callable < Boolean >>();

         for ( int i = 0 ; i < 6 ; i ++) {
             final int id = i + 1 ;
             tasks . add ( new Callable < Boolean >() {
                 public Boolean call () {
                     try {
                         // One service call is nasty and takes 50sec
                         if ( id == 3 ) {
                             Thread . sleep ( 50000 );
                         } else {
                             Thread . sleep ( random . nextInt ( 2000 ));
                         }
                         pagelet ( writer , "content" + id , "Wohooo" + id );
                     } catch ( InterruptedException e ) {
                         return false ;
                     }
                     return true ;
                 }
             });
         }

         try {
             executor . invokeAll ( tasks , 1500 , TimeUnit . MILLISECONDS );
         } catch ( InterruptedException ignored ) {
             // ignored
         }
         writer . write ( "</BODY></HTML>" );
     }

     private void content ( PrintWriter writer , String ... contentIds ) {
         for ( String id : contentIds ) {
             writer . write ( "<div id=\"" + id + "\">-</div>\n" );
         }
     }

     private void pagelet ( PrintWriter writer , String id , String content ) {
         if ( writer . checkError ()) return ;
         writer . write ( "<script>" +
                 "arrived(\"" + id + "\", \"" + content + "\");" +
                 "</script>\n" );
         writer . flush ();
     }
}
view raw BigPipe.java This Gist brought to you by  GitHub.

This will result in the following HTML code – see how content2 is rendered before content1. Due to threads the order in which content arrives is non-deterministic.

       
       
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns= "http://www.w3.org/1999/xhtml" xml:lang= "de" lang= "de" id= "facebook" class= " no_js" >
<head>
<meta http-equiv= "Content-type" content= "text/html; charset=utf-8" />
<meta http-equiv= "Content-language" content= "de" />
<script type= "text/javascript" > function arrivedHtml ( id , text ) { var b = document . getElementById ( id ); b . innerHTML = text ; } </script></HEAD><BODY>
<div>Progressive Loading
   <div id= "content1" >- </div>
   <div id= "content2" >- </div>
</div>
<script> arrivedHtml ( "content2" , "Wohoo" ); </script>
<script> arrivedHtml ( "content1" , "Wohoo" ); </script>
</BODY></HTML>
view raw gistfile1.html This Gist brought to you by  GitHub.

Some problems I’ve encountered:

  • You need the correct content type, content encoding and doc versiom, otherwise the page will not render progressivly
  • Your framework needs to enable flushing the output at certain points, so content is pushed to the browser

I would like to have some framework support and I’m taking a look into how to do this in Play.

You can leave a Reply hereOf course, you should follow me on twitter here.

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值