一直以來早有將這些年用Vert.x的經驗整理一下的想法,奈何天生不是勤快人,直到最近扶牆老師問起,遂成此文。前端
如今想一想,咱們應該算是國內用Vert.x的最先一批人,版本大概是1.2.x吧,當時Vert.x內置了一個比較坑爹的模塊系統,看似不錯,但其實很坑爹。但即便這樣,咱們當時仍是在技術選型上採用了它。理由大體以下:java
因而乎,它順利成章地成爲了咱們當時系統接入層的中流砥柱,在實踐中也確實發揮了很好的做用。node
鑑於Vert.x當前的版本是3.3.3,所以本文的內容也主要針對這個版本而言,一些咱們遇到而且已經修復的bug,也就不會也沒有必要在此囉嗦了。git
此外,本文也不是入門文檔,而是爲了預防陷坑而給出的指導意見,故在閱讀本文以前還請先仔細閱讀Vert.x的文檔。程序員
雖然Vert.x的一大亮點號稱是支持「多語言」,即同一個工程內能夠同時用Java、Groovy、Javascript等不一樣語言編寫Verticle,但我仍是建議採用Java爲主,最多輔以Groovy。緣由是:我發現不少新出的Vert.x模塊仍是對Java支持最好,對於其餘的則就至關通常了,起碼不會讓你感受特地針對這個語言而開發的。加上原本Java 8以後支持lambda,Java程序員的苦逼生活其實已經改善很多。github
在dgate中,我主要採用Java + Groovy的方式,二者分工也很明確:前者用於數據處理,後者則用於DSL和數據類。apache
此時,因爲混用了二者,而且可能會出現Groovy類要用到程序中Java類的狀況,那麼就要用到joint compile。在build.gradle中須要配置以下:編程
sourceSets.main.java.srcDirs = [] sourceSets.main.groovy.srcDirs += ["src/main/java"]
即,將Java類也交由Groovy編譯器來編譯。服務器
雖然Vert.x能夠內嵌到其餘框架中,但在實際項目上我仍是偏心單獨部署,項目的構建方式則爲:gradle + fatjar。具體例子,能夠參見這個build.gradle文件。併發
我在Vert.x郵件組中常常看到有新人問關於Vert.x的組織方式,其實這是沒有理解Vert.x的本質:Verticle。Verticle可視做Vert.x的一個最小部署和運行單元,簡單的說,可類比爲Servlet。所以,整個應用能夠這樣來劃分:
前二者負責初始化,Verticle則相似Servlet同樣等待被觸發(來自TCP/Eventbus/HTTP的Request),在實際處理時會調用到其餘類。
這也就是爲什麼在上面的build.gradle中有這樣關鍵的兩行的緣由:
manifest { attributes 'Main-Class': '……' attributes 'Main-Verticle': '……' }
Vert.x默認支持JUL,對於其餘Logging框架也有支持。但我嫌每次運行要敲那麼多命令很煩,那麼能夠在Launcher中強制設置環境變量:
System.setProperty("vertx.logger-delegate-factory-class-name", "io.vertx.core.logging.SLF4JLogDelegateFactory");
可參見dgate的Launcher代碼。
跟Servlet相似,多個Verticle之間也會有依賴關係,存在前後部署的須要。
對於單個Verticle之間的依賴,如A依賴B,很簡單,利用deployVerticle的回調就很好解決。由於代碼簡單,這裏就再也不單獨列出,仍是那句話,看文檔。
對於依賴多個Verticle,如A依賴B和C,則須要有點技巧了:
private void deployVerties(List<Map> verticles, Closure completeHandler = null) { AtomicInteger count = new AtomicInteger(0) verticles.each { verticle -> vertx.deployVerticle(verticle.name, verticle.option ?: [:]) { result -> if (result.succeeded()) { if (count.incrementAndGet() == verticles.size()) { if (completeHandler) { completeHandler.call() } } } else { exit(verticle.name, result.cause()) } } } }
看到Atom對象,你是否以爲也能夠採用CountDownLatch對象?很不幸,不行。我當時作過嘗試,整個代碼立馬被Block住,直到我按了Ctrl-C。緣由在於:Block住了EventLoop。
至於deployVerticle(),它能夠接受字符串和類實例。當使用字符串時,如果非Java類,如Groovy,須要採用這樣的格式:"語言前綴:類全限定名"。如:
'groovy:hawkeyes.rtds.processor.MailMan'
此外,部署的Verticle實例並不是越多越好,還跟CPU的核數相關。
Vert.x應用最忌諱Blocking操做,對此有多種處理:
凡是涉及IO的操做,都請考慮一下。
EventBus至關於Vert.x應用的神經系統,但有幾點須要注意:
嚴格來說,3.2以後,上述第一點並不徹底正確。這兩個Verticle之間能夠採用TCP EventBusBridge來進行通訊,具體參見這篇文章。
Cluster是當時我選擇Vert.x的一個重要考量,並且將Vert.x應用單獨打成fatjar還有一個附帶好處就是Vert.x的cli均可以直接使用,其中就包括cluster命令。
Vert.x的集羣創建在Hazelcast之上,除了集羣調度,它自己還能作內存存儲,即具有了Redis的主要功能。而且查詢語法也比Redis(2.x)的要靈活,支持類SQL語法。更重要的是,其ReadThrough特性讓人慾罷不能,簡化了編程。固然,還包括其餘如分佈式鎖、隊列、任務等等。
所謂ReadThrough,即「若內存中沒有,則查詢將下傳到下一級(一般是DB)」。Hazelcast的ReadThrough可經過實現MapLoader接口來實現。這個例子很簡單,故可查看Hazelcast的文檔瞭解。這裏重點講一下如何在Vert.x中去配置,由於Vert.x沒有對此提供直接支持。
首先,cluster.xml即爲一個標準的Hazelcast配置文件,故可在此配置相應的MapLoader便可:
<map name="map_name"> <map-store enabled="true"> <class-name>xxxLoader</class-name> </map-store> </map>
在從未給集羣Map賦過值且第一次運行下列代碼時,注意兩個名字要相同,則觸發ReadThrough:
vertx.sharedData().getClusterWideMap("map_name") {……}
若是想在Vert.x中得到Hazelcast實例,則能夠直接使用下面代碼:
Set<HazelcastInstance> instances = Hazelcast.getAllHazelcastInstances() hz = instances.first()
這樣即可利用Hazelcast的其餘功能。在3.3.3以後,Vert.x集羣支持Ignite,它是比Hazelcast更強大的內存計算工具。並且,在Vert.x 3.4-beta1中已經再也不是技術預覽版,往後我確定會全面擁抱它。
Ignite/Hazelcast不像Redis那樣曝光率那麼高,但鑑於其自己都是老牌內存計算軟件,且在開源以前都在高強度生產環境(沒記錯的話是銀行系統)實戰演練過,同時對比一下二者之間的功能列表,你會發現這些工具其實更強大,尤爲是Ignite。它們的文檔都不錯,值得一看。
最後說一說Handler中須要注意的地方,它很是適合寫Restful API。
以前用Vert.x寫接入層代碼,主要集中在Core、Groovy和Shell部分。此次寫dgate,算是紮紮實實用了一下Web部分。至於歷史,我就不詳細說了,總之一句話:哥是看着它長大的,;)。
Handler其實很簡單,只須要注意幾點:
至於其餘,沒啥可說的,都很簡單。
最後,來句雞湯:遇坑不可怕,還得敢於嘗試方能有所收穫,但願對各位有幫助!