-
這一系列的最後一篇寫給一般程式設計師,如果不感興趣可直接看本文最後幾段。
在開始設計程式碼結構之前,先回顧一下之前準備過的事情:我們有負載平衡的WEB伺服器,有主從DB伺服器並可能分片,有緩存,有可擴展的儲存。在組織代碼的各個方面,跟這些準備息息相關,我一二三的列出來分別說,並且每一條都以“前面講到”這個經典句式開頭,為了方便對照。
別急看經典句式,我思維跳躍了,插一段。實際開發中,我們總是會在效能和程式碼優雅性上作折中。對於現今的電腦和語言解釋器,多幾層少幾層物件呼叫、宣告變數為Map還是HashMap這種問題是最後才需要考慮的問題,永遠要考慮系統最慢的部分,從最慢的部分解決。例如看看你用的ORM是不是做了很多你用不到的事情,是不是有重複的資料呼叫。我們做的是web應用開發,不是底層框架API,程式碼易讀易懂是保證品質很重要的一方面,你的程式是為了什麼而設計,有不同的方法……算了,這個話題另起一篇文章來說,扯遠了,想交流可關注我的微博http://t.sina.com.cn/liuzhiyi ,咱繼續…
前面講到,WEB伺服器是要做負載平衡的,圖片伺服器是要分開的。對於這一點,程式碼在處理客戶端狀態時,不要把狀態放到單機上,舉例,不要用檔案session,嗯,常識。如果有可能,最好在一開始就做好用戶單點認證的統一接口,包括跨域如何判斷狀態、靜態頁面如何判斷狀態,需要登錄時的跳轉和返回參數定義,底層給好接口,應用層直接就用(可參考GAE的user服務)。登入方面的設計要考慮行動裝置的特性,例如電腦可以用浮動層窗口,但NOKIA自帶的瀏覽器或UCWEB就無法處理這種表現形式,程式一定既能處理Ajax請求又能直接透過URL來處理請求。圖片伺服器分開,資源檔案最好也佈局到圖片伺服器,也就是WEB伺服器只服務動態程式。雖然開發測試時稍微複雜(因為需要絕對URI才能存取),但將來頁面前端優化上會輕鬆許多,而且你的WEB伺服器IO優化也輕鬆許多。當程式引用資源檔案時,要有一個統一的處理方法,在方法內部可以自動完成很多事情,例如將CSS/js根據組合,拼成一個文件,或者自動在生成的URI後面加上QUERYSTRING,如果將來前端用了快取服務,那麼產生QUERYSTRING是最簡單的刷新服務端快取和客戶端快取的辦法。
前面講到,資料庫會有複製,可能多主多從,可能會分片。我們程式在處理資料的過程中,最好能抽像出來單獨放做一層。拿現在流行的MVC模式來說,就是在M層下方再放一個資料層,這個資料層不是通常所說的JDBC/PDO/ActiveRecord等,而是你自己的存取資料層,僅對外暴露方法,隱藏資料存取細節。這個資料層內部不要怕寫的難看,但一定要提供所有的資料儲存功能,其他任何層次不要看到跟資料庫打交道的字眼。之所以這樣做,是因為在單關係資料庫的情況下,可能會SELECT…JOIN…或直接INSERT…INTO…,可你可能會將一些表格放到key-value資料庫裡存儲,或者分片,這麼做之後原來的語句和方式要全部改變,如果過於分散,則移植時會耗費很大精力,或得到一個很大的Model。在資料層面的設計上,盡量避免JOIN查詢,我們可以多做冗餘,多做緩存,每種資料盡量只需要一次查詢,然後在你的程式裡面進行組合。對於較複雜的資料組合,在即時性要求不高的情況下,可採用非同步處理,使用者在存取時只取處理後的結果。在對於主鍵的處理上,避免使用自增ID,可以用某一規則產生的唯一值當做主鍵,這種主鍵是最簡單的分片分佈策略。即使用自增ID,也最好用一個自增ID產生器,否則從資料庫不小心被寫了一下,那主鍵很容易衝突。
前面講到,咱資料庫前面還有某些快取擋著。別把MySQL的query cache當緩存,應用稍複雜的時候QUERY CACHE反而會變成累贅。快取跟資料庫和業務結合的很緊密,正因為跟業務關係緊密,所以這點沒有放之四海而皆準的方法。但我們還是有一些規則可以參考。規則一:越接近前端,快取的顆粒度越大。例如在WEB最前端快取整個頁面,再往後一層快取部分頁面區域,再往後快取區域內的單一記錄。因為越靠近後端,我們的可操作性越靈活,而且變化最多的前端程式碼也比較方便編寫。在實作中,因為產品需求變化速度非常快,迭代週期越來越短,有時很難將Controller和Model分的那麼清楚,Controller層面處理部分快取必不可免,但要確保如果發生這種情況,Controller所操作的快取一定不要影響其他資料需求方,也就是要確保這個快取資料只有這一個Controller在用。規則二:沒有快取時程式不能出錯。在不考慮緩存失效引發的雪崩效應時,你的程式要有緩存跟沒緩存一個樣,不能像新浪微博一樣,緩存一失效,粉絲微博全空,整個應用都亂套了。在快取必不可少的情況下,給用戶出錯資訊都比給一個讓人誤解的資訊強。規則三,快取更新要確保原子性或稱作線程安全,特別是採用被動緩存的方式時,很可能兩個用戶訪問時導致同一個緩存被更新,通常情況這不是大問題,可緩存失效後重建時很可能是引發連鎖反應的原因之一。規則四:快取也是有成本的。不只是技術成本,還有人工時間成本。如果一個功能使用快取和不使用,在可預見的訪問量情況下區別微小,但使用快取會使複雜度增加,那就不用,我們可以加個TODO標註,在下次迭代的時候加上快取處理。
前面講到,檔案儲存是獨立的,那麼所有的檔案操作都是遠端呼叫。可以在檔案伺服器上提供一個很簡單的RESTful接口,也可以提供xmlrpc或json serveice,WEB伺服器端所產生和處理的文件,全部透過接口通知文件伺服器去處理,WEB伺服器本身不要提供任何檔案儲存。你會發現很多大網站的上傳圖片跟保存文章是分兩步驟完成的,就是基於這個原因。
以上幾條“前面講到”,其實無數人都講過,我也只是結合前幾篇文章用自己的話重複了一遍,真正分析起來精髓很簡單——除了良好的功能邏輯分層,我們還要為資料庫儲存、快取、佇列、檔案服務等程式外層資源呼叫單獨設計接口,你可以把你的程式想像成是運行在Amazon EC2 上並用他的所有web service服務,你的資料庫就是它的SimpleDB,你的佇列就是他的SQS,你的儲存就是他的S3,唯一不同是amazon的介面是遠端調用,你的是內部調用。
將支撐服務介接化,意味著將MySQL更換到PostgreSQL不需要更改業務處理程序,移植團隊甚至不需要跟業務開發團隊過多溝通;意味著業務開發團隊是對介面程式設計而不是對資料庫程式設計;意味著不會因為某個業務開發人員的失誤而拖垮效能。
對程式掃盲不感興趣的直接看這裡——
產品設計完了,程序框架完成了,可能有矛盾在這個節骨眼兒產生了。不斷有產品設計抱怨他的創意沒實現到預期效果,有程式設計師抱怨產品設計不切實際。這種抱怨多緣於產品人員不懂技術,技術人員不理解產品。從廣義上來講,產品包含市場策略、行銷手段、功能設計,產品和技術在爭論時往往把焦點放在功能上,而實際重點是,實現這個功能所消耗的成本跟能這個功能帶來的利益能否換算,能否取其輕重。若可以,爭議解決。若不能,則拋硬幣看運氣。因為一個功能的加強而引發指標井噴,或因專案拖延而導致貽誤戰機的例子比比皆是。激進的決策者註重利益,保守的決策者註重損失,聰明的決策者會考慮這個問題是否真的那麼嚴重。
關係到未來的事都說不準,要不怎麼說創業一半靠運氣呢。不過總有能說的準的事情,那就得靠數據說話。
沒有100%也有99.9%的網站安裝了訪問統計代碼,連我的http://zhiyi.us也不例外,新聞聯播也總說科學決策科學發展的。有了統計,能確定的事情就很多了。例如,可以根據來源-目標轉換率來分析哪一類管道的人均獲取成本低,根據來源-內容訪問猜測用戶跳出率原因,根據用戶點擊行為判斷連結位置是否合理等。將數據以不同方式組合起來,找到內在聯繫,分析內因外因,制定對應策略,減少拍腦門決策。靠數據支撐運作是一件非常專業的事情,雖然不懂深奧的數學模型不會複雜的公式計算,漸漸學會因為A所以B,因為A和B所以C還是相對簡單的。
文章作者:劉志一(轉載請註明出處連結及作者)
文章來源: http://zhiyi.us/