高性能页面加载技术(流水线加载)BigPipe的C#简单实现(附源码)
一,BigPipe简介
BigPipe是一个重新设计的基础动态网页服务体系。大体思路是,分解网页成叫做Pagelets的小块,然后通过Web服务器和浏览器建立管道并管理他们在不同阶段的运行。这是类似于大多数现代微处理器的流水线执行过程:多重指令管线通过不同的处理器执行单元,以达到性能的最佳。虽然BigPipe是对现有的服务网络基础过程的重新设计,但它却不需要改变现有的网络浏览器或服务器,它完全使用PHP和JavaScript来实现。(来源百度百科)
BigPipe可以简单理解为:将网页分为若干个块(pagelet),请求到达服务器后先初始化pagelet布局并Response.Write(这时用户能看到网页的大致布局),然后使用多线程并行执每个pagelet 的业务逻辑,每个pagelet线程执行完后立即Response.Write,对于用户来说,他看到的网页是一块块同时或先后呈现出来,和ajax异步加载比较类似。
二,实现思路
1, 前端实现:需要一个js接收服务端输出的HTML,并显示到对应的pagelet中,每个pagelet是一个局部视图(partialView), 代码如下:
1 var PageLet = PageLet || {}; 2 3 PageLet = (function () { 4 var LoadCss = function (css) { 5 var c = document.createElement("link"); 6 c.setAttribute("type", "text/css"); 7 c.setAttribute("rel", "stylesheet"); 8 c.setAttribute("href", css); 9 document.getElementsByTagName("head")[0].appendChild(c); 10 } 11 var LoadJs = function (js) { 12 var s = document.createElement("script"); 13 s.setAttribute("type", "text/javascript"); 14 s.setAttribute("src", js); 15 document.getElementsByTagName("head")[0].appendChild(s); 16 } 17 //PageLet对象公开InitHtml方法,在后台的输出HTML时会带上该方法。 18 19 var InitHtml = function (json) { $('#' + json.Pid).html(json.Html); LoadCss(json.Css); LoadJs(json.Js); } return { InitHtml: InitHtml } })();
2,服务端实现:先初始化页面pagelet布局并输出,然后用多线程并行执行没个pagelet里的业务,执行完后立即输出,这里需要注意的是:输出的HTML代码(partailView)需要做一个JS包裹,只有这样这段HTML代码才能在对应的pagelet显示。代码如下:
HomeController.cs
1 using MyBigPipe.Common; 2 using MyBigPipe.Models; 3 using Newtonsoft.Json; 4 using System; 5 using System.Collections.Generic; 6 using System.Linq; 7 using System.Threading; 8 using System.Threading.Tasks; 9 using System.Web; 10 using System.Web.Mvc; 11 12 namespace MyBigPipe.Controllers 13 { 14 public class HomeController : Controller 15 { 16 private static readonly object _sysRoot = new object(); 17 18 public void Index() 19 { 20 InitHomeHtml(); 21 22 Parallel.Invoke(() => 23 { 24 //模拟操作1秒 25 Thread.Sleep(500); 26 //头部块 27 InitTop(); 28 29 }, () => 30 { 31 Thread.Sleep(500); 32 InitLeft(); 33 34 }, () => 35 { 36 Thread.Sleep(1000); 37 //右边块 38 InitRight(); 39 40 }, () => 41 { 42 Thread.Sleep(1000); 43 //底部块 44 InitFoot(); 45 }); 46 } 47 48 private void InitTop() 49 { 50 ViewBag.Top = "TopTopTopTopTopTopTopTopTopTopTopTopTopTopTopTopTopTopTopTopTopTopTopTopTopTopTop"; 51 52 Pagelet pagelet = new Pagelet() 53 { 54 Css = "", 55 Html = Helper.RenderPartialViewToString(this, "TopPartial"), 56 //Js = "/Scripts/BigPipeT1.js", 57 Js = "", 58 Pid = "topId" 59 }; 60 lock (_sysRoot) 61 { 62 Response.Write("<script type='text/javascript'>PageLet.InitHtml(" + JsonConvert.SerializeObject(pagelet) + ");</script>"); 63 Response.Flush(); 64 Response.Clear(); 65 } 66 } 67 68 private void InitLeft() 69 { 70 ViewBag.Left = "bbbbbbbbbbbbbbbbbbbbbbb"; 71 72 Pagelet pagelet = new Pagelet() 73 { 74 Css = "", 75 Html = Helper.RenderPartialViewToString(this, "LeftPartial"), 76 Js = "", 77 Pid = "leftId" 78 }; 79 lock (_sysRoot) 80 { 81 Response.Write("<script type='text/javascript'>PageLet.InitHtml(" + JsonConvert.SerializeObject(pagelet) + ");</script>"); 82 Response.Flush(); 83 Response.Clear(); 84 } 85 } 86 87 private void InitRight() 88 { 89 90 Pagelet pagelet = new Pagelet() 91 { 92 Css = "", 93 Html = Helper.RenderPartialViewToString(this, "RightPartial"), 94 Js = "", 95 Pid = "rightId" 96 }; 97 lock (_sysRoot) 98 { 99 Response.Write("<script type='text/javascript'>PageLet.InitHtml(" + JsonConvert.SerializeObject(pagelet) + ");</script>"); 100 Response.Flush(); 101 Response.Clear(); 102 } 103 } 104 105 private void InitFoot() 106 { 107 lock (_sysRoot) 108 { 109 Response.Write( 110 new Pagelet() 111 { 112 Css = "", 113 Html = Helper.RenderPartialViewToString(this, "FootPartial"), 114 Js = "", 115 Pid = "footId", 116 PartialViewName = "FootPartial" 117 118 }.RenderHtml(this)); 119 Response.Flush(); 120 Response.Clear(); 121 } 122 } 123 124 /// <summary> 125 /// 初始化HTML网页框架 126 /// </summary> 127 private void InitHomeHtml() 128 { 129 var str = Helper.RenderPartialViewToString(this, "Index"); 130 Response.Write(str); 131 Response.Flush(); 132 Response.Clear(); 133 } 134 } 135 }
三,总结
BigPipe和Ajax二者区别:对于一个分成若干个块的页面而言,如果使用Ajax的话,每一块都需要单独发送一个HTTP请求,而如果使用BigPipe的话,不管有多少块,都仅有一个HTTP请
求。所以Ajax对服务器造成的压力会是BigPipe的若干倍。
BigPipe优点:速度快,将页面分割成多个pagelet,后台代码会为每个pagelet开启线程执行代码逻辑,执行完后立即输出到前端
BigPipe缺点:不利于SEO, 对于这点可以写两份代码,一份使用bigpipe,一份不使用, 用User Agen判断访问者是用户还是搜索引擎,分别执行对应的那份代码。当然这样工作量增加了。