微服務(wù)并不是一個新的東西塑娇。它源自1970年代的研究晚吞,最近火了起來是因為微服務(wù)可以讓我們更快速地改變桥言、更方便地實現(xiàn)價值,提高靈活性葵礼。微服務(wù)源自actor-based系統(tǒng)号阿、服務(wù)設(shè)計、自動化系統(tǒng)鸳粉、domain驅(qū)動設(shè)計和分布式系統(tǒng)扔涧。它細(xì)粒度的模塊化設(shè)計必然促使開發(fā)者們構(gòu)建分布式系統(tǒng)。你肯定發(fā)現(xiàn)了届谈,分布式系統(tǒng)很困難枯夜;因為它們?nèi)菀壮鰡栴},運(yùn)行緩慢艰山,并且被CAP和FLP理論所限制湖雹。換句話說,它們的構(gòu)建和運(yùn)維都特別復(fù)雜曙搬。為了解決這個問題摔吏,reactive便出現(xiàn)了。
** 30多年的演變 **
Actor模型在1973年被 C. Hewitt, P. Bishop, and R.Steiger 提出纵装。自主計算征讲,一個在2001年創(chuàng)造的術(shù)語,用于描述分布式系統(tǒng)的自管理特性(自愈性橡娄、自由化性等)
但是诗箍,究竟什么是reactive?Reactive這個詞如今被加上了新的含義。牛津詞典定義reactive為“給刺激信號一個反饋”挽唉。因此滤祖,reactive程序會響應(yīng)刺激信號,并且根據(jù)收到的信號來調(diào)整自己的行為瓶籽。但是氨距,這種響應(yīng)性和適應(yīng)性對編程來說是一種挑戰(zhàn),因為它們意味著計算流程不是被程序員而是刺激信號所控制棘劣。這一章,我們將會了解Vert.x是如何幫助你來實現(xiàn)reactive的:
- Reactive編程——一種開發(fā)模型楞遏;其專注于數(shù)據(jù)流向茬暇、對變化的反饋,以及傳播它們
- Reactive系統(tǒng)——一種架構(gòu)風(fēng)格寡喝;其基于異步消息糙俗,來構(gòu)建響應(yīng)式的、魯邦的分布式系統(tǒng)
Reactive微服務(wù)系統(tǒng)是由若干個reactive微服務(wù)組成预鬓。因為異步特點巧骚,微服務(wù)的實現(xiàn)具有挑戰(zhàn)。Reactive編程可以減少其復(fù)雜性。現(xiàn)在我們來看下是如何做到的劈彪。
Reactive 編程
Reactive編程是一種開發(fā)模型竣蹦,它以數(shù)據(jù)流和數(shù)據(jù)傳播為驅(qū)動。在reactive編程中沧奴,刺激信號是數(shù)據(jù)的轉(zhuǎn)移痘括,叫做streams。有很多方法可以實現(xiàn)reactive編程模型滔吠,在本文中纲菌,我們使用Reactive Extensions (http://reactivex.io/) ;其中疮绷,streams被叫做observables翰舌,消費(fèi)者對observables進(jìn)行訂閱并且對其值響應(yīng)(如Figure 2-1所示)。
為了更具體得說明這個概念冬骚,我們來看一個例子椅贱;該例子使用了RxJava(https://github.com/ReactiveX/RxJava) ,一個實現(xiàn)了Reactive Extensions的java庫唉韭。該例子位于代碼目錄的 reactive-programming 目錄中夜涕。
在這個例子中,代碼對一個Observable進(jìn)行了觀測(subscribe)属愤,當(dāng)其值在數(shù)據(jù)流中發(fā)生轉(zhuǎn)移時女器,代碼會發(fā)現(xiàn)。訂閱者可以接受三種類型的事件住诸。onNext會在新值出現(xiàn)時被調(diào)用驾胆;onError會在流(stream)發(fā)生錯誤或者拋出異常時調(diào)用;onComplete會在流結(jié)束時被調(diào)用贱呐,而對于無邊界流(unbounded stream)這種情況是不會發(fā)生的丧诺。RxJava包含一個運(yùn)算集來生產(chǎn)、轉(zhuǎn)移奄薇、協(xié)調(diào)Observable們驳阎,例如map可以將一個值轉(zhuǎn)移到另一個中,flatMap可以產(chǎn)生一個Observable或者約束另一個異步動作馁蒂,如下圖所示:
- Observables是有限或無限流呵晚,其包含一系列的值
- Singles是只包含一個值的流,通常是一個運(yùn)算(或操作)的異步結(jié)果沫屡,跟future或promise類似
- Completables是不包含任何數(shù)據(jù)的流饵隙,不過其展示一個運(yùn)算是否完成或已經(jīng)失敗
** RxJava 2 **
RxJava 2.x 最近已經(jīng)發(fā)布了,但是本書中仍然使用了上一個版本(RxJava 1.x). 新的版本概念和老版本保持一致沮脖。2.x新增了兩種stream金矛。Observable表示不支持背壓(back-pressure)的stream芯急,而Flowable表示支持背壓的Observable。2.x還引入了Maybe類型驶俊,表示可以包含0個或者1個值或者一個error的stream.
那我們可以用RxJava做什么呢娶耍?例如,我們可以用其描述一串異步行為并使其井井有條废睦。假如你想下載一個文件伺绽,處理完畢,然后上傳嗜湃;下載和上傳動作是異步的奈应。為了實現(xiàn)這一串動作,你可以使用類似下面的代碼:
你還可以管理和編排異步任務(wù)购披。例如杖挣,為了結(jié)合異步操作的處理結(jié)果,你可以使用zip操作符將兩個不懂流的結(jié)果合并起來:
這些操作符給了你很強(qiáng)的power:你可以優(yōu)雅地有條不紊地管理異步任務(wù)和數(shù)據(jù)流刚陡。那么這和reactive微服務(wù)有什么關(guān)系惩妇?回答這個問題,我們現(xiàn)在來了解以下reactive系統(tǒng)筐乳。
** Reactive Streams **
你或許已經(jīng)聽說過reactive流(http://www.reactivestreams.org/) 歌殃。Reactove流是為了給有背壓異步流的處理一個標(biāo)準(zhǔn)。它提供了很小的一套接口和規(guī)范蝙云,用于描述非阻塞背壓的異步數(shù)據(jù)流的處理氓皱;它并沒有定義流操作符,而是主要用做一個互操作層(an interoperability layer). 這一倡議被很多組織支持勃刨,包括Netflix, Lightbend, and Red Hat等波材。
Reactive 系統(tǒng)
Reactive編程是一個開發(fā)模型,而reactive系統(tǒng)則是一種分布式系統(tǒng)的架構(gòu)風(fēng)格 (http://www.reactivemanifesto.org/) . 它是一套規(guī)范身隐,用于實現(xiàn)響應(yīng)性廷区,使系統(tǒng)能夠在出現(xiàn)錯誤或在壓力之下,也能夠及時地進(jìn)行響應(yīng)贾铝。
為了構(gòu)建這樣一個系統(tǒng)隙轻,reactive系統(tǒng)使用了消息驅(qū)動的方法。所有的構(gòu)建通過異步消息的發(fā)送和接收來交互垢揩。為了結(jié)構(gòu)發(fā)送者和接收者大脉,構(gòu)建都將消息發(fā)送到虛擬地址,同時也像虛擬地址注冊以接收消息水孩。地址(address)是消息目的地的代號,例如一個不透明字符串(opaque string)或一個URL. 若干個接收者可以注冊到同一個地址琐驴;消息投遞的邏輯由底層的實現(xiàn)決定俘种。發(fā)送者不會阻塞著等待回復(fù)秤标;它們可能會稍后才接收到回復(fù),但是在此之前宙刘,它可以同樣接收和發(fā)送苍姜。這個異步特征對你應(yīng)用的實現(xiàn)尤其重要。
使用異步消息傳遞的交互方式悬包,reactive系統(tǒng)會有兩個重要的特征:
- 伸縮性——可以橫向伸縮(scale out/in)
- 恢復(fù)性——可以處理錯誤并且恢復(fù)
伸縮性來自消息傳遞的解耦衙猪。消息被發(fā)送到一個地址之后,可以被一組消費(fèi)者按照一種負(fù)載均衡方法消費(fèi)布近。當(dāng)reactive系統(tǒng)遇到負(fù)載高峰時垫释,它可以創(chuàng)造出新的消費(fèi)者,并在此之后銷毀它們撑瞧。
恢復(fù)性來自處理消息的非阻塞特征棵譬,以及每個組件和可替換特征。首先预伺,這種消息交互模式允許組件在其本地處理錯誤订咸;得益于異步特征,組件不需要等待消息酬诀,因此當(dāng)一個組件發(fā)生錯誤時脏嚷,其他組件仍然會正常工作。其次瞒御,可替換的特征是另一個關(guān)鍵因素父叙;當(dāng)一個處理消息的組件發(fā)生錯誤后,消息可以可以傳遞給在相同地址注冊的其他組件葵腹。
這兩個特點給系統(tǒng)帶來了響應(yīng)性這一特征高每。系統(tǒng)可以自己根據(jù)負(fù)載的輕重進(jìn)行調(diào)整,并且在高負(fù)載或出現(xiàn)錯誤的情況下践宴,依然可以響應(yīng)請求鲸匿。對于構(gòu)建微服務(wù)系統(tǒng)和那些不受調(diào)用者控制的系統(tǒng)來說,這些特征是根本的需求阻肩;在不停止服務(wù)的情況下带欢,能夠添加若干節(jié)點來均衡負(fù)載或處理錯誤,對這些系統(tǒng)來說非常必要烤惊。下一節(jié)我們來看一下Vert.x是怎么解決這些問題的乔煞。
Reactive微服務(wù)
在構(gòu)建一個微服務(wù)系統(tǒng)(也是分布式系統(tǒng))的時候,每一個服務(wù)都可能發(fā)生變化柒室、失敗渡贾、變得緩慢,或者直接被收回雄右。這些事件一定不能影響到整個系統(tǒng)的行為空骚;你的系統(tǒng)必須能夠擁抱變化和處理錯誤纺讲;它可以在次級的模式下運(yùn)行,但是它必須能夠保持響應(yīng)請求囤屹。
為保證這一特性熬甚,reactive微服務(wù)系統(tǒng)是由reactive微服務(wù)組成的。這些微服務(wù)有下面四個特征:
- 自治性
- 異步性
- 恢復(fù)性
- 伸縮性
Reactive微服務(wù)是可自治的肋坚。他們可以根據(jù)周圍的服務(wù)是否可用來調(diào)整自己的行為乡括。自治性往往伴隨著孤立性;Reactive微服務(wù)可以在本地處理錯誤智厌、獨立地完成任務(wù)诲泌,并在必要時和其他服務(wù)合作。它們使用異步消息傳遞的機(jī)制和其他服務(wù)溝通峦剔;它們也會接收消息并且對其作出回應(yīng)档礁。
得益于異步消息機(jī)制,reactive微服務(wù)可以處理錯誤并根據(jù)情況調(diào)整自己的行為吝沫。錯誤不會被擴(kuò)散呻澜,而是在靠近錯誤源頭的地方被處理掉。當(dāng)一個微服務(wù)掛掉之后惨险,它的消費(fèi)者微服務(wù)要能夠處理錯誤并避免擴(kuò)散羹幸。這一孤立原則是避免錯誤逐層上浮而毀掉整個系統(tǒng)的關(guān)鍵”栌洌可恢復(fù)性不只是關(guān)于處理錯誤栅受,它還涉及到自愈性;一個reactive微服務(wù)應(yīng)該能夠從錯誤中恢復(fù)并且對錯誤進(jìn)行補(bǔ)救恭朗。
最后屏镊,reactive微服務(wù)必須是可伸縮的,這樣系統(tǒng)才可以根據(jù)負(fù)載情況來調(diào)整節(jié)點數(shù)量痰腮。這一特性意味著將會有一系列的限制而芥,比如不能有在內(nèi)存中的狀態(tài),要能夠在必要時同步狀態(tài)信息膀值,或者要能夠?qū)⑾⒙酚傻綘顟B(tài)信息相同的節(jié)點棍丐。
什么是Vert.x?
Vert.x是一個用于構(gòu)建reactive和分布式系統(tǒng)的工具箱,其使用了異步非阻塞編程模型沧踏。因為它是一個工具箱而非框架歌逢,因此你可以像使用其他任何library一樣使用Vert.x。它并不會限制你怎樣架構(gòu)你的系統(tǒng)翘狱,你可以任意使用秘案。Vert.x非常靈活,你可以把它當(dāng)做一個獨立的程序,也可以嵌入其它更大的應(yīng)用中踏烙。
從開發(fā)者的角度來說师骗,Vert.x是一系列JAR包。每一個Vert.x模塊都是一個JAR文件讨惩,你可以將其加入你的Classpath中。Vert.x提供了很多模塊來幫你構(gòu)建你想要的系統(tǒng)寒屯,例如HTTP服務(wù)端和客戶端荐捻、消息模塊、或者更底層的模塊例如TCP和UDP等寡夹。你可以自由選擇這些模塊处面,連同Vert.x Core(Vert.x的核心模塊)一起,來構(gòu)建你的系統(tǒng)菩掏。下圖完整展示了Vert.x的生態(tài)魂角。
Vert.x還為構(gòu)建微服務(wù)系統(tǒng)提供了很棒的工具。Vert.x在微服務(wù)流行之前就在推行這種方式智绸。它的設(shè)計和實現(xiàn)都是為了提供一種構(gòu)建微服務(wù)系統(tǒng)的野揪、直觀有效的方法。除此之外瞧栗,你還可以使用Vert.x構(gòu)建reactive的微服務(wù)斯稳;當(dāng)使用Vert.x構(gòu)建微服務(wù)的時候,微服務(wù)會自然地帶上一個核心特征:所有事情都是異步的迹恐。
異步開發(fā)模式
用Vert.x構(gòu)建的所有應(yīng)用都是異步的挣惰,是事件驅(qū)動并且非阻塞的。當(dāng)有趣的事件發(fā)生時殴边,你的應(yīng)用會被通知憎茂。我們來看一個實實在在的例子;Vert.x提供一種創(chuàng)建HTTP服務(wù)器的簡單方式锤岸,這個HTTP服務(wù)器在每次接收到一個HTTP請求的時候被通知:
這個例子中竖幔,我們讓一個requestHandler接收HTTP請求(事件)并且返回"hell Vert.x"。Handler是一個函數(shù)能耻,當(dāng)事件發(fā)生時赏枚,它會被調(diào)用。在我們的例子中晓猛,handler代碼會在每次請求進(jìn)來時被調(diào)用執(zhí)行饿幅。要注意的是,Handler并不會返回一個結(jié)果戒职,但是它可以提供一個結(jié)果栗恩;這個結(jié)果是怎樣被提供的,這個要看是哪種交互行為洪燥。在上面的代碼段中磕秤,它只是向一個HTTP response寫入了結(jié)果乳乌。這個Handler后面跟了一個方法令其監(jiān)聽8080端口。調(diào)用這個HTTP服務(wù)它會返回一個簡單的response:
除了極少數(shù)的例外市咆,Vert.x的所有API都不會阻塞線程汉操。如果結(jié)果可以被馬上提供,那么就直接返回蒙兰;否則磷瘤,就會使用Handler來接收事件。當(dāng)一個事件做好被處理的準(zhǔn)備搜变,或者異步操作的結(jié)果已經(jīng)計算完成時采缚,Handler就會被通知。
傳統(tǒng)的編程方式中挠他,你會寫下如下的代碼:
在這段代碼中扳抽,你在等待計算結(jié)果。當(dāng)換成異步非阻塞開發(fā)模型時殖侵,你會創(chuàng)建一個Handler贸呢,在結(jié)果計算完成時調(diào)用:
在上面代碼中,compute函數(shù)不再返回一個結(jié)果愉耙,因此你不用等待結(jié)果計算完成或返回贮尉。你傳給它一個Handler,在結(jié)果準(zhǔn)備好的時候調(diào)用就可以了朴沿。
得益于這種非阻塞開發(fā)模型猜谚,你可以使用很少的線程處理高并發(fā)工作。絕大多數(shù)情況赌渣,Vert.x會用一個叫做event loop的線程來調(diào)用你的handler們魏铅。下圖描述了Event loop,它消費(fèi)一個事件隊列坚芜,并且將事件分發(fā)到對應(yīng)的Handler中览芳。
基于消息循環(huán)的線程模型有一個很大的優(yōu)點:它簡化了并發(fā)。因為只有一個線程存在鸿竖,因此你永遠(yuǎn)都只被一個線程調(diào)用而不存在并發(fā)的情況沧竟。但是,同樣它也有一個很重要的規(guī)矩缚忧,你一定要遵守:
不要阻塞消息循環(huán)
因為沒有阻塞悟泵,一個消息循環(huán)線程可以短時間內(nèi)分發(fā)巨量的事件,這個模式就叫做reactor模式(reactor pattern, https://en.wikipedia.org/wiki/Reactor_pattern).
我們想一下闪水,如果你不遵守準(zhǔn)則會怎樣糕非。在上面的代碼段中,請求的handler都會一直從一個消息循環(huán)線程中被調(diào)用。所以朽肥,如果HTTP請求不是迅速響應(yīng)用戶而是阻塞禁筏,那么其他的請求就不能夠及時處理,在隊列中等待消息循環(huán)線程被釋放衡招。這樣的話篱昔,你就失去了Vert.x的可擴(kuò)展性和高效的優(yōu)勢。那么始腾,究竟什么會被阻塞呢旱爆?最為明顯的例子是JDBC數(shù)據(jù)庫訪問,它們天生就是會阻塞的窘茁。長時間的計算也會阻塞,例如脆烟,一段計算圓周率小數(shù)點后200000位數(shù)字的代碼一定會阻塞山林。不要擔(dān)心,Vert.x提供了處理阻塞性代碼的工具邢羔。
在一個標(biāo)準(zhǔn)的reactor模型的實現(xiàn)中驼抹,只會有一個消息循環(huán)線程不停地將到來的消息分發(fā)到對應(yīng)的handler中去。單線程問題很簡單拜鹤,它只能同時運(yùn)行在一個CPU核上框冀。Vert.x于此不同胆屿,每個Vert.x實例都維護(hù)了若干個消息循環(huán)線程徐伐,我們稱此為multireactor模式,如下圖所示崭歧。
圖中惯裕,事件被發(fā)到不同的消息循環(huán)線程中温数。不過,如果一個Handler被一個消息循環(huán)線程調(diào)用蜻势,那么它就會一直被這個線程繼續(xù)調(diào)用撑刺,以確保reactor模式的并發(fā)性。如果像上圖一樣握玛,你有若干個消息循環(huán)够傍,那么負(fù)載就可以分?jǐn)偟讲煌腃PU核心上。那么在之前的HTTP例子中怎樣體現(xiàn)呢挠铲?Vert.x會進(jìn)行一次socket listener的注冊冕屯,然后將請求分發(fā)到不同的消息循環(huán)隊列中。
Verticles —— 構(gòu)建的磚石
Vert.x給你提供了構(gòu)建應(yīng)用和代碼的自由市殷。它同時也為輕易實現(xiàn)應(yīng)用提供所需的基礎(chǔ)磚石愕撰,連同簡單、可擴(kuò)展、類似actor的部署方式和并發(fā)模型一起為我們所用搞挣。Verticles是被Vert.x部署和運(yùn)行的代碼塊带迟。一個如微服務(wù)的應(yīng)用,是由運(yùn)行在同一個Vert.x實例上的若干verticle組成的囱桨。一個verticle通常會創(chuàng)建服務(wù)器或客戶端仓犬、注冊一組Handler,以及封裝一部分系統(tǒng)的業(yè)務(wù)處理邏輯舍肠。
標(biāo)準(zhǔn)的verticle會在Vert.x的消息循環(huán)中被執(zhí)行搀继,并且永遠(yuǎn)不會阻塞。Vert.x保證了每一個verticle都會只被同一個線程執(zhí)行而不會有并發(fā)發(fā)生翠语,從而避免同步工作叽躯。Java中,一個verticle是一個繼承自AbstractVerticle的類:
** Worker Verticle **
和標(biāo)準(zhǔn)的verticle不同肌括,worker verticle不是在消息循環(huán)中執(zhí)行的点骑,這就意味著他們可以執(zhí)行阻塞代碼。但是谍夭,這會限制你的可擴(kuò)展性黑滴。
Verticle可以訪問vertx成員變量(是由AbstractVerticle類提供的)來創(chuàng)建服務(wù)器和客戶端,以及和其他的verticle交互紧索。Verticle還可以部署其他的verticle袁辈,對它們進(jìn)行配置,并設(shè)置創(chuàng)建實例的數(shù)量珠漂。這些實例會和不同的消息循環(huán)線程綁定晚缩,Vert.x通過這些實例來均衡負(fù)載。
從Callbacks到Observables
如前面幾節(jié)所講甘磨,Vert.x開發(fā)模式使用回調(diào)方法橡羞。在組織管理多個異步動作時,這種基于回調(diào)的開發(fā)模式容易產(chǎn)生復(fù)雜的代碼济舆。例如卿泽,我們來看一下我們怎樣從數(shù)據(jù)庫中查詢出數(shù)據(jù)。首先滋觉,我們需要和數(shù)據(jù)庫簡歷連接签夭,然后將查詢請求發(fā)送給數(shù)據(jù)庫,接著處理返回結(jié)果椎侠,最后釋放連接第租。所有的這些步驟都應(yīng)該是異步的,使用回調(diào)函數(shù)和Vert.x的JDBC客戶端我纪,你會寫出類似下面這段代碼:
雖然還可以維護(hù)慎宾,但是這個例子顯示出回調(diào)函數(shù)會讓代碼迅速變得不可讀丐吓。你還可以使用Vert.x的Future來處理異步操作。和Java的Future不同趟据,Vert.x的Fureture是非阻塞的券犁。Future提供高層的操作來構(gòu)建一系列的動作或并發(fā)處理動作。典型地汹碱,就像上面的例程一樣粘衬,我們使用future來構(gòu)建一系列的異步動作:
雖然Future使得代碼更加有自說明性,但是我們把所有數(shù)據(jù)行一次查詢出然后再做處理咳促。這樣會導(dǎo)致查詢結(jié)果巨大并且查詢耗時嚴(yán)重稚新。同時,你也不需要等所有數(shù)據(jù)查詢出來之后才進(jìn)行處理跪腹,我們可以拿到一行數(shù)據(jù)就處理一行褂删。幸運(yùn)的是,Vert.x提供了解決這個開發(fā)難題的答案冲茸,給你一套使用reactive編程開發(fā)模型來實現(xiàn)reactive微服務(wù)的方法笤妙。Vert.x提供RxJava API:
- 結(jié)合和協(xié)調(diào)異步任務(wù)
- 以輸入流的方式響應(yīng)接收的消息
我們使用RxJava API重寫上面這段代碼:
為了提高可讀性,reactive編程允許你訂閱結(jié)果的stream噪裕,在它們準(zhǔn)備好時立刻處理。使用Vert.x你可以自行選擇開發(fā)模型股毫。在本書中膳音,我們同時使用回調(diào)函數(shù)和RxJava.
讓我們開始Coding!
是時候開始動手實踐了铃诬。我們使用Apache Maven和Vert.x的Maven插件來開發(fā)第一個Vert.x應(yīng)用祭陷,但是你可以使用你喜歡的任何工具(Gradle,Apache Maven,Apache Ant). 你可以在代碼目錄找到不同的例子(在packaging-examples目錄中)。本節(jié)中展示出的代碼都來自其中的hello-vertx目錄趣席。
創(chuàng)建項目
創(chuàng)建一個叫my-first-vertx-app的目錄并且打開它:
mkdir my-first-vertx-app
cd my-first-vertx-app
然后兵志,執(zhí)行下面的命令:
mvn io.fabric8:vertx-maven-plugin:1.0.5:setup
-DprojectGroupId=io.vertx.sample
-DprojectArtifactId=my-first-vertx-app
-Dverticle=io.vertx.sample.MyFirstVerticle
這個命令會創(chuàng)建Maven項目結(jié)構(gòu),設(shè)置vertx-maven-plugin宣肚,并創(chuàng)建一個verticle類(io.vertx.sample.MyFirstVeticle)想罕,不過這個類是空的。
寫你的第一個Verticle
現(xiàn)在你可以開始寫你第一個verticle的代碼了霉涨。修改類 src/main/java/io/vertx/sample/MyFirstVerticle.java 為下面的內(nèi)容:
執(zhí)行命令運(yùn)行這個應(yīng)用:
mvn compile vertx:run
如果一切正常的話按价,你可以在瀏覽器中打開 http://localhost:8080 看到你的應(yīng)用。vertx:run命令會運(yùn)行Vert.x應(yīng)用并且監(jiān)控代碼的修改笙瑟。因此楼镐,如果你編輯了代碼,應(yīng)用會自動重新編譯并且啟動往枷。
下面我們看一下這個應(yīng)用的輸出:
Hello from vert.x-eventloop-thread-0
這個請求被消息循環(huán)線程0所處理框产。你可以嘗試發(fā)出更多的請求凄杯,它們都會被同樣的線程處理,以確保Vert.x的并發(fā)模型秉宿。使用Ctrl+C可以停止應(yīng)用戒突。
使用RxJava
到這個時候,我們可以看一下Vert.x提供的RxJava支持蘸鲸,以便更好的了解它妖谴。在你pom.xml文件中,添加如下的依賴:
然后酌摇,我們將<vertx.verticle>屬性改為io.vertx.sample.MyFirstRXVerticle膝舅。這個屬性告訴Vert.x Maven plugin哪一個verticle是程序的入口。接著創(chuàng)建新的類 io.vertx.sample.MyFirstRXVerticle窑多,并輸入如下的代碼:
Vert.x API中的RxJava變量都來自帶有rxjava字樣的包仍稀。RxJava方法都會有rx作為前綴,例如rxListen埂息。這些API還添加了一些方法來提供Observable實例技潘,通過它們你可以訂閱接收數(shù)據(jù)。
將你的應(yīng)用打包成Fat Jar
Vert.x Maven plug-in 將應(yīng)用打包到一個fat jar中千康。打包之后享幽,你可以簡單得執(zhí)行 java -jar <name>.jar 來運(yùn)行應(yīng)用:
mvn clean package
cd target
java -jar my-first-vertx-app-1.0-SNAPSHOT.jar
應(yīng)用又被啟動,監(jiān)聽HTTP請求拾弃。使用Ctrl+C可以關(guān)閉它值桩。
作為一個工具箱,Vert.x并不偏向哪一個打包方式豪椿,你可以自由選擇你所喜歡的奔坟。例如,你可以使用fat jar搭盾,也可以引入其他目錄作為library咳秉,將應(yīng)用放入一個war包之中,或者在其他程序中調(diào)用鸯隅。
在本書中澜建,我們將會使用fat jar——包含了應(yīng)用的代碼、資源蝌以,以及所有的依賴霎奢;包括Vert.x和其依賴。這種打包方式使用flat class加載機(jī)制饼灿,可以讓我們更容易理解應(yīng)用的啟動幕侠、依賴順序,以及日志碍彭。更重要的是晤硕,它可以幫助減少運(yùn)行程序所需要提前安裝的那些模塊悼潭。你不需要將應(yīng)用部署在已有的應(yīng)用服務(wù)器上。一旦打包成fat jar舞箍,你只要簡單的java -jar <name>.jar命令即可在任何地方運(yùn)行應(yīng)用舰褪。Vert.x Maven plug-in可以幫你構(gòu)建fat jar,不過你也可以使用其他的Maven plug-in疏橄,例如maven-shader-plugin.
日志占拍、監(jiān)控,及其他生產(chǎn)元素
使用fat jar是一種很棒的打包方式捎迫,它簡化了微服務(wù)和其他應(yīng)用的部署和運(yùn)行晃酒。但是,那些應(yīng)用服務(wù)器提供的已有的生產(chǎn)環(huán)境工具呢窄绒?通常贝次,我們期望能夠產(chǎn)生并收集日志,監(jiān)控應(yīng)用彰导,增加額外配置蛔翅,健康檢查等等。
不要擔(dān)心——Vert.x提供了所有這些功能位谋。而且由于Vert.x是中立的山析,因此它提供了多種方案供你選擇使用。例如掏父,Vert.x并不推行某一種日志框架盖腿,而是任你使用自己所喜歡的,例如Apache Log4j 1/2损同,SLF4j,甚至JUL(the JDK logging API)鸟款。如果你對Vert.x所log的消息感興趣膏燃,你可以為Vert.x內(nèi)部的日志工具選擇配置成上述的任意一種。Vert.x應(yīng)用的監(jiān)控是通過JMX來完成的何什。Vert.x Dropwizard Metric模塊將Vert.x的監(jiān)控數(shù)據(jù)提供給JMX组哩。你也可以自己選擇一種監(jiān)控服務(wù)來處理這些數(shù)據(jù),例如Prometheus(https://prometheus.io/) 处渣,或CloudForms(https://www.redhat.com/en/technologies/management/cloudforms) 伶贰。
總結(jié)
這一章我們學(xué)習(xí)了reactive微服務(wù)和Vert.x相關(guān)的知識。并且你還創(chuàng)建了你第一個Vert.x應(yīng)用罐栈。這一章并不是一個全面的閱讀指南黍衙,只是快速地給你介紹了核心的概念。如果你想深究這些問題荠诬,可以參考下面的資源:
Reactive programming vs. Reactive systems
The Reactive Manifesto
RxJava website
Reactive Programming with RxJava
The Vert.x website