文章來自 在外企和互聯網碰撞的猴子 的微信公共帳號。是咱們跨境電商項目的架構師寫的。咱們項目的每一個點讀提到了,方便記錄查找,我轉載一下。php
針對如火如荼的跨境電商行業,催生了提供一個SaaS電商平臺爲一些傳統企業和機構開展跨境電商業務的市場機會,因此咱們有機會來作這件事情。今天重點討論銷售和運營系統,不涉及跨境貿易價值鏈中的採購、通關、購匯結匯以及支撐電商業務的CRM,OMS,ERP等系統。前端
團隊成員-最初由3名具備比較豐富企業級電商平臺開發經驗(>6年)的資深工程師組成,通過1年發展,團隊規模成長到30+工程師。node
1. 領域驅動 – 基於本身對企業級電商平臺設計和開發的豐富經驗,進行領域建模,並將領域映射到業務子系統,劃分高階系統邊界linux
2. 分佈式、服務化 - 吸收企業級電商平臺的經驗教訓,各業務子系統獨立開發、測試和部署,子系統以服務暴露本身的能力,子系統與子系統之間以服務進行通訊和交互。這一點可能會有些爭議,在業務發展初期,整個系統的容量不須要那麼大,分佈式是否是設計過分,特別是在部署上對一個小團隊來說也很難駕馭。在項目中途我也曾經深深的質疑過這一點,可是慢慢也體會到它帶來的好處,放開分佈式架構帶來的將來系統更容易更靈活的優點暫且不談,至少每一個業務子系統能比較獨立高效的開發,測試和部署,在一個高速發展的團隊,不可避免的溝通會成爲一個巨大的挑戰,這種獨立性能夠很大程度減小一些艱難和低效的溝通。web
3. 持續集成 – 從第一天開始創建持續集成的體系和流程,保持開發、測試、部署的高效性docker
在人員有限的狀況下,以一個業務領域切入,構建業務子系統,肯定基礎技術選型,提供業務子系統的樣本項目工程和框架供後續業務子系統開發參考。由於交易是電商平臺最重要的一個業務領域,而且涉及到跟幾乎全部其餘業務領域的交互,咱們選擇交易子系統爲切入點。數據庫
基礎技術選型:Java+Spring做爲服務開發的基礎框架,MySQL存儲交易數據,RestEasy做爲REST服務框架,子系統間的服務調用使用Hessian。由於團隊成員曾經在淘寶交易平臺的工做經歷,整個服務框架基礎技術棧的的選型深受阿里的影響。後端
1. 一個業務子系統由-app, -client, -server三個項目組成。其中app是controller層,經過RestEasy把服務以REST接口發佈出去;client暴露了業務服務接口及Domain對象;server提供業務服務的實現。這裏app不會直接調用server,app須要調用業務服務也是以client爲入口。這裏主要的考慮是基礎服務和應用服務這兩級服務的概念。這個概念相信瞭解淘繫系統的人不會陌生,基礎服務只提供對核心領域對象的基本操做(典型的增刪改查及基於此的一些基本操做),應用服務通常對應一個業務場景,須要調用一個或多個基礎服務,進行串聯、聚合或其它計算處理。例如:購物車的增刪改查是典型的基礎服務,而計算購物車時一個典型的應用服務。這樣設計,隨着業務量的增加,系統將來更容易向微服務架構演進。緩存
2. 業務子系統打包成WAR部署到Tomcat須要一個web項目,-web,Web項目裏,包含datasource,hessian服務發佈,聲明包含哪些業務子系統,以及配置文件。由於在項目初期咱們須要支撐的業務量還沒達到必定的規模,出於成本控制的考慮咱們須要將多個業務子系統打包發佈到一個WAR中,因此這裏出現了須要在Web項目中聲明包含哪些業務子系統。將來若是須要從一個WAR中將一些業務子系統拆出來單獨發佈,也比較靈活。php框架
3. 服務調用框架,說到這裏,你們第一反應確定是Dubbo,在項目初期,咱們並未使用Dubbo的方案,主要是考慮到團隊成員並無Dubbo使用的經驗,並且在咱們並未產生大規模分佈式服務和服務治理的需求時,Dubbo的使用在項目初期反而可能成爲咱們的不可承受之重,把Dubbo用起來並不難,難的是如何駕馭它。因此咱們選擇使用DNS+Load Balance來作服務發現,基於動態代理實現了Hessian/Local/Mock的服務調用機制(Local主要用於不一樣業務子系統部署到一個WAR中,Mock主要用於UT),每一個Web項目提供一個服務調用的配置文件,配置這個Web項目要調用的服務的調用機制和服務endpoint,由於咱們使用DNS+Load Balance來作服務發現,因此服務endpoint都是Load Balance的地址。
4. 分佈式日誌,分佈式服務調用如何跟蹤一個請求的整個服務調用鏈條,是咱們必需要解決的問題,基於Google的Dapper論文,淘寶實現了EagleEye。其實Dapper的原理並不難理解,咱們選擇本身實現請求的global ID並在分佈式服務調用間進行透傳,與ELK(ElasticSearch+Logstash+Kibana)相結合,能夠比較清晰的呈現一個外部API請求跨系統服務調用的全部日誌。
5. 緩存的使用,對於內容型數據(例如:價格,商品稅率),使用Redis緩存,這裏特別要注意防緩存穿透(當數據庫記錄自己不存在每次都穿透緩存去讀數據庫);對於配置型數據,使用Local JVM緩存(未使用EhCahce,本身實現MRU和緩存空間設定,主要緣由是之前項目有相應的積累,可重用);對於操做型數據(例如:購物車,庫存),使用Redis緩存,操做型數據裏,特別是購物車讀寫一樣頻繁,在高併發環境下容易由於緩存更新失敗而致使緩存和數據庫數據不一致,須要使用MQ記錄不一致情形並經過Listener應用執行緩存失效。
6. 圖片服務器的使用,團隊小夥伴之前有大規模應用TFS的經驗,給我反饋是TFS算是淘寶開源做品裏爲數很少的精品,剎那間我是有點心動的。現實立刻打醒我,小夥伴須要去作其它更重要的事情,TFS這玩意,等咱們圖片規模大到必定程度必須本身來作圖片服務器和存儲系統再考慮吧。因而咱們選擇了七牛,很完整的圖片管理和CDN服務,算下來一年的價格也不貴。固然第三方服務始終是不可靠的,咱們也專門引入了assets的業務子系統管理圖片及圖片在服務器上的元信息,這樣在未來比較容易的遷移到其它圖片雲服務或者本身使用Nginx+TFS。
隨着人員的逐步到位,各個業務領域的業務子系統開始鋪開進行開發,這個時候如何協調開發人員以統一的風格來實現各個業務子系統就顯得格外重要了。並且,在基礎框架和工具層面,也要儘可能統一,減小維護的成本,因此,咱們在開發團隊裏一直有一個由精兵強將組成的小組負責這一部分。
1. 數據庫規範:包括每一個業務對象對應的數據庫表如何設定主鍵,惟一ID,惟一性索引,每張數據庫的預留字段(建立時間,更新時間,建立人,更新人,樂觀鎖計數等)。
2. REST API規範:增刪改查的URI pattern,特殊POST操做的URI pattern,API版本。
3. 通用參數規範:對語言,貨幣,請求ID,調用者等的統一命名和處理。
4. 領域邊界劃分:在劃分基礎領域邊界的基礎上,爲了提升性能,減小沒必要要的遠程服務調用,在業務容許的狀況下,進行必定的冗餘。例如:在購物車中不只僅存儲商品SKU ID,還包括商品的名稱,商品縮略圖連接,店鋪名稱等信息,避免在查詢購物車時再去調用商品API獲取這些信息。
5. 異常處理:提供異常基礎類,基於異常基礎類提供異常處理框架在業務服務層和REST服務層對異常進行統一處理。各業務子系統僅需擴展異常基礎類,在業務代碼中拋出這些異常便可。異常處理框架幫助處理剩下的事情。這樣對於API調用者來講也是極其友好的。
6. 配置管理:在系統尚未發展到必定規模時,可能還不須要一個集中的配置中心。可是,每一個業務子系統切忌分散管理配置信息,由於開發,測試,預發佈,線上各類環境須要的配置極可能是不相同的,分散管理的配置信息在每次部署一個新的環境簡直是噩夢。目前,由於咱們已經的系統規模已經發展到10+個業務子系統,即便每一個子系統都集中管理配置信息,如何高效的管理各個不一樣環境的配置信息已經成爲一個很是突出的問題,咱們基於disconf已經作了POC,計劃在下一個階段把disconf用起來。我本人是Netflix OSS的粉絲,原本Netflix Archiaus是個人首選,可是考慮最近接觸的國內開源的工具愈來愈好,社區也愈來愈活躍,咱們果斷的選擇了disconf。
7. Session:在這個方面咱們是走了一些彎路的,已開始咱們有規劃專門的session service,而且與shiro作整合,後面的session信息存到Redis中,這個已經基本開發完畢。可是在項目實際演進的過程當中,這個階段咱們的前端應用裏PC端和移動端商城都是基於PHP開發(基於Yii Framework),在PHP裏已經有比較完整的session處理,咱們後端的業務服務徹底是無狀態的,在這裏再去使用這個統一的session service反到在項目時間特別緊張的狀況下給PHP開發形成不少困擾,因此,咱們決定短時間內放棄session service,全權交由PHP來處理。在將來系統演進到前段多應用,甚至不一樣技術棧時在從新把它找回來。
8. Scheduler和任務處理:對淘寶瞭解的同窗必定據說過他們早期開源的TBScheduler和如今正在使用的TTD,還有最近噹噹開源的Elastic-Job,但是,對於一個剛剛起步的業務系統,對於這種任務處理還沒到須要分佈式的地步,因此,咱們選擇了從Quartz和Spring Batch切入,可是Quartz自己只負責調度自己,咱們仍是須要存儲一些任務的詳細信息和狀態,要支持異步任務的回調通知狀態,排他性任務,已經簡單的任務流程編排,咱們按照本身的需求,基於Quartz作了擴展支持這些功能。將來若是業務發展須要咱們引入分佈式任務處理的機制,我想Elastic-Job會是個人第一選擇。
9. 統一登陸服務:今天我相信在Java的世界裏你們要實現統一登陸服務大部分人會第一選擇會是CAS。CAS對各類認證協議的支持何嘗完備,後面也很容易掛本身實現的credential數據庫。不過比較討厭的是,CAS自帶前臺登陸頁面,這部分是基於JSP的,而咱們的前端工程師全是PHP的,改造起來那叫一個費勁。雖然咱們能夠放棄它自帶的JSP頁面直接調用CAS的API,可是處理起來過於複雜,對於咱們這個規模有限的初創團隊有點不可承受之重。另外,目前咱們團隊在註冊這個環節是直接繞過了CAS調用用戶子系統的註冊API,對於PHP同窗們來說也是很高興的。
10. 非Java業務子系統:在電商平臺中,從業務需求來說,促銷是很是複雜的一個業務子系統,各類不一樣的促銷條件,促銷次數限制,促銷之間的互斥和組合,促銷金額計算,和優惠券或優惠碼結合使用,其實這須要一個高效的規則引擎,一方面可以比較清晰的定義這些規則,另外一方面在執行期也可以在毫秒級可以完成對一個訂單的促銷計算,傳統的Java+關係數據庫建模是很難搞定的。已開始咱們想基於一個開源的規則引擎來作,因此咱們隊Drools作了必定的研究,做爲一個通用的規則引擎,Drools功能比較強大,可是也比較複雜,在咱們只有1個開發工程師能鋪到這個領域的狀況下,也很難搞定;另外,Drools在實時規則計算的效率上也是一個很大的問號,並且促銷管理業務工具對咱們是很是重要的,基於Drools的規則定義語言來開發這個工具難度過高。咱們最終選擇了DSL,定義一套促銷這個領域的專有語言,用JRuby來實現促銷DSL的語義模型和語法解析器,一來Ruby的一些聲明式語法特性是很是適合來定義DSL的語義模型和進行語法解析,另外一方面JRuby運行在JVM中能夠和各類Java庫無縫整合。促銷引擎分爲4個子系統:DSL引擎-負責管理促銷DSL定義,解析DSL轉化爲語義模式;預計算引擎-負責對促銷規則進行預計算,將商品和其可能應用的促銷進行關聯(提升runtime計算的效率);runtime計算引擎-在線爲一個訂單計算促銷結果。優惠券引擎 – 處理優惠券和優惠碼的生成,管理和使用。目前,咱們的促銷規則DSL定義,預計算結果和優惠券信息均使用MongoDB進行存儲。其實即便這樣,業務管理工具來管理DSL來表達的促銷規則仍是一個挑戰,咱們在二者直接加入了一層meta data層來作映射緩解這個問題,這就意味着業務管理工具只是DSL表達能力的一個子集,對於一些高級的促銷,咱們直接在工具上開放了DSL編輯器直接對DSL進行編輯。另外,促銷引擎的4個子系統都已經利用Docker進行部署,也爲咱們系統向容器化部署演進打下了基礎。
11. 前臺商城系統和CMS:自己我本身在CMS上有一些研究,對WordPress, Joomla和Drupal都有必定的瞭解,加上電商運營的特色,對CMS的要求特別強,我一開始是打算在Drupal上往前走的。並且咱們在Drupal上也作了投資,研究學習,POC,前先後後也有小三個月,可是慢慢我也發現一個問題:Drupal自己也是很完整的一個系統,加上生態系統,和電商的領域已經有不少交集,這給個人小夥伴形成了不少困擾,他一直在質疑爲何產品目錄樹,購物車這些Drupal都有現成的插件咱們還要再去實現一套後端服務。雖然從架構上很容易解釋,可是現實是當你天天面對這樣一個系統時是不免會很糾結的。後來,咱們找到了一個既有豐富的Drupal經驗,也經歷了去Drupal,自主開發CMS的小夥伴,一番溝通和探討,咱們決定放棄Druple,本身幹。
我這裏貼一下個人小夥伴給個人建議吧:drupal善於作cms,模塊當然成熟,可是犧牲了性能爲代價。咱們的業務場景更加靈活豐富,結合了傳統pc,線下收銀,以及移動,o2o等各類場景。單純的依靠定製模塊開發將會是事情變得更加複雜且不可控。 採用一個流行且易用的開源框架,使得咱們在迅速迭代與穩定架構之間得到了更好的平衡,同時也能更好的結合中國特點的市場需求。經過框架的crud功能,咱們迅速完成了傳統的cms模塊。而經過mvc最流行的業務分層模式,咱們更好的經過m層同後臺進行了鬆耦合式的開發體驗。使得整個系統既能夠脫離應用層縱向拓展,也能夠經過其餘各類應用層架構(node.js agular.js)實現橫向的拓展。drupal的缺點還有一些,好比社區陳舊,模塊數量雖多可是優質模塊不多。性能上由於其自身機制,當系統複雜以後容易產生性能瓶頸,且不利於開發人員trace。 若是採用drupal,咱們將被迫用5-10年的senior phper去學習一個在中國並不流行的技術架構。基本是很難找到人的。換開源php框架以後,那麼,只須要有幾個senior的開發人員進行組織總體架構並review,就能利用中國大量的碼農(coder)。
12. 業務管理工具:AngularJS+Bootstrap,這個應該沒有太多爭議,雖然現現在React.js如火如荼,但是在咱們那個時候Angular仍是更靠譜的選擇,另外咱們的工具應用選用了Spring的AppFuse框架,讓他們可以更快速的開發前臺工具應用,並方便的處理訪問控制,多語言,先後臺交互等問題。
1. 單元測試 – 節奏再快,資源再短缺,我仍是堅守底線,你們作好UT,並且是基於mock的UT,前期在UT的投資雖然對於快速迭代的節奏來說每每都會有衝突,但是它帶來的後期維護的便捷和高效每每是值得咱們付出的。
2. 持續集成 – 從第一天,咱們就投入一個專職工程師進行來負責持續集成,Jenkins, Maven repostiory, Git的搭建,持續集成腳本的開發,持續集成流程的創建。這些在不少小團隊看來沒必要要的投資其實隨着項目的執行其帶來的好處是會不斷被放大的。咱們採用chef來生成操做系統和中間件級別的標準image(例如:Tomcat, Nginx, MySQL, Redis, MQ, ElasticSearch等等的標準image),可是咱們的持續發佈腳本並未使用chef來編寫,而是直接用ruby來實現的,這個主要緣由是直接用ruby提供了更大的靈活性,咱們在之前的項目中也有必定的積累。
3. 測試 – 很遺憾,咱們的測試資源是在有限,在保系統功能的基本準則下,咱們犧牲了API級別的功能測試,前期只能靠UT來保障了。這也有不少無奈,若是有一天測試資源跟上,須要第一時間把API自動化測試,和基於場景的自動化測試不上。
1. 配置中心,目前很痛的一個環節,前文已經提到,咱們已完成disconf的POC,接下來就會使用。
2. 服務發現和調用框架:用不用dubbo?其實有其它更輕量的選擇(例如:consul作服務註冊和發現,hystrix/turbine作故障隔離和服務metris),咱們如今的實現已經很小清新。另外一個角度dubbo在國內的生態的確不錯,基本上主流電商公司都在使用。這個留着慢慢糾結吧。
3. 錯誤數據校訂平臺:在高併發的場景老是不可避免出現一些異常狀況形成錯誤數據,創建一套錯誤數據校訂平臺是很重要的,要知道客戶投訴起來,運營抱怨起來,每每技術團隊是最慘的。
4. 全docker化:目前除了促銷引擎的4個業務子系統,其餘仍是使用的青雲上的linux虛機,考慮到初期業務不大要節省服務器成本,咱們不得不將一些業務子系統合併到一個WAR裏面發佈,若是每一個業務子系統都可以docker化,就不須要這麼費勁了。
5. API網關:目前咱們的業務服務已經有需求要被外部系統調用(例如:聯盟營銷系統),如何將內部的業務服務暴露出去變成一個課題,咱們須要引入API網關來解決這個問題,目前咱們已經開始調研zuul。
6. 多租戶模式下不一樣客戶的個性化定製:目前咱們只能作到前端商城的個性化定製和外部系統集成的個性化定製,核心業務層如何作個性化定製是須要解決的,咱們之前有作傳統企業級電商平臺的定製框架的經驗,可是在SaaS多租戶的場景下這個將會是個巨大的挑戰。
7. 壓測體系:SaaS和傳統企業電商平臺的壓測是很不同的,若是評估線上環境的容量,如何持續的對線上環境作壓測,如何模擬真實流量,如今這些一二線互聯網電商平臺已經積累了足夠的經驗,咱們要作的,就是一步一步,吸收人家的精華,結合自身的狀況,執行起來。
8. 運維體系:這是一個比較大的話題,咱們能夠單獨再討論。