剝離靜態資源請求到CDN
一般在主域名下的HTTP請求里都會攜帶大量Cookie信息,最大4KB,每個域名下最多50條;但如果僅僅訪問js/css/jpeg等靜態資源文件的話是不需要Cookie信息,所以可以將整個站點的靜態資源放到一個專門的域名下,以求減小網絡開銷,也就是Cookie free domain;
多域名存儲資源
瀏覽器在對同一個域名下的並發請求資源數量是有上限的(IE為8個,chrome為4-6個),一個完整的html頁面需要加載的資源一般已經超過100個,所以為了縮短加載速度可以將下載資源分布在多個域名下(也不能太多,DNS查詢也需要耗時);這樣不僅可以增加資源加載的並發數,還可以實現靜態資源Cookie Free加載;
合並HTTP請求
加速網頁加載不僅需要加快單個請求速度的同時,還需要減少總的請求個數,css spirites可以將多張圖片合並成一張圖片,這樣只需要一次請求,不同css展示圖片上不同的位置;通過使用smartspirites命令在打包的時候講圖片合並成一張圖片;
壓縮請求資源
將js/css進行concat, minify和compress,最終合並成一個大的js/css,然后使用gzip生成.gz格式的文件,只要在request里指定accept-encoding=gzip,deflate,則可以請求打包之后的.gz文件,極大降低http請求的個數和帶寬的負載壓力。基於Ant的構建打包里js/css的合並、混淆和壓縮可以通過concat, closure實現,而gzip則需要client browser和web server同時約定一些規定。首先client browser發送的request要寫明accept-encoding=gzip,deflate,表示可以接受gzip的文件編碼;然后web server將js/css文件打包成跟源文件具有同樣名字路徑的gz后綴文件,並且在response里寫明content-encoding=gzip,這樣web server響應請求的時候就可以返回gz文件,web browser也可以正確對gz文件進行解析。
合理利用瀏覽器cache
瀏覽器會將網頁資源cache到客戶端本地,然后通過HTTP request header里的cache-control,expires等來控制cache的生命周期。
典型的HTTP request消息頭
cache-control: no-cache cookie: _xsrf=e6552b76-a418-4409-9ec5-967573ea1cda; origin: https://zhuanlan.zhihu.com pragma: no-cache referer: https://zhuanlan.zhihu.com/c_120823325?utm_source=wechat_session&utm_medium=social&utm_oi=54126419378176 user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
典型的HTTP response消息頭
access-control-allow-credentials: true access-control-allow-origin: https://zhuanlan.zhihu.com cache-control: private, no-store, max-age=0, no-cache, must-revalidate, post-check=0, pre-check=0 content-encoding: br content-type: application/json date: Mon, 09 Jul 2018 11:39:10 GMT etag: W/"88c2b758dbbd144dc7a13a6134675430cc50ad7a" expires: Fri, 02 Jan 2000 00:00:00 GMT pragma: no-cache server: ZWS set-cookie: tgw_l7_route=ec452307db92a7f0fdb158e41da8e5d8; Expires=Mon, 09-Jul-2018 11:54:10 GMT; Path=/ status: 200
expires:web服務器響應的頭字段,表示瀏覽器在某個時間點(絕對時間點,存在服務器時區不匹配問題)之前可以使用該資源的緩存;但expires字段是HTTP 1.0的定義,現在瀏覽器一般默認為HTTP 1.1,所以它的作用基本可以忽略。(另一個基本可以忽略的是pragma: no-cache,由於其屬於html文件內容的meta信息,所以僅有某些瀏覽器支持)
cache-control:用於替換expires字段,所以優先級一般高於expires字段,cache-control的值解釋如下,max-age用的是當前時間的相對值(Cache-Control: max-age=30000)。
etag;屬於HTTP 1.1的特性,具有比last-modified更高的優先級,表示有web server根據resource的path,size, last modified date進行hash后生成的值,可以判斷文件在前一次響應之后是否有被修改。etag可以解決last-modified的幾個問題,last-modified只能精確到秒級別,如果恰好resource在1秒內更新了,則client browser不能根據last-modifed進行判斷;如果文件僅僅是被touch,文件內容沒有改變,last-modified卻改變了。
If-None-Match:在client browser判斷resource cache已經expire后,如果之前的response里有etag字段,則重新發出請求並帶上該字段,web server收到請求后對比request里的etag和web server上resource最新生成的etag值,如果不相等,則響應整個resource並設置status code = 200,否則直接返回status code=304。
Last-Modified:表示web server告訴client browser當前resource的最近修改時間。
If-Modified-Since:在client browser判斷resource cache已經expire后,如果之前的response里有Last-Modified字段的話,則重新發出請求並帶上該字段,web server在收到請求后對比request里的Last-Modified和web server上resource的實際的修改時間,如果實際修改時間較新,則響應整個resource並設置status code=200,否則直接返回status code=304。
使用靜態頁面
將內容變動頻次低的頁面做成靜態頁面(相對於JSP等內容需要實時生成的動態頁面),並緩存到web server內存或者CDN上,常見的技術有freemarker;JSP頁面需要依賴app server提供的容器環境,而freemarker可以完全脫離app server(Jetty),簡單的web server(Nginx)就可以滿足訪問需求。https://freemarker.apache.org/
直接使用freemarker合並ftl文件和data,並生成html文件,html文件可以直接放置到CDN或者web server上供用戶訪問。
1 class App1 { 2 private static final Configuration freeMarkerConfig; 3 static { 4 freeMarkerConfig = new Configuration(Configuration.VERSION_2_3_23); 5 try { 6 freeMarkerConfig.setDefaultEncoding("UTF-8"); 7 freeMarkerConfig.setTemplateExceptionHandler( 8 TemplateExceptionHandler.RETHROW_HANDLER); 9 freeMarkerConfig.setTemplateLoader( 10 new ClassTemplateLoader(App1.class, "/template")); 11 //freeMarkerConfig.setDirectoryForTemplateLoading(dir); 12 } catch (Exception e) {} 13 } 14 public void generateHtmlByTemplate(String ftlFileName, 15 String htmlFileName, Map<String, Object> params) { 16 String dir = ""; 17 Writer fileWriter = null; 18 try { 19 File file = new File(dir + File.separator + htmlFileName); 20 Template template = freeMarkerConfig.getTemplate(ftlFileName); 21 fileWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); 22 template.process(params, fileWriter); 23 } catch (Exception e) { 24 } finally { 25 if (Objects.nonNull(fileWriter)) { 26 try { 27 fileWriter.flush(); 28 fileWriter.close(); 29 } catch (Exception e) {} 30 } 31 } 32 } 33 }