打造內(nèi)部應(yīng)用框架
當(dāng)當(dāng)技術(shù)部現(xiàn)在是按照產(chǎn)品線(xiàn)劃分的,一個(gè)產(chǎn)品線(xiàn)的產(chǎn)品、開(kāi)發(fā)、測(cè)試都在一個(gè)部門(mén),但像項(xiàng)目管理、運(yùn)維、架構(gòu)這些技術(shù)體系中公用的部分是獨(dú)立的部門(mén)。架構(gòu)部里主要分成三部分,一個(gè)是架構(gòu)與規(guī)范,一個(gè)是性能測(cè)試,一個(gè)是基礎(chǔ)應(yīng)用系統(tǒng)研發(fā)。
我們花了比較多的精力在技術(shù)架構(gòu)上,去年我們?cè)贒ubbo上做了二次開(kāi)發(fā),做了DubboX并且對(duì)外開(kāi)源,業(yè)界反饋還不錯(cuò),包括很多來(lái)面試的人都知道。
我們的技術(shù)體系、核心業(yè)務(wù)系統(tǒng)明確的方向是Java,去年年底,我們開(kāi)始做一個(gè)基于Java的應(yīng)用開(kāi)發(fā)框架,DDFrame,用它去對(duì)接一些核心 組件,包括SOA、作業(yè)調(diào)度、緩存、消息隊(duì)列、數(shù)據(jù)庫(kù)、配置中心等,現(xiàn)在已經(jīng)發(fā)布了2.0版本。雖然受限于資源,進(jìn)度比較緩慢,但我們一直在做這件事,未 來(lái)也會(huì)慢慢完善這個(gè)框架,使其成為技術(shù)體系的核心。
開(kāi)源Dubbox,擴(kuò)展Dubbo服務(wù)框架支持REST風(fēng)格遠(yuǎn)程調(diào)用
當(dāng)當(dāng)網(wǎng)近日開(kāi)源了Dubbox項(xiàng)目,可為Dubbo服務(wù)框架提供多項(xiàng)擴(kuò)展功能,包括REST風(fēng)格遠(yuǎn)程調(diào)用、Kryo/FST序列化等等。
當(dāng)當(dāng)網(wǎng)架構(gòu)部和技術(shù)委員會(huì)架構(gòu)師沈理向InfoQ中文站介紹了Dubbox項(xiàng)目,開(kāi)發(fā)背景和主要特點(diǎn)描述如下:
Dubbo是一個(gè)被國(guó)內(nèi)很多互聯(lián)網(wǎng)公司廣泛使用的開(kāi)源分布式服務(wù)框架,即使從國(guó)際視野來(lái)看應(yīng)該也是一個(gè)非常全面的SOA基礎(chǔ)框架。作為一個(gè)重要的技術(shù)研究課題,在當(dāng)當(dāng)網(wǎng)我們根據(jù)自身的需求,為Dubbo實(shí)現(xiàn)了一些新的功能,并將其命名為Dubbox(即Dubbo eXtensions)。
主要的新功能包括:
支持REST風(fēng)格遠(yuǎn)程調(diào)用(HTTP + JSON/XML):基于非常成熟的JBoss RestEasy框架,在dubbo中實(shí)現(xiàn)了REST風(fēng)格(HTTP + JSON/XML)的遠(yuǎn)程調(diào)用,以顯著簡(jiǎn)化企業(yè)內(nèi)部的跨語(yǔ)言交互,同時(shí)顯著簡(jiǎn)化企業(yè)對(duì)外的Open API、無(wú)線(xiàn)API甚至AJAX服務(wù)端等等的開(kāi)發(fā)。事實(shí)上,這個(gè)REST調(diào)用也使得Dubbo可以對(duì)當(dāng)今特別流行的“微服務(wù)”架構(gòu)提供基礎(chǔ)性支持。 另外,REST調(diào)用也達(dá)到了比較高的性能,在基準(zhǔn)測(cè)試下,HTTP + JSON與Dubbo 2.x默認(rèn)的RPC協(xié)議(即TCP + Hessian2二進(jìn)制序列化)之間只有1.5倍左右的差距,詳見(jiàn)下文的基準(zhǔn)測(cè)試報(bào)告。
支持基于Kryo和FST的Java高效序列化實(shí)現(xiàn):基于當(dāng)今比較知名的Kryo和FST高性能序列化庫(kù),為Dubbo 默認(rèn)的RPC協(xié)議添加新的序列化實(shí)現(xiàn),并優(yōu)化調(diào)整了其序列化體系,比較顯著的提高了Dubbo RPC的性能,詳見(jiàn)下圖。
支持基于嵌入式Tomcat的HTTP remoting體系:基于嵌入式tomcat實(shí)現(xiàn)dubbo的HTTP remoting體系(即dubbo-remoting-http),用以逐步取代Dubbo中舊版本的嵌入式Jetty,可以顯著的提高REST等的遠(yuǎn)程調(diào)用性能,并將Servlet API的支持從2.5升級(jí)到3.1。(注:除了REST,dubbo中的WebServices、Hessian、HTTP Invoker等協(xié)議都基于這個(gè)HTTP remoting體系)。
升級(jí)Spring:將dubbo中Spring由2.x升級(jí)到目前最常用的3.x版本,減少項(xiàng)目中版本沖突帶來(lái)的麻煩。
升級(jí)ZooKeeper客戶(hù)端:將dubbo中的zookeeper客戶(hù)端升級(jí)到最新的版本,以修正老版本中包含的bug。
上面很多功能已在當(dāng)當(dāng)網(wǎng)內(nèi)部穩(wěn)定的使用,現(xiàn)在開(kāi)源出來(lái),供大家參考和指正。也希望感興趣的朋友也來(lái)為Dubbo貢獻(xiàn)更多的改進(jìn)。
注:dubbox和dubbo 2.x是兼容的,沒(méi)有改變dubbo的任何已有的功能和配置方式(除了升級(jí)了Spring之類(lèi)的版本)。另外,dubbox也嚴(yán)格遵循了Apache 2.0許可證的要求。
分布式作業(yè)調(diào)度框架elastic-job的開(kāi)源
elastic-job原本是當(dāng)當(dāng)java應(yīng)用框架ddframe的一部分,本名dd-job。ddframe包括編碼規(guī)范,開(kāi)發(fā)框架,技術(shù)規(guī)范,監(jiān)控以及分布式組件。ddframe規(guī)劃分為4個(gè)演進(jìn)階段,目前處于第2階段。3、4階段涉及的技術(shù)組件不代表當(dāng)當(dāng)沒(méi)有使用,只是ddframe還未統(tǒng)一規(guī)劃。
ddframe由各種模塊組成,均已dd-開(kāi)頭,如dd-container、dd-soa、dd-rdb、dd-job等。當(dāng)當(dāng)希望將ddframe的各個(gè)模塊與公司環(huán)境解耦并開(kāi)源以反饋社區(qū)。之前開(kāi)源的Dubbo擴(kuò)展版本DubboX即是dd-soa的核心模塊。而本次介紹的elastic-job則是dd-job的開(kāi)源部分,其中監(jiān)控(但開(kāi)源了監(jiān)控方法)和ddframe核心接入等部分并未開(kāi)源。
elastic-job主要的設(shè)計(jì)理念是無(wú)中心化的分布式定時(shí)調(diào)度框架,思路來(lái)源于Quartz的基于數(shù)據(jù)庫(kù)的高可用方案。但數(shù)據(jù)庫(kù)畢竟沒(méi)有分布式協(xié)調(diào)功能,所以在高可用方案的基礎(chǔ)上增加了彈性擴(kuò)容和數(shù)據(jù)分片的思路,以便于更大限度的利用分布式服務(wù)器的資源。
團(tuán)隊(duì)目前由3個(gè)部分組成,第一部分是開(kāi)發(fā)團(tuán)隊(duì),由架構(gòu)部的架構(gòu)師曹昊、高洪濤和我組成,主要負(fù)責(zé)設(shè)計(jì)和編碼;第二部分是來(lái)自于各個(gè)研發(fā)團(tuán)隊(duì)的應(yīng)用架構(gòu)師、開(kāi)發(fā)工程師和架構(gòu)部總監(jiān)史海峰,他們負(fù)責(zé)推廣落地,整理需求并貢獻(xiàn)當(dāng)當(dāng)已經(jīng)存在的最佳實(shí)踐代碼;第三部分由架構(gòu)部性能測(cè)試團(tuán)隊(duì)組成,負(fù)責(zé)框架的性能和穩(wěn)定性測(cè)試。
elastic-job的主要分為注冊(cè)中心、數(shù)據(jù)分片、分布式協(xié)調(diào),定時(shí)任務(wù)處理和多作業(yè)模式等模塊。
注冊(cè)中心模塊目前直接使用Zookeeper,用于記錄作業(yè)的配置,服務(wù)器信息以及作業(yè)運(yùn)行狀態(tài)。Zookeeper雖然很成熟,但原理復(fù)雜,使用較難,在海量數(shù)據(jù)支持的情況下也會(huì)有性能和網(wǎng)絡(luò)問(wèn)題。目前elastic-job已經(jīng)抽象出注冊(cè)中心的接口,下一步將會(huì)考慮支持多注冊(cè)中心,如etcd,或由用戶(hù)自行實(shí)現(xiàn)注冊(cè)中心。無(wú)臨時(shí)節(jié)點(diǎn)和監(jiān)聽(tīng)機(jī)制的注冊(cè)中心需要自行實(shí)現(xiàn)定時(shí)心跳監(jiān)測(cè)等功能。
數(shù)據(jù)分片是elastic-job中實(shí)現(xiàn)分布式的重要概念,將真實(shí)數(shù)據(jù)和邏輯分片對(duì)應(yīng),用于解耦作業(yè)框架和數(shù)據(jù)的關(guān)系。作業(yè)框架只負(fù)責(zé)將分片合理的分配給相關(guān)的作業(yè)服務(wù)器,而作業(yè)服務(wù)器需要根據(jù)所分配的分片匹配數(shù)據(jù)進(jìn)行處理。服務(wù)器分片目前都存儲(chǔ)在注冊(cè)中心中,各個(gè)服務(wù)器根據(jù)自己的IP地址拉取分片。
分布式協(xié)調(diào)模塊用于處理作業(yè)服務(wù)器的動(dòng)態(tài)擴(kuò)容縮容。一旦集群中有服務(wù)器發(fā)生變化,分布式協(xié)調(diào)將自動(dòng)監(jiān)測(cè)并將變化結(jié)果通知給各個(gè)仍存活的作業(yè)服務(wù)器。協(xié)調(diào)時(shí)將會(huì)涉及主節(jié)點(diǎn)選舉,重分片等操作。目前使用的Zookeeper的臨時(shí)節(jié)點(diǎn)和監(jiān)聽(tīng)器實(shí)現(xiàn)主動(dòng)檢查和通知功能。
定時(shí)任務(wù)處理根據(jù)cron表達(dá)式定時(shí)觸發(fā)任務(wù),目前有防止任務(wù)同時(shí)觸發(fā),錯(cuò)過(guò)任務(wù)重出發(fā)等功能。主要還是使用Quartz本身的定時(shí)調(diào)度功能,為了便于控制,每個(gè)任務(wù)都使用獨(dú)立的線(xiàn)程池。
多作業(yè)模式將定時(shí)任務(wù)分為多種流程,有不經(jīng)任何修飾的簡(jiǎn)單任務(wù);有用于處理數(shù)據(jù)的fetchData/processData的數(shù)據(jù)流任務(wù);以后還將增加消息流任務(wù),文件任務(wù),工作流任務(wù)等。用戶(hù)能以插件的形式擴(kuò)展并貢獻(xiàn)代碼。
作業(yè)即定時(shí)任務(wù)。一般來(lái)說(shuō),系統(tǒng)可使用消息傳遞代替部分使用作業(yè)的場(chǎng)景。兩者確有相似之處??苫ハ嗵鎿Q的場(chǎng)景,如隊(duì)列表。將待處理的數(shù)據(jù)放入隊(duì)列表,然后使用頻率極短的定時(shí)任務(wù)拉取隊(duì)列表的數(shù)據(jù)并處理。這種情況使用消息中間件的推送模式可更好的處理實(shí)時(shí)性數(shù)據(jù)。而且基于數(shù)據(jù)庫(kù)的消息存儲(chǔ)吞吐量遠(yuǎn)遠(yuǎn)小于基于文件的順序追加消息存儲(chǔ)。
但在某些場(chǎng)景下則不能互換:
時(shí)間驅(qū)動(dòng) OR 事件驅(qū)動(dòng):內(nèi)部系統(tǒng)一般可以通過(guò)事件來(lái)驅(qū)動(dòng),但涉及到外部系統(tǒng),則只能使用時(shí)間驅(qū)動(dòng)。如:抓取外部系統(tǒng)價(jià)格。每小時(shí)抓取,由于是外部系統(tǒng),不能像內(nèi)部系統(tǒng)一樣發(fā)送事件觸發(fā)事件。
批量處理 OR 逐條處理:批量處理堆積的數(shù)據(jù)更加高效,在不需要實(shí)時(shí)性的情況下比消息中間件更有優(yōu)勢(shì)。而且有的業(yè)務(wù)邏輯只能批量處理,如:電商公司與快遞公司結(jié)算,一個(gè)月結(jié)算一次,并且根據(jù)送貨的數(shù)量有提成。比如,當(dāng)月送貨超過(guò)1000則額外給快遞公司多1%的快遞費(fèi)。
非實(shí)時(shí)性 OR 實(shí)時(shí)性:雖然消息中間件可以做到實(shí)時(shí)處理數(shù)據(jù),但有的情況并不需要。如:VIP用戶(hù)降級(jí),如果超過(guò)1年無(wú)購(gòu)買(mǎi)行為,則自動(dòng)降級(jí)。這類(lèi)需求沒(méi)有強(qiáng)烈的時(shí)間要求,不需要按照時(shí)間精確的降級(jí)VIP用戶(hù)。
系統(tǒng)內(nèi)部 OR 系統(tǒng)解耦。作業(yè)一般封裝在系統(tǒng)內(nèi)部,而消息中間件可用于系統(tǒng)間解耦。
elastic-job的主要功能
主要功能
分布式:重寫(xiě)Quartz基于數(shù)據(jù)庫(kù)的分布式功能,改用Zookeeper實(shí)現(xiàn)注冊(cè)中心。
并行調(diào)度:采用任務(wù)分片方式實(shí)現(xiàn)。將一個(gè)任務(wù)拆分為n個(gè)獨(dú)立的任務(wù)項(xiàng),由分布式的服務(wù)器并行執(zhí)行各自分配到的分片項(xiàng)。
彈性擴(kuò)容縮容:將任務(wù)拆分為n個(gè)任務(wù)項(xiàng)后,各個(gè)服務(wù)器分別執(zhí)行各自分配到的任務(wù)項(xiàng)。一旦有新的服務(wù)器加入集群,或現(xiàn)有服務(wù)器下線(xiàn),elastic-job將在保留本次任務(wù)執(zhí)行不變的情況下,下次任務(wù)開(kāi)始前觸發(fā)任務(wù)重分片。
集中管理:采用基于Zookeeper的注冊(cè)中心,集中管理和協(xié)調(diào)分布式作業(yè)的狀態(tài),分配和監(jiān)聽(tīng)。外部系統(tǒng)可直接根據(jù)Zookeeper的數(shù)據(jù)管理和監(jiān)控elastic-job。
定制化流程型任務(wù):作業(yè)可分為簡(jiǎn)單和數(shù)據(jù)流處理兩種模式,數(shù)據(jù)流又分為高吞吐處理模式和順序性處理模式,其中高吞吐處理模式可以開(kāi)啟足夠多的線(xiàn)程快速的處理數(shù)據(jù),而順序性處理模式將每個(gè)分片項(xiàng)分配到一個(gè)獨(dú)立線(xiàn)程,用于保證同一分片的順序性,這點(diǎn)類(lèi)似于kafka的分區(qū)順序性。
其他功能
失效轉(zhuǎn)移:彈性擴(kuò)容縮容在下次作業(yè)運(yùn)行前重分片,但本次作業(yè)執(zhí)行的過(guò)程中,下線(xiàn)的服務(wù)器所分配的作業(yè)將不會(huì)重新被分配。失效轉(zhuǎn)移功能可以在本次作業(yè)運(yùn)行中用空閑服務(wù)器抓取孤兒作業(yè)分片執(zhí)行。同樣失效轉(zhuǎn)移功能也會(huì)犧牲部分性能。
Spring命名空間支持:elastic-job可以不依賴(lài)于spring直接運(yùn)行,但是也提供了自定義的命名空間方便與spring集成。
運(yùn)維平臺(tái):提供web控制臺(tái)用于管理作業(yè)。
非功能需求
穩(wěn)定性:在服務(wù)器無(wú)波動(dòng)的情況下,并不會(huì)重新分片;即使服務(wù)器有波動(dòng),下次分片的結(jié)果也會(huì)根據(jù)服務(wù)器IP和作業(yè)名稱(chēng)哈希值算出穩(wěn)定的分片順序,盡量不做大的變動(dòng)。
高性能:同一服務(wù)器的批量數(shù)據(jù)處理采用自動(dòng)切割并多線(xiàn)程并行處理。
靈活性:所有在功能和性能之間的權(quán)衡,都可通過(guò)配置開(kāi)啟/關(guān)閉。如:elastic-job會(huì)將作業(yè)運(yùn)行狀態(tài)的必要信息更新到注冊(cè)中心。如果作業(yè)執(zhí)行頻度很高,會(huì)造成大量Zookeeper寫(xiě)操作,而分布式Zookeeper同步數(shù)據(jù)可能引起網(wǎng)絡(luò)風(fēng)暴。因此為了考慮性能問(wèn)題,可以犧牲一些功能,而換取性能的提升。
冪等性:elastic-job可犧牲部分性能用以保證同一分片項(xiàng)不會(huì)同時(shí)在兩個(gè)服務(wù)器上運(yùn)行。
容錯(cuò)性:作業(yè)服務(wù)器和Zookeeper斷開(kāi)連接則立即停止作業(yè)運(yùn)行,用于防止分片已經(jīng)重新分配,而腦裂的服務(wù)器仍在繼續(xù)執(zhí)行,導(dǎo)致重復(fù)執(zhí)行。
Elastic-job在當(dāng)當(dāng)內(nèi)部也是剛剛進(jìn)行推廣,目前支付系統(tǒng)、訂單系統(tǒng)、發(fā)票系統(tǒng)、促銷(xiāo)系統(tǒng)都有使用,快遞系統(tǒng)和倉(cāng)儲(chǔ)系統(tǒng)也即將使用。開(kāi)源后也得知不少公司有使用計(jì)劃。