天天看點

[轉]百萬級通路網站前期的技術準備

開了自己域名的部落格,第一篇就得來個重磅一點的才對得起這4美金的域名。作為一個技術從業者十年,逛了十年發現有些知識東一榔頭西一棒槌的得滿世界 看個遍才整理出個頭緒,那咱就系統點的從頭一步一步的說,一個從日幾千通路的小小網站,到日通路一兩百萬的小網站,怎麼才能讓它平滑的度過這個階段,别在 技術上出現先天不足,寫給一些技術人員,也寫給不懂技術的創業者。

對網際網路有了解的人都有自己的想法,有人就把想法付諸實作,做個網站然後開始營運。其實從純網站技術上來說,因為開源模式的發展,現在建一個小網站 已經很簡單也很便宜。當通路量到達一定數量級的時候成本就開始飙升了,問題也開始顯現了。因為帶寬的增加、硬體的擴充、人員的擴張所帶來的成本提高是顯而 易見的,而還有相當大的一部分成本是因為代碼重構、架構重構,甚至底層開發語言更換引起的,最慘的就是資料丢失,辛辛苦苦好幾年,一夜回到創業前。

減少成本就是增加利潤。很多事情,我們在一開始就可以避免,先打好基礎,往後可以省很多精力,少操很多心。

假設你是一個參與創業的技術人員,目前一窮二白,什麼都要自己做,自己出錢,初期幾十萬的資金,做一個應用不是特别複雜的網站,那麼就要注意以下幾點:

一、開發語言

一般來說,技術人員(程式員)創業都是根據自己技術背景選擇自己最熟悉的語言,不過考慮到不可能永遠是您一個人寫程式,這點還得仔細想想。無論用什麼語言,最終代碼品質是看管理,是以我們還是從純語言層面來說實際一點。現在流行的java、php、.net、python、ruby都 有自己的優劣,python和ruby,現在人員還是相對難招一些,性能優化也會費些力氣,.net平台買不起windows server。java、php用的還是最多。對于初期,應用幾乎都是靠前端支撐的網站來說,php的優勢稍大一些,入門簡單、設計模式簡單、寫起來快、 性能足夠等,不過不注重設計模式也是它的劣勢,容易變得松散,隐藏bug稍多、難以維護。java的優勢在于整套管理流程已經有很多成熟工具來輔助,強類 型也能避免一些弱智BUG,大多數JAVA程式員比較注重設計模式,别管實不實際,代碼格式看起來還是不錯的。這也是個劣勢,初學者可能太注重模式而很難 解決實際需求。

前端不隻是html、css這類。整個負責跟使用者互動的部分都是前端,包括處理程式。這類程式還是建議用php,主要原因就是開發迅速、從業人員廣泛。至于後端例如行為分析、銀行接口、異步消息處理等,随便用什麼程式,那個隻能是根據不同業務需求來選擇不同語言了。

二、代碼版本管理

如果開發人員之間的網絡速度差不多,就SVN;比較分散例如跨國,就hg。大多數人還是svn的.

假設選了svn,那麼有幾點考慮。一是采用什麼樹結構。初期可能隻有一條主幹,往後就需要建立分支,例如一條開發分支,一條上線分支,再往後,可能 要每個小組一個分支。建議一開始人少時選擇兩條分支,開發和線上,每個功能本地測試無誤後送出到開發分支,最後統一測試,可以上線時合并到上線分支。如果 喜歡把svn當做移動硬碟用,寫一點就commit一次也無所謂,就是合并的時候頭大一些,這些人可以自己建個分支甚至建立個本地代碼倉庫,随便往自己的 分支送出,測試完畢後再送出到開發分支上。

部署,可以手工部署也可以自動部署。手工部署相對簡單,一般是直接在伺服器上svn update,或者找個新目錄svn checkout,再把web root給ln -s過去。應用越複雜,部署越複雜,沒有什麼統一标準,隻要别再用ftp上傳那種形式就好,一是上傳時檔案引用不一緻錯誤率增加,二是很容易出現開發人員 的版本跟線上版本不一緻,導緻本來想改個錯字結果變成復原的杯具。如果有多台伺服器還是建議自動部署,更換代碼的機器從目前服務池中臨時撤出,更新完畢後 再重新加入。

不管項目多小,養成使用版本管理的好習慣,最起碼還可以當做你的備份,我的 http://zhiyi.us 雖然就是一個wordpress,可還是svn了,隻改動一兩句css那也是勞動成果。

三、伺服器硬體

别羨慕大客戶和有錢人,看看機房散戶區,一台伺服器孤獨的支撐的網站數不清。如果資金稍微充足,建議至少三台的标準配置,分别用作web處理、資料 庫、備份。web伺服器至少要8G記憶體,雙sata raid1,如果經濟稍微寬松,或靜态檔案或圖檔多,則15k sas raid1+0。資料庫至少16G記憶體,15k sas raid 1+0。備份伺服器最好跟資料庫伺服器同等配置。硬體可以自己買品牌的底闆,也就是機箱配主機闆和硬碟盒,CPU記憶體硬碟都自己配,也可以上整套品牌,也可 以相容機。三台機器,市場行情6、7萬也就配齊了。

