一次分佈式架構cms系統頁面靜態化模塊開發經歷

項目背景

公司開發新的項目,選擇了分佈式系統架構,同時項目中有大量圖文展示的需求,考慮到開發效率和性能問題,需要集成cms的功能,程序實現後臺錄入數據靜態化。

技術架構

java開發、jdk1.8,項目整體的技術架構大致是這樣的:

架構圖 
前端採用的bootstrap、extJs,權限採用shiro框架,搜索採用solr或者lucene(這個暫時沒有實現)。

過往問題

在這個項目之前,公司有另一套cms系統,做爲公司主要經營業務的基礎,它有很多優點,最主要的優點是定製性強,能適應各種前端版式的頁面。另一個優點是開發速度特別快,基於模版標籤開發,屏蔽了代碼級的複雜度和開發人員水平的差距。 
弱點有三個 
一、在於標籤庫的實現,完全是基於正則替換,然後處理標籤表述的邏輯進行查庫、處理。標籤沒有實現語法樹,也沒有上下文,導致了多個標籤函數嵌套的時候,某些時候無法正確實現替換邏輯。 
二、沒有應用更多的緩存技術(只實現了jvm內的HashMap緩存),數據量大的任務,生成速度在查庫上,有明顯的瓶頸。 
三、生成失敗的模版,沒有給出足夠明確的錯誤提示,輔助使用者調試錯誤。

基本思路

整個大體流程

這裏寫圖片描述

整個生成是基於freemarker模版引擎,項目初期,曾經對比過velocity與freemarker,因爲freemarker最後一次更新時間比較近,最終選擇了freemarker,其實兩者應該差不多。

關鍵點

先曬一下目前的類結構 
這裏寫圖片描述

一、自定義標籤與自定義函數

freemarker支持實現TemplateMethodModelEx接口的自定義函數,及實現TemplateDirectiveModel的自定義標籤。上面代碼,fun包下面的就是自定義函數,tags包下面的是自定義標籤。 
兩者的區別, 
1、從調用方法來看,

arts(10, 10, ”, ”)

這種是自定義函數 
<@arts menu="15" pagesize="15" sort="" sql="";arts, page></@arts> 
這種是自定義標籤 
2、自定義函數有返回值,可以再套入別的函數進行計算或者迭代。自定義標籤沒有返回值,它會返回幾個變量,作用域只在標籤體內部。 
3、自定義標籤傳入的參數有Environment、TemplateDirectiveBody等等,即可以獲取其他位置定義的模版變量,又可以追加變量,還可以輸出字符串文本到最終生成的頁面上。自定義函數,只能僅僅返回處理後的返回值。

二、freemarker生成靜態頁到指定路徑的方法

這個問題,基本上查api或者別的博客就能找到具體的方法,但是有些資料可能不準確,這裏我直接把我實現了的核心代碼貼出來。我使用的freemarker版本是2.3.23。

//成員變量
private static Configuration config = new  Configuration(Configuration.VERSION_2_3_25);
//config設置
config.setLocale(Locale.CHINA);  
            config.setDefaultEncoding("utf-8");  
            config.setEncoding(Locale.CHINA, "utf-8");  
            config.setObjectWrapper(new DefaultObjectWrapper(Configuration.VERSION_2_3_25));

StringTemplateLoader stringLoader = new StringTemplateLoader();  
//content是模版內容的字符串        stringLoader.putTemplate("siteTemplate",content);  
        config.setTemplateLoader(stringLoader);
try(FileWriter out = new FileWriter(filePath.toFile());) {
             Template template = config.getTemplate("siteTemplate","utf-8");  
             config.setCustomAttribute("customDataModel", dataModel);
             template.process(dataModel, out);
             out.flush();
        } catch (Exception e) {
            //異常處理
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

三、列表頁分頁,如何根據一個模版,動態生成出一或多個分頁面

這個問題包括很多細節,比如生成後的地址規則。假設目標地址是news_index.html,那麼第二頁就不能用這個地址,我的處理方式是news_index_x.html,x代表第幾頁,可以是2~n。但是第一頁就是原地址。 
最關鍵 
如果你也需要基於freemarker實現這個功能,大概機會發現,最關鍵的問題,是如何在生成第一頁的時候,知道還有第二頁需要生成。因爲產生多個分頁面,一般是在自定義標籤裏面,比如有一個循環數據列表的標籤,發現一頁10條,無法顯示完,怎樣告知生成階段的代碼(就是上面那一段)。這是兩個完全不同的代碼區域和處理階段。 
參考了另一款開源cms,我採取了一種有點技巧性的做法。核心思想就是,上面說到自定義標籤,可以在生成後的頁面上,任意輸出字符串。假設標籤中判斷還有下一頁,需要生成,就在頁面上輸出一段註釋,本頁面生成完以後,重新讀取生成的頁面,發現有包含此種註釋,則繼續生成下一頁。

下面貼一些代碼

//輸出還有下一頁的標識
env.getOut().write(LBLCommon.getHasNextPage(totalRecord, totalPage, pageIndex));

public static String getHasNextPage(int recordCount, int totalPage, int pageindex) {
        return hasNextPage+"_"+recordCount+"_"+totalPage+"_"+pageindex+"-->";
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

//生成靜態化頁面 this.writeUseFreeMarker(name, content, url, dataModel); 
//分頁機制,查詢是否需要分頁———————————————————— 
this.morePageProcess(name, dataModel, url, rootPath, content, entry);

//處理分頁
private void morePageProcess(String name, Map<String, Object> dataModel, String url, String rootPath, String content, Map.Entry<String, String> entry) throws Exception{
    String html = this.readFile(url);
    LBLPagerModel pager = checkHasNextPage(html);
    if (pager != null) {
        //重設置一個基礎的總記錄數
        dataModel.put("totalRecord", pager.getRecordCount());
        for (int i = 2; i<= pager.getTotalPage(); i ++) {
            //設置頁碼
            setPageIndex(dataModel, i);
            url = rootPath + LBLCommon.getPageUrl(entry.getValue(), i);
            this.writeUseFreeMarker(name, content, url, dataModel);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

整個靜態化的部分,還有很多別的細節和坑,比如標籤參數的轉型問題,生成靜態頁過程中模版exception的catch和反饋,查詢生成隊列情況的監控模塊(基於mongo),標籤設計,自動生成、定時生成、過濾生成…

總結

靜態化的功能,與業務結合的很緊密,好的標籤設計非常重要,這個可能得靠多踩坑才能提升一點躲開雷區的能力。另外,這次的開發過程,也讓我更深刻的認識到,深思熟慮,先慢後快,不寫一行垃圾代碼,纔是最節約時間的開發模式。

發佈了27 篇原創文章 · 獲贊 14 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章