前情回顧
上一期《serverless往事:從零搭建一個web應(yīng)用》被要求整改了。整改完與這一期同時發(fā)布饭宾。新搭建的互聯(lián)網(wǎng)Web應(yīng)用改名為E-Pub役电,HR母系統(tǒng)就叫做IVF。
這一期里我將E-pub前后端做了分離庶柿,并將從前端框架村怪、通信、緩存浮庐、ORM逐層介紹新的技術(shù)棧实愚。同時也會有一些小小的吐槽。
前端框架之Nuxt.Js
上一期提到了VUE的服務(wù)端渲染(SSR)框架Nuxt兔辅。網(wǎng)上討論SSR的時候往往只專注于首頁渲染和SEO腊敲,我個人覺得后端渲染的最大優(yōu)點是配置簡單,易學易用维苔。這可能也是我們廠一直以來偏好后端渲染的某種原因吧碰辅,從后端HTML拼接,到j(luò)sp介时,到IVF使用的thymeleaf没宾,起步階段并不需要太多學習成本。(我的Nuxt并非繼承于IVF沸柔,因為E-pub只是支線業(yè)務(wù)循衰,總監(jiān)們可能并不知道我的技術(shù)棧)但是起步簡單并不見得坑少,IVF就懶得噴了褐澎,我們的Nuxt也是一個深坑会钝,其中讓我最頭疼的問題就是部署。lambda的部署時有一個磁盤限制:Package必須同時滿足壓縮狀態(tài)下小于50M和解壓后小于250M這兩個條件。而Nuxt的Package天生巨大迁酸,這讓我一度想轉(zhuǎn)到SPA(single page web application)模式先鱼。當然SPA也有自己的坑,后期實踐也發(fā)現(xiàn)了一些難題:比如前端日志搜集奸鬓、History Mode配置焙畔、首頁渲染、webpack的學習成本等等串远。
所幸的是Nuxt2適時release了宏多,體量大幅減少,然后組里的一個小朋友分離了前后端澡罚,減少了依賴绷落,將package的體積限制在了100余M,總算暫時渡過了難關(guān)始苇。
通信之Graphql
那時Graphql v.s. Rest的話題已經(jīng)大火砌烁,各種公眾號狂推Graphql。我在仔細閱讀過Graphql開發(fā)文檔后立馬決定試水新技術(shù)催式。這時公司里又是另一番景象函喉,鮮有人聽聞Graphql,所以我還特地做了一個廠內(nèi)的技術(shù)分享《Graphql Trial》荣月,可惜并沒有一個領(lǐng)導(dǎo)來聽管呵。Graphql最吸引我的地方是代碼即文檔,主要還是因為懶吧哺窄;IVF(對捐下,就是我們的母系統(tǒng))中就碰到過這種事,各種胡亂定義萌业、是似而非的rest api坷襟,根本沒人維護。我當時就一個光桿司令生年,與其提出一些不切實際的document要求婴程,搭建一個沒人維護的swagger服務(wù)器,還不如一開始就放棄Restful抱婉。除此之外Graphql在復(fù)雜查詢档叔、Mock、類型檢查等等操作上與Rest相比有壓倒性優(yōu)勢蒸绩。后來我甚至將Graphql服務(wù)單獨作為各個微服務(wù)的收集器用于前端調(diào)用衙四,這是后話了。以下是2018年Javascirpt數(shù)據(jù)層的主流框架患亿,以今年的趨勢传蹈,不出意外2019年graphql就應(yīng)該登頂了。
Cache之 DataLoader
我第一篇博客介紹的就是DataLoader。DataLoader的主要功能有兩點:
- 內(nèi)存級緩存
- 批處理
我當時引入DataLoader的主要目的是減少Dynamodb的資費卡睦,因為Dynamodb有單位讀寫限制宴胧。后來發(fā)現(xiàn)Cache層本身就意義重大:
- 解決Graphql N+1 Problem
- 減少DB讀寫
- 加速前端數(shù)據(jù)讀取
- 臨時存儲preview數(shù)據(jù)
但緩存也帶來一些不可避免的負面因素漱抓,比如臟數(shù)據(jù)表锻。因此在編寫緩存策略時要設(shè)置expiry time和max size,但即便如此也不可能完全杜絕負面影響乞娄。很多時候架構(gòu)就是一種權(quán)衡瞬逊,兩害相權(quán)。
ORM之Dynamoose
Dynamodb的操作也是一堆坑仪或。AWS提供了一套原生的Javascript SDK确镊,大體寫法如下,及其難用范删!每次都要指定table蕾域、key,然后再通過params傳值到旦。此外旨巷,最大的困擾是:javascript和Dynamodb都是非強類型的,你會有一種特別的不安全感添忘,因為任何類型采呐、任何域都能往DB里塞。表結(jié)構(gòu)及其混亂搁骑,不具備很強的工程實踐性斧吐。
var params = {
TableName: 'Table',
IndexName: 'Index',
KeyConditionExpression: 'HashKey = :hkey and RangeKey > :rkey',
ExpressionAttributeValues: {
':hkey': 'key',
':rkey': 2015
}
};
var documentClient = new AWS.DynamoDB.DocumentClient();
documentClient.get(params, function(err, data) {
if (err) console.log(err);
else console.log(data);
});
被惡心一段時間后,我把es6換成了Typescript仲器,并開始使用Dynamoose代替原生的SDK煤率。Dynamoosejs引入了schema的概念(受Mongoose啟發(fā)),與table列一一對應(yīng)乏冀。工程中涕侈,可以很直觀地見識到表結(jié)構(gòu)。
const schema = {
hkey: {
hashKey: true,
type: String,
},
rkey: {
rangeKey: true,
type: Number,
},
// Other columns...
status: String,
}
dynamoose.model(Table_Name, schema).get(key);// return Promise
架構(gòu)改進
加入新的技術(shù)棧后我調(diào)整了一下架構(gòu)煤辨,IVF依舊通過輪詢Rest Api調(diào)用E-pub后端裳涛,E-pub前端nuxt則通過單點/grapql
訪問后端;后端則對互聯(lián)網(wǎng)屏蔽所有/api
众辨。
說到這里我吐槽一下端三,很多人都有一種烏托邦的幻想,特別崇拜頂層設(shè)計鹃彻,以為神祗畫一個圈就可以天下大治郊闯。我不相信這些傳說,我更愿意相信由下而上的自發(fā)改良。架構(gòu)設(shè)計中也是這樣团赁,并沒有神祗架構(gòu)通配一切育拨。反倒是在人力、設(shè)備欢摄、管理熬丧、技術(shù)都不適配的情況下強推過于復(fù)雜的架構(gòu),更容易帶來毀滅性的結(jié)果怀挠。下面是書上看來的一些架構(gòu)設(shè)計原則:
- 簡單優(yōu)于復(fù)雜
- 合適優(yōu)于時髦
- 迭代演化優(yōu)于一步到位析蝴。
把握好這些尺度,架構(gòu)設(shè)計才能真正幫助解耦軟件開發(fā)中的高復(fù)雜度绿淋。
小結(jié)
這一期回憶了我在工程起步后對架構(gòu)的一些調(diào)整闷畸。層級很簡單,但是新架構(gòu)運行的還算良好吞滞。很快這一套基礎(chǔ)框架就在組里被大力推廣佑菩,PC端、mobile端裁赠、line端同步展開殿漠。我一個人的項目迅速擴展到了十幾人。
越來越多的新功能被要求集成到serverless里组贺,老的架構(gòu)似乎又開始面臨新的問題凸舵,這回我們又該如何變化呢?To be continue....