web伺服器可以既跑程式又當記憶體緩存,資料庫伺服器則隻跑主資料庫(假如是MySQL的話),備份伺服器幹的活就相對多一些,web配置、緩存配置、資料庫配置都要跟前兩台一緻,這樣WEB和資料庫任意一台出問題,把備份伺服器換個ip就切換上去了。備份政策,可以drbd,可以rsync,或者其他的很多很多的開源備份方案可選擇。rsync最簡單,放cron裡自己跑就行。備份和切換,建議多做測試,選最安全最适合業務的,并且盡可能異地備份。

四、機房

三種機房盡量不要選:聯通通路特别慢的電信機房、電信通路特别慢的聯通機房、電信聯通通路特别慢的移動或鐵通機房。那網通機房呢?親,網通聯通N久 以前合并改叫聯通了。多多尋找,實地參觀,多多測試,多方打探,北京、上海、廣州等各個主節點城市,還是有很多優質機房的,找個網絡品質好,管理嚴格的機 房,特别是管理要嚴格,千萬别網站無法通路了,打個電話過去才知道别人維護時把你網線碰掉了,這比DOS都頭疼。自己扯了幾根光纖就稱為機房的,看您抗風 險程度和心理素質了。機房可以說是非常重要,直接關系到網站通路速度,網站通路速度直接關系到使用者體驗,我可以翻牆看風景,但買個網遊vpn才能打開你這 個還不怎麼知名的網站就有難度了。或許您網站的ajax很出色,可是document怎麼也不ready,一些代碼永遠絕緣于使用者。

五、架構

初期架構一般比較簡單,web負載均衡+資料庫主從+緩存+分布式存儲+隊列。大方向上也确實就這幾樣東西,細節上也無數文章都重複過了,按照将來 會有N多WEB,N多主從關系,N多緩存,N多xxx設計就行,基本方案都是現成的,隻是您比其他人厲害之處就在于設計上考慮到緩存失效時的雪崩效應、主 從同步的資料一緻性和時間差、隊列的穩定性和失敗後的重試政策、檔案存儲的效率和備份方式等等意外情況。緩存總有一天會失效,資料庫複制總有一天會斷掉, 隊列總有一天會寫不進去,電源總有一天會燒壞。根據墨菲定律,如果不考慮這些,網站早晚會成為茶幾。

六、伺服器軟體

Linux、nginx、php、mysql,幾乎是标配,我們除了看名字,還得選版本。Linux發行版衆多,隻要沒特殊要求,就選個用的人最多的,社群最活躍的,配置最友善的,軟體包最全最新的,例如debian、ubuntu。 至于RHEL之類的嘛,你用隻能在RHEL上才能運作的軟體麼?剩下的nginx、php、mysql、activemq、其他的等等,除非你改過這些軟 件或你的程式真的不相容新版本,否則盡量版本越新越好,版本新,意味着新特性增多、BUG減少、性能增加。總有些道聽途說的人跟你說老的版本穩定。所謂穩 定,是相對于特殊業務來說的,而就一個php寫的網站,大多數人都沒改過任何伺服器軟體源代碼,絕大多數情況是能平穩的更新到新版本的。類似于jdk5到 jdk6,python2到python3這類變動比較大的更新還是比較少見的。看看ChangeLog,看看更新說明,結合自己情況評估一下,越早更新 越好,别人家都用php6寫程式了這邊還php4的逛遊呢。優秀的開源程式更新還是很負責任的,看好文檔,别怕。

以上這六點準備完畢,現在我們有了運作環境,有了基本架構骨架,有了備份和切換方案,應該開始着手設計開發方面的事情了。開發方面的事情無數,下一篇會先說一些重點。

原文位址

七、資料庫

幾乎所有操作最後都要落到資料庫身上,它又最難擴充(存儲也挺難)。對于mysql,什麼樣的表用myisam,什麼樣的表用innodb,在開發 之前要确定。複制政策、分片政策,也要确定。表引擎方面,一般,更新不多、不需要事務的表可以用myisam,需要行鎖定、事務支援的,用innodb。 myisam的鎖表不一定是性能低下的根源,innodb也不一定全是行鎖,具體細節要多看相關的文檔,熟悉了引擎特性才能用的更好。現代WEB應用越來 越複雜了,我們設計表結構時常常設計很多備援,雖然不符合傳統範式,但為了速度考慮還是值得的,要求高的情況下甚至要杜絕聯合查詢。程式設計時得多注意資料一 緻性。

