微服務(wù)的確不是一個新事物耻涛。它出現(xiàn)于19世紀(jì)70的研究,近來變得注目瘟檩,因為微服務(wù)是更快遷移抹缕、傳遞值更容易、提升更敏捷的途徑墨辛,然而歉嗓,微服務(wù)源于Actor模型的系統(tǒng)和服務(wù)設(shè)計,動態(tài)的背蟆、自治的系統(tǒng)鉴分,領(lǐng)域驅(qū)動設(shè)計,以及分布式系統(tǒng)带膀。有條理的微服務(wù)模塊設(shè)計必然引領(lǐng)開發(fā)者創(chuàng)建分布式系統(tǒng)志珍。我相信您注意到了,分布式系統(tǒng)是難做垛叨。它易出錯伦糯,它慢,它受約束于CAP和FLP理論嗽元。另一方面敛纲,它構(gòu)建和維護(hù)都很復(fù)雜。響應(yīng)式起源于此剂癌。
但是什么是響應(yīng)式? 目前響應(yīng)式是一個極度被使用的名詞淤翔。牛津詞典定義響應(yīng)式是”對刺激作出響應(yīng)”,因此旁壮,響應(yīng)式軟件基于它接受到刺激作出響應(yīng)监嗜、調(diào)節(jié)它的行為裁奇。然而刽肠,這個定義刺激出的響應(yīng)是編程方面的挑戰(zhàn)音五,因為計算流不是被程序員控制而是被刺激。在這個章節(jié)中惶傻,我們將看看Vert.X怎樣在響應(yīng)式上幫助你:
. 響應(yīng)式編程:一個聚焦于訂閱數(shù)據(jù)流励翼、響應(yīng)變化并傳播的開發(fā)模型
. 響應(yīng)式系統(tǒng):一種基于異步消息傳遞的構(gòu)建響應(yīng)式汽抚、健壯的分布式系統(tǒng)的構(gòu)架風(fēng)格
響應(yīng)式微服務(wù)是響應(yīng)式微服務(wù)系統(tǒng)的構(gòu)成塊苗桂,然而,因為異步的特征氨肌,這些微服務(wù)的實現(xiàn)是有挑戰(zhàn)性的考婴。響應(yīng)式編程減少了這種復(fù)雜性。它怎樣做到的?讓我們現(xiàn)在回答這個問題。
響應(yīng)式編程是一個面向數(shù)據(jù)流和數(shù)據(jù)傳播的開發(fā)模型。在響應(yīng)式編程里,刺激是在流中傳輸?shù)臄?shù)據(jù),這被稱為流(streams)谢床。有許多方式來實現(xiàn)響應(yīng)式編程展箱。在這本書中混驰,我們將采用
Reactive Extensions(http://reactivex.io/),在這里皂贩,流(streams)被稱為observables栖榨,消費者訂閱這些observables并作出響應(yīng)。
為了讓這些概念沒那么抽象先紫,讓我們看一個RxJava (https://github.com/ReactiveX/RxJava)的例子治泥,RxJava是一個用Java實現(xiàn)了Reactive Extensions的庫包。這些例子被放在代碼倉庫的reactive-programming目錄下:
observable.subscribe(
data-> { // onNext
System.out.println(data);
},
error -> { // onError
error.printStackTrace();
},
() -> { // onComplete
System.out.println("No more data");
}
);
在這一小段里遮精,代碼是訂閱一個Observables居夹,當(dāng)值在流中傳輸時被通知。訂閱者能夠收到3種類型的事件:當(dāng)一個新值出現(xiàn)時onNext被call本冲,當(dāng)流中發(fā)生一個錯誤或者一個步驟拋出一個異常時onError被call准脂,當(dāng)流抵達(dá)末尾時onComplete回調(diào)被請求,對于一個無限的流檬洞,onComplete將不被觸發(fā)狸膏。RxJava包含了產(chǎn)生、轉(zhuǎn)換添怔、協(xié)同Observables湾戳,比如map轉(zhuǎn)換一個值為另一個值,flatMap產(chǎn)生一個Observables或者另一個異常操作鏈:
// sensor is an unbound observablepublishing values.
sensor
// Groups values 10 by 10, and produces anobservable
// with these values.
.window(10)
// Compute the average on each group
.flatMap(MathObservable::averageInteger)
// Produce a json representation of theaverage
.map(average -> "{'average': "+ average + "}")
.subscribe(
data -> {
System.out.println(data);
},
error -> {
error.printStackTrace();
}
);
RxJava v1.x定義了下面的流類型:
Observable: 預(yù)期包含一個值序列的有界的或無界的流
Single:是包含單一值的流广料,通常預(yù)期一個操作的結(jié)果砾脑,類似于future或promise
Completable:是不包含值但包含一個指示是否一個操作成功或失敗的流
RxJava2
RxJava2最近被release了,這本書仍然使用RxJava1.x艾杏。 RxJava2.x提供了相似的概念韧衣。RxJava2
增加了兩個新的流類型。舊的Observable不支持背壓(back-pressure),而Flowable則是支持背壓的Observable畅铭。RxJava2也引進(jìn)了Maybe類型氏淑,一個包含0或1個item、或者是1個錯誤的流類型硕噩。
用RxJava我們能做什么假残?舉個例子,我們能夠描述異步操作序列并編排它們榴徐。讓我們假設(shè)你想下載一個文檔守问,處理然后上傳它匀归。下載和上傳操作是異步的坑资。開發(fā)這個序列,你使用這樣的代碼:
// Asynchronous task downloading a document
Future downloadTask = download();
// Create a single completed when thedocument is downloaded.
Single.from(downloadTask)
// Process the content
.map(content -> process(content))
// Upload the document, this asynchronous operation
// just indicates its successful completion or failure.
.flatMapCompletable(payload -> upload(payload))
.subscribe(
() -> System.out.println("Documentdownloaded, updated and uploaded"),
t -> t.printStackTrace()
);
你也能夠編排異步任務(wù)穆端。舉個例子袱贮,合并兩個異步操作的結(jié)果,你用zip操作合并兩個不同流的值:
// Download two documents
Single downloadTask1 =downloadFirstDocument();
Single downloadTask2 =downloadSecondDocument();
// When both documents are downloaded,combine them
Single.zip(downloadTask1, downloadTask2,
(doc1, doc2) -> doc1 + "\n" +doc2)
.subscribe(
(doc) -> System.out.println("Documentcombined: " + doc),
t ->t.printStackTrace()
);
這些操作的運行給你超強能力:你能夠以一種聲明式的優(yōu)雅的方式來協(xié)同異步任務(wù)和數(shù)據(jù)流体啰。這與響應(yīng)式微服務(wù)有何關(guān)系攒巍?為了回答這個問題,讓我們看看響應(yīng)式系統(tǒng)荒勇。
響應(yīng)流(Reactive Streams)
你可能聽說過響應(yīng)流(http://www.reactive-streams.org/)柒莉。響應(yīng)流是一個倡議,為支持背壓的異步流處理提供一個標(biāo)準(zhǔn)沽翔。它提供了一個最小的接口和協(xié)議集合兢孝,來描述實現(xiàn)支持非阻塞背壓的異步流數(shù)據(jù)的操作和實體。它不定義操縱流的操作仅偎,主要用在互操作層跨蟹。這個倡議被Netflix, Lightbend, and Red Hat以及許多其他公司所支持。
響應(yīng)式系統(tǒng)(http://www.reactivemanifesto.org/)
響應(yīng)式編程成為一種開發(fā)模式后橘沥,響應(yīng)式系統(tǒng)是一種用來構(gòu)建分布式系統(tǒng)的架構(gòu)風(fēng)格窗轩。,為了達(dá)成響應(yīng)度座咆,構(gòu)建即使是失敗或負(fù)載下能對請求作出及時響應(yīng)的系統(tǒng)痢艺,這有一些準(zhǔn)則。
為了構(gòu)建這樣一個系統(tǒng)介陶,響應(yīng)式系統(tǒng)擁抱了消息驅(qū)動的途徑堤舒。所有的組件交互用消息異步地發(fā)送和接收。為了解耦發(fā)送方和接收方斤蔓,組件發(fā)送消息到虛擬地址植酥、注冊到虛擬地址來接收消息。一個地址是一個目標(biāo)地標(biāo)識,比如一個串或者一個URL友驮。多個接收者能注冊到同一個地址---傳送語義依賴于底層技術(shù)漂羊。發(fā)送方不阻塞等待一個響應(yīng)。發(fā)送方可以后面接收一個響應(yīng)卸留,但是同時走越,它能夠接收和發(fā)送其它消息。異步特征是非常重要的耻瑟、影響到你的應(yīng)用如何開發(fā)旨指。
異步的消息交互提供了響應(yīng)式系統(tǒng)兩個重要的特性:
. 彈性(Elasticity):水平擴(kuò)展的能力
. 可恢復(fù)性(Resilience):處理失敗和恢復(fù)的能力
彈性(Elasticity)來自于消息交互提供的解耦。送到一個地址的消息能被一組采用負(fù)載均衡策略的消費者消費喳整。當(dāng)一個響應(yīng)式系統(tǒng)面臨一個負(fù)載高峰時谆构,它擴(kuò)充新的消費者實例、隨后殺掉它們框都。
可恢復(fù)性(Resilience)是被非阻塞處理失敗搬素、以及復(fù)制組件的能力所提供的。首先魏保,消息交互允許組件處理本地失敗熬尺。感謝異步特性,組件不等候響應(yīng)谓罗,因此一個組件發(fā)生失敗時將不影響到其它組件粱哼。復(fù)制也是一個恢復(fù)處理的關(guān)鍵能力。當(dāng)一個節(jié)點處理消息失敗時檩咱,消息能夠被注冊在這個地址的其它節(jié)點處理揭措。
感謝這兩個特性,系統(tǒng)變得可響應(yīng)税手。它能夠適應(yīng)高的或者低的負(fù)載蜂筹,在出現(xiàn)高負(fù)載或失敗的情況下繼續(xù)提供服務(wù)。這些是構(gòu)建分布式微服務(wù)系統(tǒng)最基本的準(zhǔn)則芦倒。這是必需的艺挪,運行多個服務(wù)實例以均衡負(fù)載、處理失敗兵扬、不間斷可用性麻裳。在下一章節(jié),我們將看到Vert.X怎樣實現(xiàn)這些topic器钟。
響應(yīng)式微服務(wù)
構(gòu)建一個微服務(wù)系統(tǒng)津坑,每個服務(wù)能夠變化、演化傲霸、失敗疆瑰、變慢或者是在某個時刻處于離線眉反。這要求不影響到整個系統(tǒng)的表現(xiàn)。你的系統(tǒng)必須擁抱變化穆役、能夠處理失敗寸五。你可以在降級模式下運行,但是你的系統(tǒng)應(yīng)該仍然能夠處理請求耿币。
為了確保系統(tǒng)有如此表現(xiàn)梳杏,響應(yīng)式微服務(wù)系統(tǒng)由響應(yīng)式微服務(wù)構(gòu)成。這些微服務(wù)有4個特征:
. 自治(Autonomy)
. 異步(Asynchronisity)
. 可恢復(fù)(Resilience)
. 彈性(Elasticity)
響應(yīng)式微服務(wù)是自治的淹接。他們能夠適應(yīng)周邊的服務(wù)可用或不可用十性。然而,自治與隔離成對出現(xiàn)塑悼。響應(yīng)式微服務(wù)能夠處理本地失敗劲适、獨立行動、按需要與其它微服務(wù)進(jìn)行協(xié)作拢肆。一個響應(yīng)式微服務(wù)用異步的消息與其它微服務(wù)進(jìn)行交互减响。它也接收消息,有對消息作出響應(yīng)的能力郭怪。
感謝異步消息傳遞,響應(yīng)式微服務(wù)能夠應(yīng)付失敗刊橘,相應(yīng)地調(diào)節(jié)它的行為鄙才。失敗不應(yīng)當(dāng)被擴(kuò)散,而是應(yīng)該在靠近根源處被處理掉促绵。當(dāng)一個微服務(wù)跨掉時攒庵,微服務(wù)的消費者必須處理失敗、而不是擴(kuò)散它败晴。隔離準(zhǔn)則是一個關(guān)鍵特征浓冒,阻止失敗放大導(dǎo)致整個系統(tǒng)癱瘓〖饫ぃ可恢復(fù)性不僅是失敗管理稳懒,它也是自愈。一個微服務(wù)應(yīng)該實現(xiàn)當(dāng)失敗發(fā)生的時候的恢復(fù)或者補償策略慢味。
最后场梆,一個響應(yīng)式微服務(wù)必須是彈性的,這樣系統(tǒng)才能夠調(diào)節(jié)微服務(wù)的實例數(shù)以管理負(fù)載纯路。這意味著一些約束或油,比如避免內(nèi)存狀態(tài),多個微服務(wù)實例共享狀態(tài)(如果需要)驰唬,或者對于有狀態(tài)服務(wù)顶岸,能夠路由消息到同樣的微服務(wù)實例腔彰。
Vert.X是什么?
Vert.X是一個用異步的非阻塞的開發(fā)模式構(gòu)建響應(yīng)式的辖佣、分布式的系統(tǒng)的軟件包萍桌。因為它是一個軟件包而不是一個框架,你可以象別的庫包一樣使用Vert.X凌简。它不約束你怎樣去構(gòu)建或組織你的系統(tǒng)上炎,你按你所想的方式使用它。Vert.X是很靈活的雏搂,你能夠用它作為一個獨立的應(yīng)用藕施,或者是將它嵌入到一個大應(yīng)用里面去。
站在開發(fā)者的立場上看凸郑,Vert.X是一個JAR庫文件的集合裳食。每一個Vert.X模塊是一個JAR庫文件,你加它們至CLASSPATH芙沥。從HTTP 服務(wù)端和客戶端诲祸,到消息,到低水平協(xié)議比如TCP或UDP而昨,Vert.X提供了大量模塊來構(gòu)建你所想要的應(yīng)用救氯。你可以選擇任何模塊附加到Vert.X核心庫(Vert.x Core)來構(gòu)建你的系統(tǒng)。圖2-2展示了Vert.X生態(tài)系統(tǒng)的一個節(jié)選視圖:
Vert.X也提供了一個大的技術(shù)棧來幫助你構(gòu)建微服務(wù)系統(tǒng)歌憨。Vert.X推動了微服務(wù)的發(fā)展使得它變得流行着憨。它被設(shè)計和構(gòu)建以提供直觀的、強有力的方式構(gòu)建微服務(wù)系統(tǒng)务嫡。而且這不是全部甲抖。用Vert.X你能夠構(gòu)建微服務(wù)系統(tǒng)。當(dāng)用Vert.X構(gòu)建一個微服務(wù)時心铃,它貫徹了它的核心特性到微服務(wù)中:它完全是異步的准谚。
異步開發(fā)模式
所有用Vert.X構(gòu)建的應(yīng)用都是異步的。Vert.X應(yīng)用是事件驅(qū)動的去扣、非阻塞的柱衔。當(dāng)它關(guān)注的事件發(fā)生時,你的應(yīng)用被通知厅篓。讓我們看一個具體例子秀存。Vert.X提供了一個容易的方式創(chuàng)建一個HTTP Server,這個HTTP Server每次收到http請求時被通知:
vertx.createHttpServer()
.requestHandler(request -> {
// This handler will be called every time an HTTP
// request is received at the server
request.response().end("hello Vert.x");
})
.listen(8080);
在這個例子中羽氮,我們設(shè)置了一個請求處理器來接收http請求(事件)或链、返回hello Vert.x (響應(yīng))。一個處理器(Handler)是一個回調(diào)函數(shù)档押,當(dāng)一個事件發(fā)生時澳盐。在我們的例子中祈纯,每一個請求進(jìn)入時,handler的代碼被執(zhí)行叼耙。注意腕窥,一個handler不返回一個結(jié)果,可是筛婉,一個handler能夠提供一個結(jié)果簇爆。結(jié)果怎樣被提供,依賴于交互的類型爽撒。在最后的一小段里入蛆,它僅僅輸出結(jié)果到http response。這個handler被鏈到一個socket請求監(jiān)聽器硕勿,向這個http endpoint發(fā)出請求哨毁,生成一個簡單的http響應(yīng):
HTTP/1.1 200 OK
Content-Length: 12
hello Vert.x
幾乎沒有例外,在Vert.X里源武,沒有API會阻塞發(fā)起調(diào)用的線程扼褪。如果一個結(jié)果能夠被立刻提供,它就被返回粱栖,否則话浇,一個處理器(handler)被用來接收稍后的事件。當(dāng)一個被處理事件就緒或者當(dāng)一個異步操作完成運算后查排,處理器被通知凳枝。
在傳統(tǒng)的急切編程中,你將像這樣寫代碼:
int res =compute(1, 2);
在這個代碼里跋核,你等待方法的結(jié)果。當(dāng)切換至異步的非阻塞的開發(fā)模式時叛买,你傳遞一個處理器(handler)砂代,當(dāng)結(jié)果就緒時它被請求:
compute(1, 2, res -> {
// Called with the result
});
在最后一小段里,compute不再返回結(jié)果率挣,因此你不用等到結(jié)果被計算并返回刻伊,你傳遞了一個處理器,當(dāng)結(jié)果就緒時它被回調(diào)椒功。
感謝非阻塞的開發(fā)模式捶箱,你能夠用很少的線程來處理高并發(fā)的負(fù)載。在大多數(shù)情況下动漾,Vert.X用一個稱之為事件輪詢(event loop)的線程來調(diào)用你的處理器(handler)丁屎。事件輪詢在圖2-3中描繪,它消費一個事件隊列旱眯,分發(fā)每個事件給相關(guān)的處理器(handler):
事件輪詢所提倡的線程模型有巨大的好處:它使并發(fā)簡單化晨川。因為這只有一個線程证九,你總是被同一個線程調(diào)用、決不會同時被調(diào)用共虑。然而愧怜,它有一個很重要的法則,你必須遵從:
不要阻塞事件輪詢 --- Vert.X黃金法則
因為沒有阻塞妈拌,一個事件輪詢能夠在很短的時間里傳送大量事件拥坛,這被稱為響應(yīng)模式(https://en.wikipedia.org/wiki/Reactor_pattern)。
讓我們想像一下尘分,你打破了這個法則猜惋。在前面的代碼片段里,請求處理器總是被同一個事件輪詢調(diào)用音诫,因此惨奕,如果http請求處理阻塞、而不是立即返回竭钝,其它請求將不能被及時處理梨撞,被放至隊列中、等候事件輪詢線程被釋放香罐。你就失去了伸縮性以及Vert.X的效率優(yōu)勢卧波。那么什么會阻塞?第一個顯而易見的例子是JDBC數(shù)據(jù)庫訪問庇茫,它們自然是阻塞的港粱。長時間的運算也是阻塞的,例如旦签,計算Pi到200,000位小數(shù)點的計算肯定是阻塞的查坪。不用擔(dān)心剑肯,Vert.X也提供了處理阻塞的組件媒役。
在一個標(biāo)準(zhǔn)的響應(yīng)式實現(xiàn)里,只有單一的事件輪詢線程谜洽,在一個環(huán)里面?zhèn)魉褪录o事件處理器羔巢。單一事件輪詢線程的問題是:它只能運行在一個CPU核中望忆。Vert.X的機(jī)制有所不同,替代單一事件輪詢竿秆,每一個Vert.X實例維護(hù)幾個事件輪詢启摄,這被稱為多響應(yīng)模型,正如圖2-4所展示:
事件是被不同的事件輪詢器分發(fā)幽钢,然而歉备,一旦一個處理器(handler)被一個事件輪詢器執(zhí)行,它就總是被這個事件輪詢器調(diào)用搅吁,堅守響應(yīng)模式的并發(fā)優(yōu)勢威创。像圖2-4落午,你有多個事件輪詢器,它能夠均衡負(fù)載到不同的CPU核肚豺。在我們的http例子里溃斋,它怎樣工作?一旦Vert.X注冊了socket監(jiān)聽器吸申,分發(fā)請求到不同的事件輪詢器梗劫。
Verticles --- 構(gòu)建塊
Vert.X在怎樣塑造應(yīng)用和編碼上給了你大量自由。但是截碴,它也提供了積木梳侨,讓你更容易開始寫Vert.X應(yīng)用,伴隨著一個簡單的日丹、可伸縮的走哺、類actor開發(fā)以及開箱即用的并發(fā)模型。Verticles是Vert.X部署和運行的代碼塊哲虾。一個應(yīng)用丙躏,比如微服務(wù),典型地是同一時間運行在同一個Vert.X實例里面的多個Verticles實例構(gòu)成束凑。典型地晒旅,一個Verticle創(chuàng)建服務(wù)器或客戶端,注冊一組處理器汪诉,封裝部分系統(tǒng)的業(yè)務(wù)邏輯废恋。
規(guī)范的Verticles是被Vert.X事件輪詢器執(zhí)行、決不阻塞扒寄。Vert.X確保每個verticle總是被同一個線程執(zhí)行且決不并發(fā)鱼鼓,因此避免了同步塊。在Java里该编,一個verticle是一個擴(kuò)展Abstract
Verticle的類:
importio.vertx.core.AbstractVerticle;
public classMyVerticle extends AbstractVerticle {
@Override
public void start() throws Exception {
// Executedwhen the verticle is deployed
}
@Override
public void stop() throws Exception {
// Executedwhen the verticle is un-deployed
}
}
工作者Verticle
不像規(guī)范的verticle蚓哩,工作者verticles不是在事件輪詢器上執(zhí)行,這意味著它們能夠執(zhí)行阻塞代碼上渴,然而,這限制了可伸縮性喜颁。
Verticles訪問vertx成員(由AbstractVerticle類提供)來創(chuàng)建服務(wù)器和客戶端稠氮,與其它verticles交互。Verticles也可以部署其它Verticles半开,配置它們隔披,設(shè)置創(chuàng)建的實例數(shù)量。實例是被關(guān)聯(lián)到不同的事件輪詢器(實現(xiàn)多響應(yīng)模型)寂拆,Vert.X在這些實例中均衡負(fù)載奢米。
從回調(diào)(callback)到訂閱(Observables)
正如前一章節(jié)所看到的抓韩,Vert.X開發(fā)采用了回調(diào)模式。當(dāng)編排多個異步操作時鬓长,基于回調(diào)的開發(fā)模式導(dǎo)致產(chǎn)生復(fù)雜的代碼谒拴。例如,讓我們看看怎樣從數(shù)據(jù)庫查詢數(shù)據(jù)涉波。首先英上,我們需要連接,然后我們給數(shù)據(jù)庫發(fā)一個查詢啤覆,處理結(jié)果苍日,釋放數(shù)據(jù)庫連接,所有這些操作是異步的窗声。用回調(diào)相恃,你將用Vert.X JDBC客戶端寫下面的代碼:
client.getConnection(conn -> {
???????? if(conn.failed()) {/* failure handling */}
???????? else{
?????????????????? SQLConnectionconnection = conn.result();
?????????????????? connection.query("SELECT* from PRODUCTS", rs -> {
??????????????????????????? if(rs.failed()) {/* failure handling */}
??????????????????????????? else{
???????????????????????????????????? Listlines =
???????????????????????????????????? rs.result().getResults();
???????????????????????????????????? for(JsonArray l : lines) {
?????????????????????????????????????????????? System.out.println(newProduct(l));
???????????????????????????????????? }
???????????????????????????????????? connection.close(done-> {
?????????????????????????????????????????????? if(done.failed()) {/* failure handling */}
?????????????????????????????????????????????? });
??????????????????????????? }
?????????????????? });
???????? }
});
這個例子展示了回調(diào)會很快導(dǎo)致代碼難以閱讀。你也可以用Vert.X Future來處理異步操作笨觅,不象Java
Future拦耐,Vert.X是非阻塞的。Future提供了高層次的操作組合:構(gòu)建順序操作或是并行地執(zhí)行動作屋摇。典型地揩魂,正如下面的代碼片段所展示的,我們組合future以構(gòu)建順序執(zhí)行的異步操作:
Future future =getConnection();
future.compose(conn -> {
???????? connection.set(conn);
???????? //Return a future of ResultSet
???????? returnselectProduct(conn);
})
// Return a collection of products bymapping
// each row to a Product
.map(result ->toProducts(result.getResults()))
.setHandler(ar -> {
???????? if(ar.failed()) { /* failure handling */ }
???????? else{
?????????????????? ar.result().forEach(System.out::println);
???????? }
???????? connection.get().close(done-> {
?????????????????? if(done.failed()) { /* failure handling */ }
???????? });
});
Futures使得代碼更明了炮温,我們在一個批次里查詢所有行并處理它們火脉。結(jié)果是巨大的,檢索耗費了許多時間柒啤。同時倦挂,開始處理結(jié)果,你并不需要全部結(jié)果担巩。我們能夠只要一獲得每一行數(shù)據(jù)就處理它方援。幸運地,Vert.X提供了這種開發(fā)模式了應(yīng)對之策涛癌,提供你一種用響應(yīng)式編程開發(fā)模型實現(xiàn)響應(yīng)式微服務(wù)的途徑犯戏。Vert.X提供了RxJava API:
. 聯(lián)合和協(xié)同異步任務(wù)
. 作為一個輸入流對輸入消息來響應(yīng)
讓我們用RxJava API來重寫前面的代碼:
// We retrieve a connection and cache it,
// so we can retrieve the value later.
Single connection =client.rxGetConnection();
connection
.flatMapObservable(conn ->
???????? conn
???????? //Execute the query
???????? .rxQueryStream("SELECT* from PRODUCTS")
???????? //Publish the rows one by one in a new Observable
???????? .flatMapObservable(SQLRowStream::toObservable)
???????? //Don't forget to close the connection
???????? .doAfterTerminate(conn::close)
)
// Map every row to a Product
.map(Product::new)
// Display the result one by one
.subscribe(System.out::println);
除提升了可讀性外,響應(yīng)式編程允許你訂閱一個結(jié)果流拳话、一獲得就馬上處理先匪。用Vert.X你能夠選擇你喜歡的開發(fā)模式,在這本書里弃衍,回調(diào)和RxJava二者我們都會使用呀非。
讓我們開始編碼
是時候讓你動動手了。我們打算用Apache Maven和Vert.X Maven插件來開發(fā)我們的第一個Vert.X應(yīng)用。然而岸裙,你可以用任何你想用的工具:Gradle, Apache Maven加其它的插件包猖败,或者Ant。你可以在代碼倉庫中找到不同的例子降允,在這一章節(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
這個命令生成了Maven工程的結(jié)構(gòu)拟糕,配置vertx-maven-plugin插件判呕,創(chuàng)建一個verticle類(io.vertx.sample.MyFirstVerticle),沒有做其它的送滞。
寫第一個Verticle
是時候?qū)懩愕牡谝粋€verticle的代碼了侠草。用下面的內(nèi)容修改src/main/java/io/vertx/sample/MyFirstVerticle.java文件:
package io.vertx.sample;
import io.vertx.core.AbstractVerticle;
/**
* A verticle extends the AbstractVerticleclass.
*/
public class MyFirstVerticle extendsAbstractVerticle {
???????? @Override
???????? publicvoid start() throws Exception {
?????????????????? //We create a HTTP server object
?????????????????? vertx.createHttpServer()
?????????????????? //The requestHandler is called for each incoming
?????????????????? //HTTP request, we print the name of the thread
?????????????????? .requestHandler(req-> {
??????????????????????????? req.response().end("Hellofrom " + Thread.currentThread().getName());
?????????????????? })
?????????????????? .listen(8080);// start the server on port 8080
???????? }
}
執(zhí)行下面命令,啟動這個應(yīng)用:
mvn compile vertx:run
如果一切順利犁嗅,你將能夠看到你的應(yīng)用在瀏覽器中被打開(http://localhost:8080)边涕。vertx:run目標(biāo)啟動Vert.x應(yīng)用,并且監(jiān)視代碼變化褂微。因此功蜓,如果你修改源代碼,應(yīng)用將被自動地重新編譯并重啟宠蚂。
讓我們現(xiàn)在看看應(yīng)用的輸出:
Hello from vert.x-eventloop-thread-0
請求被事件輪詢器0所處理式撼。你可以試試發(fā)出更多請求,請求將總是被同一個事件輪詢器處理求厕,強制的并發(fā)模型著隆。按CTRL+C組合鍵停止執(zhí)行。
使用RxJava
讓我們看一下Vert.X支持的RxJava呀癣,更好地理解它怎樣工作美浦。在你的pom.xml文件加上下面的依賴:
io.vertx
vertx-rx-java
接下來,修改屬性為io.vertx.sample.MyFirstRXVerticle项栏,這個屬性告訴Vert.X
Maven插件哪個verticle是應(yīng)用的起點浦辨。用下面的內(nèi)容創(chuàng)建新的verticle類(io.vertx.sample.MyFirstRXVerticle):
package io.vertx.sample;
// We use the .rxjava. package containingthe RX-ified APIs
importio.vertx.rxjava.core.AbstractVerticle;
importio.vertx.rxjava.core.http.HttpServer;
public class MyFirstRXVerticle extendsAbstractVerticle {
???????? @Override
???????? publicvoid start() {
?????????????????? HttpServerserver = vertx.createHttpServer();
?????????????????? //We get the stream of request as Observable
?????????????????? server.requestStream().toObservable()
?????????????????? .subscribe(req->
?????????????????? //for each HTTP request, this method is called
??????????????????????????? req.response().end("Hellofrom " + Thread.currentThread().getName())
?????????????????? );
?????????????????? //We start the server using rxListen returning a
?????????????????? //Single of HTTP server. We need to subscribe to
?????????????????? //trigger the operation
?????????????????? server
?????????????????? .rxListen(8080)
?????????????????? .subscribe();
???????? }
}
Vert.X的RxJava API在包名上用rxjava,方法名以rx打頭沼沈,比如rxListen流酬。另外,API被增強列另,方法提供Observable對象康吵,你可以訂閱、以接收傳輸數(shù)據(jù)访递。
打包你的應(yīng)用成一個Fat Jar
Vert.X Maven插件打包應(yīng)用成一個Fat Jar。一旦打完包同辣,你可以很容易啟動應(yīng)用拷姿,用java –jar .jar命令:
mvn clean package
cd target
java -jar my-first-vertx-app-1.0-SNAPSHOT.jar
應(yīng)用將啟起來惭载,監(jiān)聽指定端口的http請求,鍵CTRL+C停止它响巢。
作為一個不固步自封的軟件包描滔,Vert.X不推從一種打包方式超過另一種---你可以自由地選擇你喜歡的打包方式。例如踪古,你可以用fat jar, 或者把應(yīng)用嵌入到一個war文件含长。
在這本書中,我們采用fat jar伏穆,自包含的jar拘泞,嵌入應(yīng)用代碼、資源枕扫、以及它所依賴的一切陪腌,這包括Vert.X,你所用的Vert.X組件和他們的依賴。這個包模型采用扁平的類加載機(jī)制烟瞧,這使得更容易理解應(yīng)用的啟動诗鸭、依賴順序、以及日志参滴。更重要的强岸,它有助于減少需要安裝進(jìn)產(chǎn)品的可移動塊數(shù)量。你不需要部署應(yīng)用到一個存在的應(yīng)用服務(wù)器砾赔。一旦它被打包成fat jar蝌箍,應(yīng)用可以用一個簡單的java –jar 命令開始運行。
Vert.X Maven插件為你構(gòu)建fat jar过蹂,但是你也可以用別比如maven-shader-plugin十绑。
日志,監(jiān)控酷勺,以及其它的產(chǎn)品要素
因為部署和啟動簡單本橙,對于微服務(wù)和其它類型的應(yīng)用,fat jar是一個很好的打包方式脆诉。但是應(yīng)用服務(wù)器提供了哪些特性讓你的應(yīng)用作為產(chǎn)品而就緒甚亭?典型地,我們希望能夠?qū)懞褪占罩净魇ぁ⒈O(jiān)控應(yīng)用亏狰、推送外部的配置、健康檢查等等偶摔。
不用擔(dān)心暇唾,Vert.X提供了所有這些特性。因為Vert.X是中立的,它提供多種選擇策州,讓你挑選或是實現(xiàn)你自己的瘸味。例如,對于日志够挂,Vert.X不推行一個指定的日志框架而是允許你使用任何你想用的日志框架旁仿,比如Apache Log4J 1或者2,SLF4J孽糖,或者甚至是JUL(JDK的日志API)枯冈。如果你對Vert.X它自己的日志有興趣,Vert.X內(nèi)部的日志能夠配置成這些日志框架中的任何一個办悟。監(jiān)控Vert.X應(yīng)用通常是用JMX實現(xiàn)尘奏。Vert.x Dropwizard Metric模塊提供了基于JMX的Vert.X度量。你也可以選擇發(fā)布這些度量到一個監(jiān)控服務(wù)誉尖,比如Prometheus(https://prometheus.io/)或者是CloudForms(https://www.redhat.com/en/technologies/management/cloudforms)罪既。
總結(jié)
在這一章節(jié)里,我們學(xué)習(xí)了響應(yīng)式微服務(wù)以及Vert.X铡恕。你也創(chuàng)建了你的第一個Vert.X應(yīng)用琢感。這個章節(jié)并不是一個全面的指導(dǎo),僅僅提供了對主要概念的一個快捷介紹探熔。如果你想更深地了解這些topic驹针,查看下面的資源:
. 響應(yīng)式編程和響應(yīng)式系統(tǒng)
? https://www.oreilly.com/ideas/reactive-programming-vs-reactive-systems
. 響應(yīng)式宣言
? http://www.reactivemanifesto.org/
. RxJava網(wǎng)站
??https://github.com/ReactiveX/RxJava/wiki
. 用RxJava做響應(yīng)式編程
??http://shop.oreilly.com/product/0636920042228.do
. Vert.X網(wǎng)站
? https://vertx.io/