陳月燕 鄒慶 邊雁 吳冠宇
(國電南瑞科技股份有限公司 江蘇省南京市 211106)
隨著互聯(lián)網(wǎng)技術(shù)的飛速發(fā)展,電力系統(tǒng)個性化人機交互界面需求越來越強烈,不僅要滿足業(yè)務(wù)展示需求和性能要求,對頁面的美觀度和交互體驗的要求也在逐漸的提升。這就導(dǎo)致前端頁面開發(fā)工作量越發(fā)的繁重,傳統(tǒng)頁面代碼開發(fā)方式存在效率低下、技術(shù)門檻高等問題,極大的制約了開發(fā)者的工作效率??梢暬撁婢庉嬒到y(tǒng)為解決這一問題應(yīng)運而生,大大的提高了系統(tǒng)的開發(fā)效率,減少了開發(fā)者的開發(fā)難度。隨著系統(tǒng)的應(yīng)用范圍逐步的擴大,越來越多的業(yè)務(wù)系統(tǒng)開發(fā)者使用可視化頁面編輯系統(tǒng)代替手動編寫頁面,這就導(dǎo)致頁面模板的規(guī)模越來越龐大,原來的單一發(fā)布方式容易造成文件存放混亂、結(jié)構(gòu)不清晰的問題,同時也為功能擴展和權(quán)限集成帶來很大的困難。
本文結(jié)合可視化頁面編輯系統(tǒng)在電力系統(tǒng)中的實際應(yīng)用場景,使用基于NodeJS 的Web 開發(fā)框架Koa2,配合文件存儲和SQLite數(shù)據(jù)庫雙存儲模式,實現(xiàn)擴展組件、頁面模板、圖片等資源文件的分類管理功能。
NodeJS 是為了開發(fā)高性能Web 服務(wù)器而誕生的一種技術(shù),它是一個基于ChromeV8 引擎,能夠快速構(gòu)建網(wǎng)絡(luò)服務(wù)與應(yīng)用的運行在服務(wù)器端的JavaScript 執(zhí)行平臺[1]。作為一個JavaScript 服務(wù)端開發(fā)平臺,開發(fā)簡單并且易于擴展,同時也具有事件驅(qū)動,異步和非堵塞I/O 等特點[2]。NodeJS 采用了模塊化管理,允許開發(fā)者將業(yè)務(wù)邏輯分解成不同的模塊,通過按需導(dǎo)入模塊的方式實現(xiàn)最大程度的輕量級復(fù)用,從而實現(xiàn)資源共享減少開發(fā)者的重復(fù)勞動。NodeJS跳過了Apache、IIS 等HTTP 服務(wù)器,不用建設(shè)在任何服務(wù)器軟件之上,可以提供強大的伸縮能力,不需要安裝其他的Web 容器,使得安裝部署非常的簡單。
Koa 是Express 團隊開發(fā)的一個基于NodeJS 平臺的Web 應(yīng)用框架,致力于成為web 應(yīng)用和API 開發(fā)領(lǐng)域的一個更小、更富有表現(xiàn)力和更健壯的web 框架?;贙oa 搭建的后端服務(wù)擁有靈活的擴展機制,可以避免重復(fù)繁瑣的回調(diào)函數(shù)嵌套,并極大地提高錯誤處理的效率[3]。Koa2 與Koa1 的最大區(qū)別是實現(xiàn)異步的方式不同,Koa2 使用了ES7 的async/await 特性,完全使用promise 并結(jié)合async 來實現(xiàn)異步,極大地解決了異步操作帶來的煩惱,因此開發(fā)異步Web 程序變得更加簡單而有效。
SQLite 是D.Richard 使用C語言編寫的一個開源嵌入式數(shù)據(jù)庫引擎,也是一款遵守ACID 的輕量級關(guān)系型數(shù)據(jù)庫,實現(xiàn)了自給自足、無服務(wù)器、零配置、事務(wù)性的SQL 數(shù)據(jù)庫引擎[4]。相比于其他的關(guān)系型數(shù)據(jù)庫,SQLite 優(yōu)點如下:
(1)輕量級,SQLite 是進程內(nèi)的數(shù)據(jù)庫,不存在其他數(shù)據(jù)庫的客戶端和服務(wù)器。它本質(zhì)上是一個文件,所以不需要服務(wù)器進程和數(shù)據(jù)庫遠程鏈接;
(2)讀取速度快,占用資源低,整個數(shù)據(jù)庫存儲在單一文件中,所以處理速度比大部分數(shù)據(jù)庫都要快(配置簡單,不需要系統(tǒng)做任何配置,就可以直接使用,也不需要安裝和管理);
(3)維護簡單,數(shù)據(jù)庫中的所有信息(表、視圖等)都存儲在一個文件中,可以很簡單的復(fù)制到其他的機器上,而不用擔心數(shù)據(jù)庫遷移出現(xiàn)的數(shù)據(jù)丟失等問題;
(4)跨平臺性,除了支持主流操作系統(tǒng)之外,還支持很多不常用的操作系統(tǒng),比如Symbin、Palm 等。
但是也存在一些缺點和不足:
(1)在并發(fā)讀寫性能方面比較欠缺,并發(fā)數(shù)一般不超過10 萬、寫入較慢;
(2)SQL 標準支持不全,支持大多數(shù)SQL92 標準等。
可視化頁面編輯系統(tǒng)采用B/S 架構(gòu)實現(xiàn),瀏覽器端采用MVVM 設(shè)計模式,服務(wù)器端采用Koa2 框架搭建,數(shù)據(jù)庫存儲采用SQLite。采用前后端完全分離的設(shè)計模式,兩者獨立開發(fā)、獨立部署,所有的服務(wù)器端功能以RESTful API 服務(wù)的形式提供給瀏覽器端調(diào)用[5],完成前后端的數(shù)據(jù)交互。Koa2 沒有提供有關(guān)組織NodeJS 項目的任何線索,但是正確的構(gòu)建項目結(jié)構(gòu)將提升系統(tǒng)的靈活性和復(fù)用性、提高服務(wù)的穩(wěn)定性和擴展性,并且有利于后期維護?;诖四康?,本文借鑒三層架構(gòu)[6]的分層式設(shè)計思想,結(jié)合Koa2的開發(fā)模式,從結(jié)構(gòu)上劃分為四層。系統(tǒng)總體架構(gòu)如圖1所示。
圖1:系統(tǒng)總體架構(gòu)圖
3.1.1 路由層
接收瀏覽器端發(fā)出的請求,路由層攔截到請求信息,根據(jù)接口信息轉(zhuǎn)發(fā)給相對應(yīng)的表示層處理用戶的請求。
3.1.2 表示層
表示層的主要功能是實現(xiàn)系統(tǒng)數(shù)據(jù)的輸入和輸出,解析請求中的參數(shù),對提交的參數(shù)進行校驗和處理,然后調(diào)用業(yè)務(wù)邏輯層進行處理;接收業(yè)務(wù)邏輯層返回的數(shù)據(jù)進行封裝,返回給瀏覽器端。
3.1.3 業(yè)務(wù)邏輯層
業(yè)務(wù)邏輯層是表示層和數(shù)據(jù)訪問層的中間橋梁,當接收表示層的用戶指令后,對具體的問題進行邏輯判斷與執(zhí)行操作,連接數(shù)據(jù)訪問層獲取數(shù)據(jù)進行邏輯處理操作,然后將處理結(jié)果返回給表示層。
3.1.4 數(shù)據(jù)訪問層
數(shù)據(jù)訪問層實現(xiàn)數(shù)據(jù)庫連接以及數(shù)據(jù)模型的創(chuàng)建封裝,數(shù)據(jù)庫和磁盤文件數(shù)據(jù)的增、刪、改、查等操作api 封裝,并將操作的結(jié)果返回到業(yè)務(wù)邏輯處理層。
在可視化頁面編輯系統(tǒng)中,開發(fā)者通過瀏覽器端在線設(shè)計的擴展組件、頁面模板以及頁面中的圖片等資源文件數(shù)據(jù),按照分類發(fā)布到服務(wù)器端保存。同時,在頁面設(shè)計的過程中,也需要向服務(wù)器端請求這些資源文件。因此,可視化頁面編輯系統(tǒng)后端服務(wù)主要提供自定義組件、頁面模板的讀取、上傳和刪除功能、以及靜態(tài)資源的讀取、上傳和刪除功能。功能模塊圖如圖2所示。
圖2:系統(tǒng)功能模塊圖
中間件是處于請求和響應(yīng)之間的輕量級模塊,每個中間件執(zhí)行特定的功能[7]。Koa2 是一個中間件框架,在Koa2 中一切的流程都是中間件,中間件的執(zhí)行順序不是從頭到尾,而是數(shù)據(jù)流向遵循先入后出的洋蔥模型[7],先從最外面的中間件開始執(zhí)行,然后next()后進入下一個中間件,每次執(zhí)行下一個中間件傳入兩個參數(shù)ctx 和next,其中ctx 封裝了request 和response。一路執(zhí)行到最內(nèi)層的中間件后,再從最內(nèi)層往外執(zhí)行。
Koa2本身并未捆綁任何中間件,而是提供了應(yīng)用(Application)、上下文(Context)、請求(Request)、響應(yīng)(Response)四個模塊,讓使用者更大程度上構(gòu)建個性化的應(yīng)用。因此,基于Koa2 開發(fā)的過程就是使用已有中間件和構(gòu)建個性化中間件的過程,以下結(jié)合四層架構(gòu)說明中間件的實現(xiàn)流程。
3.3.1 路由實現(xiàn)
系統(tǒng)路由使用koa-router 中間件來實現(xiàn),koa-router 可以管理處理函數(shù)和url 之間的映射,將請求的URL 和請求方式(post、get 等)匹配到對應(yīng)的響應(yīng)接口服務(wù)??紤]到涉及多個業(yè)務(wù)模塊和后期業(yè)務(wù)的擴展性,對路由模塊進行了拆分和嵌套,也就是創(chuàng)建3 個Router實例,然后將被嵌套的模塊路由作為父級路由的中間件使用,在入口文件app.js 中只引入路由入口文件。路由入口文件的部分代碼如下:
3.3.2 表示層實現(xiàn)
表示層代碼放在controller 目錄下,接口方法名和路由中配置的引用名保持一致。koa-bodyparser 中間件可以將post 請求和表單提交的查詢字符串轉(zhuǎn)換成對象,掛在上下文對象ctx 中的request 請求對象的body 上,方便在自己開發(fā)的中間件或者接口中取值,在入口文件app.js 中引用。首先從ctx.request.body 請求對象中獲取前端傳遞過來的參數(shù),對參數(shù)進行一些必要的校驗(比如是否為空等)和處理,然后調(diào)用業(yè)務(wù)邏輯層的處理接口方法獲取數(shù)據(jù),最后將獲取到的數(shù)據(jù)封裝到上下文對象ctx 的body 中,前端在調(diào)用接口回調(diào)方法中就可以從response 中獲取到返回值。
3.3.3 業(yè)務(wù)邏輯實現(xiàn)
業(yè)務(wù)邏輯處理代碼放在service 目錄下,當接收表示層的用戶指令后,對業(yè)務(wù)進行拆分。以“讀取組件內(nèi)容”功能為例說明,首先調(diào)用數(shù)據(jù)訪問層中獲取所有擴展組件列表的接口,然后根據(jù)返回數(shù)據(jù)中每個組件的存儲方式和文件路徑,分別調(diào)用數(shù)據(jù)訪問層中文件讀取接口和數(shù)據(jù)庫讀取接口,從數(shù)據(jù)庫或服務(wù)器指定文件目錄中讀取組件的定義文件內(nèi)容,將獲取的組件定義文件內(nèi)容和組件列表中的組件信息封裝成JSON 對象格式,最后將封裝后的數(shù)據(jù)返回給表示層。
3.3.4 數(shù)據(jù)訪問實現(xiàn)
數(shù)據(jù)訪問層對數(shù)據(jù)庫和文件的訪問細節(jié)進行了封裝,業(yè)務(wù)邏輯層調(diào)用封裝后的接口對數(shù)據(jù)庫和磁盤文件進行操作。本文將數(shù)據(jù)訪問層分為三個部分:
(1)數(shù)據(jù)庫連接以及創(chuàng)建數(shù)據(jù)庫實例的封裝;
(2)數(shù)據(jù)模型,為數(shù)據(jù)庫表創(chuàng)建相應(yīng)的數(shù)據(jù)模型;
(3)數(shù)據(jù)增刪改查操作api 封裝。
讀取指定目錄下文件內(nèi)容的api 方法代碼如下。
默認情況下,頁面模板、擴展組件和圖片存儲以文件格式存在在服務(wù)器的指定目錄下,按照文件類型保存在不同的目錄下,根據(jù)文件分類又存儲在不同的子目錄中;文件的分類關(guān)系和權(quán)限關(guān)系存儲在數(shù)據(jù)庫中。但是,用戶在發(fā)布組件和頁面模板時可以根據(jù)其需要改變默認的存儲方式。
在實際應(yīng)用場景下,一個電力業(yè)務(wù)系統(tǒng)的頁面總數(shù)一般不超過200 個,同一時段頁面開發(fā)數(shù)一般不超過10 個,而開發(fā)者同一時間同時編輯頁面并保存成模板的可能性更小。另外一方面,資源文件的刪除和保存功能相對于讀取功能是非常少的。基于上述實際應(yīng)用場景,對比SQLite 和其他數(shù)據(jù)庫的優(yōu)缺點,選擇SQLite 數(shù)據(jù)庫存儲數(shù)據(jù)。本系統(tǒng)主要涉及到的數(shù)據(jù)庫表,如表1所示。
表1:系統(tǒng)主要數(shù)據(jù)庫表以及字段描述
其中,擴展組件表和頁面模板表存儲的是文件定義本身的內(nèi)容,因此只有當具體的擴展組件和頁面模板存儲方式為數(shù)據(jù)庫時,才會保存到此表中,默認文件內(nèi)容是以磁盤文件的方式存儲在服務(wù)器指定目錄中。
本文基于NodeJS+Koa2 框架,SQLite 數(shù)據(jù)庫搭建了可視化頁面編輯系統(tǒng)的服務(wù)器端,基于此服務(wù),頁面開發(fā)者可以實現(xiàn)自定義組件和頁面模板的快速分類管理,也可以實現(xiàn)靜態(tài)資源文件的上傳下載。使用者可以根據(jù)實際的需求,靈活選擇不同的文件存儲模式,也可以為了安全考慮,定期備份,同時使用兩種存儲模式。此外,本文所設(shè)計的后臺服務(wù)結(jié)構(gòu),也適用于大多數(shù)小型系統(tǒng),對于其他架構(gòu)的后端服務(wù)架構(gòu)設(shè)計也有一定的參考價值。