複制政策方面,多主多從結構也最好一開始就設計好,代碼直接按照多主多從來編寫,用一些小技巧來避免複制延時問題,并且還要解決多資料庫資料是否一緻,可以自己寫或者找現成的運維工具。

分片政策。總會有那麼幾個表資料量超大,這時分片必不可免。分片有很多政策,從簡單的分區到根據熱度自動調整,依照具體業務選擇一個适合自己的。避免自增ID作為主鍵,不利于分片。

用存儲過程是比較難擴充的,這種情形多發生于傳統C/S,特别是OA系統轉換過來的開發人員。低成本網站不是一兩台小型機跑一個資料庫處理所有業務的模式,是機海作戰。友善水準擴充比那點預分析時間和網絡傳輸流量要重要的多的多。

NoSQL。這隻是一個概念。實際應用中,網站有着越來越多的密集寫操作、上億的簡單關系資料讀取、熱備等,這都不是傳統關系資料庫所擅長的,于是 就産生了很多非關系型資料庫,比如Redis/TC&TT/MongoDB/Memcachedb等,在測試中,這些幾乎都達到了每秒至少一萬次 的寫操作,記憶體型的甚至5萬以上。例如MongoDB,幾句配置就可以組建一個複制+自動分片+failover的環境,文檔化的存儲也簡化了傳統設計庫 結構再開發的模式。很多業務是可以用這類資料庫來替代mysql的。

八、緩存。

資料庫很脆弱,一定要有緩存在前面擋着,其實我們優化速度,幾乎就是優化緩存,能用緩存的地方,就不要再跑到後端資料庫那折騰。緩存有持久化緩存、 記憶體緩存,生成靜态頁面是最容易了解的持久化緩存了,還有很多比如varnish的分塊緩存、前面提到的memcachedb等,記憶體緩 存,memcached首當其沖。緩存更新可用被動更新和主動更新。被動更新的好處是設計簡單,緩存空了就自動去資料庫取資料再把緩存填上,但容易引發雪 崩效應,一旦緩存大面積失效,資料庫的壓力直線上升很可能挂掉。主動緩存可避免這點但是可能引發程式取不到資料的問題。這兩者之間如何配合,程式設計要多 動腦筋。

九、隊列。

使用者一個操作很可能引發一系列資源和功能的調動,這些調動如果同時發生,壓力無法控制,使用者體驗也不好,可以把這樣一些操作放入隊列,由另幾個子產品 去異步執行,例如發送郵件,發送手機短信。開源隊列伺服器很多,性能要求不高用資料庫當做隊列也可以,隻要保證程式讀寫隊列的接口不變,底層隊列服務可随 時更換就可以,類似Zend Framework裡的Zend_Queue類,java.util.Queue接口等。

十、檔案存儲。

除了結構化資料,我們經常要存放其他的資料,像圖檔之類的。這類資料數量繁多、通路量大。典型的就是圖檔,從使用者頭像到使用者上傳的照片,還要生成不 同的縮略圖尺寸。存儲的分布幾乎跟資料庫擴充一樣艱難。不使用專業存儲的情況下,基本都是靠自己的NAS。這就涉及到結構。拿圖檔存儲舉例,圖檔是非常容 易産生熱點的,有些圖檔上傳後就不再有人看,有些可能每天被通路數十萬次,而且大量小檔案的異步備份也很耗費時間。

為了将來圖檔走cdn做準備,一開始最好就将圖檔的域名分開,且不用主域名。很多網站都将cookie設定到了.domain.ltd,如果圖檔也在這個域名下,很可能因為cookie而造成緩存失效,并且占多餘流量,還可能因為浏覽器并發線程限制造成通路緩慢。

如果用普通的檔案系統存儲圖檔,有一個簡單的方法。計算檔案的hash值,比如md5,以結果第一位作為第一級目錄,這樣第一級有16個目錄。從0 到F,可以把這個字母作為域名,0.yourimg.com到f.yourimg.com(用戶端dns壓力會增大),還可以擴充到最多16個NAS叢集 上。第二級可用年月例如,201011,第三級用日,第四級可選,根據上傳量,比如am/pm,甚至小時。最終的目錄結構可能會是 e/201008/25/am/e43ae391c839d82801920cf.jpg。rsync備份時可以用腳本隻同步某年某日某時的檔案,避免計 算大量檔案帶來的開銷。當然最好是能用專門的分布式檔案系統或更專業點的存儲解決方案。

下面,我們要談談代碼了。

這一系列的最後一篇寫給普通程式設計人員,如果不感興趣可直接看本文最後幾段。開始設計代碼結構之前,先回顧一下之前準備過的事情:我們有負載均衡的 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/internet/thinking-twice-before-building-your-site-one.html

http://zhiyi.us/internet/thinking-twice-before-building-your-site-two.html

http://zhiyi.us/internet/thinking-twice-before-building-your-site-final.html