項目當時(shí)就我們5個(gè)人在做,可以說(shuō)這5個(gè)人都是百里挑一的人選,但我們需要面對以下挑戰:
1、雖然都是搞J2EE的老手,都沒(méi)有接觸過(guò)Push Mail這樣的國外項目,所以對業(yè)務(wù)的精髓不能完全吃透,導致理解需求比較吃力。
2、在3個(gè)月內完成演示版本,證明業(yè)務(wù)可行性;9個(gè)月內構建完全產(chǎn)品化的系統。
3、系統需要支持大并發(fā),海量數據,系統必須達到很高性能指標。
4、提供7X24及時(shí)服務(wù),在第一時(shí)間解決任何技術(shù)問(wèn)題。(有時(shí)差,客戶(hù)在睡覺(jué)我們在工作,客戶(hù)在工作,我們不能睡覺(jué)還在工作。)
5、對J2EE以外的技術(shù)只有1-2人了解。比如:分布式計算、應用服務(wù)器,數據庫集群技術(shù) 和 大型系統的數據、文件 存儲方案。
但是再我們所有成員的不斷努力下,這些困難都被我們一一解決。
項目里面牽涉打了J2EE 領(lǐng)域多項技術(shù),也算是一個(gè)難點(diǎn)吧,因為下列技術(shù)跨度都比較大,例如:
1、Servlet (協(xié)議請求、回送)
2、XML (協(xié)議解析)
3、JavaMail (郵件收發(fā))
4、EJB (分布式計算)
5、ibatis(數據庫操作)
6、JMS/MDB (采用分布架構中的內部通訊方式)
7、多線(xiàn)程和線(xiàn)程池 (多任務(wù)提取郵件)
8、還有很多 J2EE規范以外的技術(shù), 例如:httpclient 網(wǎng)頁(yè)抓取、html 解析、Office附件處理、圖片壓縮 、加密/解密 等等。
這個(gè) Push Mail 項目留給我最深的影響就是給我們團隊來(lái)了豐厚的利潤,客戶(hù)他是一個(gè)美籍印度佬,就是我們常說(shuō)的那種有錢(qián)人,做事爽快、干脆,也是全球移動(dòng)領(lǐng)域具有影響的專(zhuān)家,因此他要求我們提供的服務(wù)必須是具有一個(gè)國際化專(zhuān)業(yè)水準的團隊(International Corporate)。所謂專(zhuān)業(yè)化的主要需要體現在 1執行規范、2執行效率、3執行細節 這3個(gè)重要的環(huán)節上。
在項目實(shí)施過(guò)程中除了搞J2EE 的開(kāi)發(fā)者, TA、QA 、SA、DBA 一個(gè)也不能少。
1、TA 小組根據制定的項目執行規范監管我們每個(gè)開(kāi)發(fā)成員 在每個(gè)stage和里程碑的執行力,從需求分析的文檔編寫(xiě)規范一直到最后的項目交付,他們才算結束使命。
2、QA 測試團隊在我們需求和概要設計 就開(kāi)始對jboss和mysql其他幾項技術(shù)學(xué)習,其中測試人員與我們一同參與需求分析,這樣將來(lái)他們才知道該如何配合我們做各種測試,進(jìn)行深入的測試,而不是我們在引導他們在測試,他們100%能知曉業(yè)務(wù) 并且制定出不同的測試計劃。并不是依靠我們自己測試或者我們在指導他們測試,那樣運動(dòng)員和裁判員都是同一人,那樣肯定出問(wèn)題。
3、SA 的壓力最大 需要對系統整體的架構進(jìn)行設計這個(gè)都是分內的事情,更重要的 在真實(shí)生產(chǎn)的環(huán)境遇到致命性錯誤,可以回退或者拿出現成的備份方案,把問(wèn)題在短時(shí)間內化解。
4、DBA 需要從高往低的系統架構層次進(jìn)行對數據庫設計與整體規劃,必須根據客戶(hù)需求設計出具有遠景的 方案,不是一成不變,更不是所謂“一步到位”的規劃設計,是根據預計的數據增長(cháng) 進(jìn)行實(shí)施規劃的不同方案。
5、開(kāi)發(fā)者們需要對每個(gè)業(yè)務(wù)模塊都要詳細了解。因為我們需要降低風(fēng)險 開(kāi)發(fā)者們如果出現有人家里有事或者生病 任何一個(gè)成員都可以替代,不會(huì )出現我今天不來(lái)上班,導致項目進(jìn)度拖慢一天的現象。
在產(chǎn)品在設計開(kāi)發(fā)到發(fā)布的過(guò)程中,我們分為四個(gè)階段 prototype、demo、stage、producting。
prototype 是一個(gè)原型,主要完成核心的部分,完成核心的功能和業(yè)務(wù)邏輯,開(kāi)發(fā)團隊內部評估使用。
demo 階段是將產(chǎn)品的主要部分演示給客戶(hù)看,讓客戶(hù)確認主體的方向。
stage 根據項目計劃分為多個(gè)不同的stage版本,每個(gè)stage階段的版本都會(huì )在stage服務(wù)器上由我們 測試人員先測試5-7天,再發(fā)布到真實(shí)的生產(chǎn)環(huán)境中??蛻?hù)對這樣的流程要求的非常的嚴格。因此 stage 服務(wù)器的配置和數量與 producting 環(huán)境是100%相同的,沒(méi)有他們的郵件確認我們是不能擅自發(fā)布到 producting 環(huán)境中的,如果 producting 環(huán)境出現問(wèn)題,必須在短時(shí)間內能回歸到上一個(gè)穩定版本的狀態(tài) 。 后期會(huì )在 stage 和 producting 服務(wù)器上輪回 ,fix –> testing —> release。
我們當時(shí)使用SVN和Trac,值得一提的是Trac這個(gè)東西,客戶(hù)有任何需求變更 通過(guò) Trac 系統 TA,QA,DBA,SA 統統都會(huì )知道,并且知道我們會(huì )在下個(gè)版本 什么時(shí)候 會(huì ) 發(fā)布在stage 服務(wù)器上,我們也能在第一時(shí)間知道他們對 當前版本的 性能情況,客戶(hù)也能看見(jiàn)但似乎他們并不在意這個(gè)結果,因為他們需要知道我們當前的執行狀態(tài)是否符合計劃,其實(shí)TA的成員比他們更加關(guān)注我們的項目進(jìn)度,呵呵。因此,所有人的開(kāi)發(fā)進(jìn)度執行情況都在trac上進(jìn)行展現??蛻?hù)也可以看見(jiàn),也可以回復,所有信息所有人同步。
呵呵,另外那三十幾臺需要發(fā)布應用程序的機器,分別發(fā)布不同的應用程序或者不同的版本不可能完全依靠人工完成,我們需要一個(gè)半自動(dòng)化的工具幫助我們完成,所以我們選擇了hudson和自己編寫(xiě)的liunx腳本。這樣可以提供效率,并且大大減少了發(fā)布時(shí)出錯的機率。
先寫(xiě)這么多,有什么忘記的地方我回頭補上,下一篇將開(kāi)始 講述 純 技術(shù)方面的那點(diǎn)事兒了。
通過(guò)這個(gè)項目讓我們充分的認識到一個(gè)用戶(hù)的系統 例如:?jiǎn)斡脩?hù)的 OutLook 和 一個(gè)百萬(wàn)級的系統的是截然不同的,系統中僅有100條數據和100w條數據的系統更是截然不同的,從100w條數據上升到上億條數據那樣的系統更會(huì )讓人感受到無(wú)論從代碼結構、數據庫設計還是從系統架構都是完全不同的設計。
客戶(hù)端環(huán)境
MKT SDK
C 語(yǔ)言開(kāi)發(fā)環(huán)境
服務(wù)器端環(huán)境
Apache2 Web服務(wù)器
mod_proxy Web負載均衡模塊
HAProxy 前端、數據庫負載分載(后期使用)
Java EE1.5 語(yǔ)言開(kāi)發(fā)環(huán)境
Jboss 4.0.5 GA 應用服務(wù)器
Jboss MQ JMS服務(wù)器
MySQL 5.1.4 數據庫服務(wù)器
EJB 3.0 Java 開(kāi)發(fā)API 工具
iBATIS Java 開(kāi)發(fā)API 工具
OsCache 緩存組件
Gluster DFS 分布式文件系統
Nagios 系統監控、報警工具
Qmail 1臺發(fā)送郵件服務(wù)器
壓力測試工具
Apache Jemter
Apache AB
當前運行狀態(tài)
每月大約有4000多新注冊用戶(hù)
當前注冊大約用戶(hù)數量200-300w
每天大約有90-110w用戶(hù)登錄
每天大約接收500-700W 封郵件
每天大約接收附件3-5G,超過(guò)5M以上的附件將不接受
每天數據庫增加 2W條記錄
每天數據庫接受 700W 次請求
每秒并發(fā)最大18000個(gè)請求
整體架構
概覽
總體上概括,系統分為 1客戶(hù)端,2服務(wù)器端,3SP Mail Server 端,4 CRM 端 下面將講述整個(gè)系統的概況。
后端發(fā)送郵件實(shí)現過(guò)程
1.手機客戶(hù)端通過(guò)http網(wǎng)絡(luò )協(xié)議發(fā)送XML數據到服務(wù)器端,
2.服務(wù)器端接收到xml數據進(jìn)行解析,服務(wù)器端傳輸層的web請求模塊將xml 協(xié)議解析,并且重新包裝成pojo 對象,
3.中間有一個(gè)業(yè)務(wù)模塊分發(fā)器將 獲得pojo對象交給不同的業(yè)務(wù)模塊進(jìn)行處理,如:DAL層、消息中間件,
4.將處理完成的業(yè)務(wù)通過(guò)業(yè)務(wù)模塊分發(fā)器包裝成xml報文回送給客戶(hù)端。
整個(gè)業(yè)務(wù)處理組成部分如圖所示:
內部業(yè)務(wù)邏輯
1.定時(shí)器從數據庫裝載帳戶(hù),采用多線(xiàn)程實(shí)現一個(gè)線(xiàn)程池,我們稱(chēng)這個(gè)玩意兒叫做任務(wù)工廠(chǎng),
2.定時(shí)向JMS服務(wù)器中不同的消息隊列發(fā)送消息 ,消息接收端收到消息后驅動(dòng)業(yè)務(wù)模塊去 SP Mail Server 收取郵件,
3.收郵件模塊 上SP Mail Server 獲取郵件列表頭信息,并且和數據庫中已存在的郵件列表頭信息進(jìn)行新郵件比對,
4.收取新郵件并且將郵件信息保存到數據庫或者緩存,當用戶(hù)需要讀取的時(shí)候將從系統的數據庫或者緩存中讀取,
5.保存完畢后,將通過(guò)IP Push 或者 SMS Push 技術(shù) 通知用戶(hù)上服務(wù)器獲得新郵件。
后端內部架構
Web層
用戶(hù)的所有請求都是從web層進(jìn)入,最上層是apache將請求分載到每臺AP 服務(wù)器 Jboss的web容器上,web容器請求處理用戶(hù)提交的各種xml請求協(xié)議,并且回送處理結果xml協(xié)議給客戶(hù)端,apache經(jīng)過(guò)幾次優(yōu)化并且開(kāi)了20個(gè)進(jìn)程處理用戶(hù)所有請求分發(fā),另一臺apache standby 通過(guò) heartbeat做備份,如果apache server1失效 將會(huì )自動(dòng)轉移到 apach server2上去。在項目后期我們采用HaProxy來(lái)替代了Apache的轉發(fā)工作,也許2者進(jìn)行優(yōu)化后的功效相差不了多少,但是我們認為HaProxy安裝、移植、部署 比Apache更加方便,最高可以處理2.7W的并發(fā)請求處理,并且可以采用leastconn 負載均衡算法,將當前連接請求數量最小的服務(wù)器進(jìn)行命中。
任務(wù)調度
檢測用戶(hù)的新郵件功能依賴(lài)系統中的任務(wù)調度模塊,我們管這個(gè)模塊叫做任務(wù)工廠(chǎng),他先從數據庫中裝載賬戶(hù),再放入緩存中,每3分鐘裝載所有賬戶(hù)一次,向JMS服務(wù)器發(fā)送消息請求新郵件檢查。
從緩存中讀取是為了降低數據庫的壓力, 因為這樣的操作太頻繁了而且數據量越來(lái)越大,會(huì )導致數據的壓力增大。任務(wù)工廠(chǎng)是系統中非常重要的模組,如果任務(wù)工廠(chǎng)停止工作將不能檢測到所有用戶(hù)的新郵件, 整個(gè)Push Mail系統將會(huì )癱瘓,所以必須能支持壓力分載和失效轉發(fā)的功能。
BTW:任務(wù)工廠(chǎng)模塊最開(kāi)始在demo的時(shí)候采用Quartz 放入Spring微容器中進(jìn)行集群,因為不能從緩存中直接讀取,將來(lái)系統賬戶(hù)會(huì )越來(lái)越多,導致數據庫那邊的壓力越來(lái)越大,所以后期我們用 線(xiàn)程池+oscache集群技術(shù) 自己開(kāi)發(fā)了一套類(lèi)似 Quartz+spring集群的方案。另外,這個(gè)項目發(fā)生在沒(méi)有Memcached的年代,如果當時(shí)使用Memcached也許設計上會(huì )更加完美些。
分布式應用
國外用戶(hù)使用郵箱賬號多數都是以 Gmail、AOL、Hotmail、Yahoo 4大郵箱為主,所以我們將處理郵件具體操作的機器分為4組,每組機器處理具體不同的郵箱賬號的郵件存取,將整個(gè)業(yè)務(wù)中負荷最大的部分進(jìn)行分散,獨立計算, 每組中的服務(wù)器都支持 失效轉發(fā)和壓力分載。
EJB消息驅動(dòng)Bean /JMS 消息系統
系統多線(xiàn)程的定時(shí)器,將系統中所有用戶(hù)賬號裝載后由多個(gè)JMS發(fā)送端發(fā)送Queue消息到不同的消息隊列,例如:Gmail消息隊列、AOL消息隊列、Hotmail消息隊列、Yahoo消息隊列,然后接收端將接收不同的消息隊列發(fā)送過(guò)來(lái)的消息,通常我們每組消息隊列使用一個(gè)發(fā)送端對應多個(gè)接收端,每組接收端,例如:接收到一批Yahoo賬號的JMS消息,將會(huì )由多個(gè)接收端,例如:接收到一批Yahoo賬號的JMS消息,接收端將通過(guò)消息分發(fā)給不同的計算機組,JMS服務(wù)器端采用集群技術(shù),2臺JMS服務(wù)器上建有4個(gè)消息隊列,不僅可以進(jìn)行壓力分載,還可以進(jìn)行失效轉發(fā)。將4大郵箱分為4個(gè)業(yè)務(wù)群組,每分組各4臺消息接收端,一共16臺,加上JMS MOM 消息中間件服務(wù)器一共18臺,在得到上層模組傳送過(guò)來(lái)的指令后,16臺專(zhuān)門(mén)進(jìn)行用戶(hù)郵件解析、發(fā)送的操作。
數據庫
8臺 16 GB內存 、4個(gè)CPU 的機器作為 我們使用的MySQL數據庫,每臺MySQL數據庫都安裝了XtraDB MySQL插件,主要提高M(jìn)ySQL innodb引擎的讀寫(xiě)性能,對于MySQL的整體性能我們主要還是通過(guò)調整MySQL的內部參數來(lái)進(jìn)行優(yōu)化。MySQL原本算使用 NDB引擎來(lái)做數據同步和單點(diǎn)失效的,經(jīng)過(guò)實(shí)際情況我們反復比對還是使用傳統MySQL數據同步來(lái)完成我們預想的效果,因為MySQL的集群在我們當時(shí)還不算很成熟,而且這玩兒比較消耗內存。
分片(sharding)
每個(gè)用戶(hù)的請求都包含一個(gè)用戶(hù)的ID,我們將判斷不同的用戶(hù)ID到不同的數據上進(jìn)行操作,當然我們也不是每張表都做拆分,由于用戶(hù)越來(lái)越多我們只把郵件相關(guān)的表進(jìn)行拆分,例如:郵件列表、郵件內容、郵件附件表 3張表近拆分。每張表中都有用戶(hù)ID這個(gè)字段,DAL層根據用戶(hù) ID 進(jìn)行判斷 數據的路由規則到具體對應范圍的數據庫進(jìn)行存取數據,比如ID在1-10000之間的用戶(hù)對應到數據庫1, ID在10001-20000范圍對應到數據庫2,以此類(lèi)推,但是不能在不停機的狀態(tài)下動(dòng)態(tài)添加數據庫服務(wù)器節點(diǎn),后來(lái)我們想了一個(gè)辦法,添加節點(diǎn)的時(shí)候通過(guò)JMS消息通知每個(gè)機器上的DAL層重新讀取數據路由配置一次,系統中的數據需要和系統中的CRM系統相結合,后期我們又建立了一個(gè)全局數據庫(global),這個(gè)全局數據庫只作為數據索引,為了將所有數據同步,操作所有數據庫的記錄通過(guò)JMS消息定時(shí)向全局數據庫(global)進(jìn)行批量操作,如果每筆執行同步操作,將會(huì )降低系統性能。
分布式文件系統(DFS)
用戶(hù)郵件的所以郵件附件都保存在 分布文件系統 前期我們采用Gluster,后期我們采用了MooseFs搭建 使用分布式文件系統是為了能滿(mǎn)足我們2點(diǎn)需求 1、由于系統用戶(hù)不斷的增多,導致我們需要保存的郵件附件不斷增長(cháng),我們需要可以做到當磁盤(pán)容量不足時(shí)在不停機的情況下無(wú)限的添加存儲的容量,并且操作方便。2、對于保存的數據可以實(shí)時(shí)同步,防止單點(diǎn)失效,數據丟失。
JavaMail
我們將Sun JavaMail 的源代碼進(jìn)行改寫(xiě),提高收郵件的效率,這樣我們在代碼中向POP3郵件服務(wù)器發(fā)送一個(gè)命令,立刻在瞬間可以獲得上千份郵件的MessageID的頭信息??蓞⒁?jiàn) 《修改源碼提高JavaMail比較新郵件效率》一文中提到的具體技術(shù)。發(fā)送郵件采用的是Apache 的 Commons email來(lái)簡(jiǎn)化發(fā)送郵件的操作。
經(jīng)驗與教訓
1.HAProxy的擴展使用,不僅僅可以使用在Web方面,還可以使用到其他應用層,進(jìn)行壓力分鐘, 例如,可以應用到 數據MySQL??梢詤⒖歼@篇文章,http://www.alexwilliams.ca/blog/2009/08/10/using-haproxy-for-mysql-failover-and-redundancy
2.JVM的優(yōu)化是任何大型J2EE項目中必不可少的話(huà)題,當然并不是你把JVM的內存使用空間設置的越大越好,SUN的官方網(wǎng)站已經(jīng)指出,不同的操作系統,不同的應用,建議你的JVM內存分配大小是不同的。另外,對與不同的JVM也有不同的參數配置,通常的JVM配置參數可以參見(jiàn)這里,http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
3.Linux系統上的swap 空間,4G和4G內存 以?xún)鹊臋C器還是非常有必要使用swap空間的,有一次有一個(gè)應用服務(wù)器系統down機,花了很久的時(shí)間才發(fā)現,居然是忘記建立Swap空間,多么簡(jiǎn)單的問(wèn)題啊,可是把我們折磨了3個(gè)星期,3個(gè)星期里被老板和客戶(hù)罵的慘不忍睹,血的教訓,讓我們再也不會(huì )忘記小內存機器建立swap空間了。
4.Jboss MQ的抱怨,首先聲明我不是在給Apache 的JMS做廣告,因為JbossMQ實(shí)在是不好用,而且性能也不咋地,我看見(jiàn)有一篇文章說(shuō)到JbossMQ的性能在同類(lèi)產(chǎn)品中是最好的,不知道結果是怎么來(lái)的,還是建議大家還是使用Apache的JMS,文檔全面啊,不懂就可以找到很全面的資料,jboss MQ 資料的支持不多。
5.MySQL的優(yōu)化,MySQL的一些參數調優(yōu)就不說(shuō)了,但使用MySQL 插件以后給我們帶來(lái)了不少性能上的提升,特別是在數據查詢(xún)上的瓶頸可以得到不少緩解。
6.MySQL集群,這玩意兒是個(gè)不錯的解決方案,可是沒(méi)有足夠的內存是玩不起的,因為MySQL的集群因為完全依賴(lài)內存,另外對表的字段也有特別的限制,用起來(lái)有點(diǎn)不太自然,不支持所有大字段的類(lèi)型。如果是老系統遷移過(guò)來(lái),估計要折騰一段時(shí)間才能合用。
7.JMS 消息的使用,不需要放入數據庫,將消息內存當中,并且對JVM運行參數進(jìn)行優(yōu)化 ,對與客戶(hù)端的代碼也需要進(jìn)行優(yōu)化,關(guān)閉消息id (setDisableMessageID),減少消息的大小并且 省去了創(chuàng )建唯一ID的時(shí)間。關(guān)閉消息的時(shí)間戳。如果不需要時(shí)間戳,用MessageProducer的setDisableMessageTimeStamp()方法將其關(guān)閉。避免使用AUTO_ACKNOWLEDGE。 AUTO_ACKNOWLEDGE 使得每收到一個(gè)消息就要向服務(wù)器發(fā)送一個(gè)通知--這樣增加的網(wǎng)絡(luò )傳輸的負擔。如果可能,盡量使用 DUPS_OK_ACKNOWLEDGE或者CLIENT_ACKNOWLEDGE?;蛘呤褂檬聞?wù)性會(huì )話(huà),將通知在提交時(shí)批量完成。
聯(lián)系客服