最開始以爲這個系列也就最多3篇了不得了(由於事不過三嘛),沒曾想竟然迎來了第四篇!html
因爲最近決定投身到區塊鏈的學習當中的緣故,出於更好的理解它的基本概念,本身動手參考文章寫了一個迷你區塊鏈的例子。採用了kotlin + vertx的工具選擇。此次嘗試再次驗證了我在本系列一開篇所說:建議以Java語言開發爲主。緣由很簡單,由於這個是基礎,因此各方面支持(包括文檔和功能方面)確定是Java語言優先。前端
在作這個區塊鏈的例子時,Vertx Kotlin的文檔讓我有極爲糟糕的體驗:從整篇文檔中,你找不到一個完整的用kotlin書寫Verticle的例子,閱讀的時候就感受內容有跳躍。雖然你能夠猜出應該是繼承AbstractVerticle,但你確定仍是但願文檔中明確指出來。java
固然啦,儘管有這樣的問題,寫代碼的體驗仍是不錯的。就build.gradle而言,跟Java + Groovy組合的差異不大。惟一須要注意的是,你可能須要將kotlin的jvmTarget設置爲「1.8」。具體的配置能夠參考工程的build.gradle。react
總之,發現文檔有問題,就先查Java文檔。git
上面的區塊鏈的例子由兩部分組成:前端靜態頁面 + 後端的Verticle,前端靜態頁面經過Ajax請求與後端的Verticle交互。這其實就是經過Vertx-Web的StaticHandler來實現的,很簡單。這裏只提兩個須要留心的小地方。github
首先,靜態資源的根路徑默認狀況下是:src/main/resources/webroot。即當你請求「http://localhost:8080/index.html」時,其實對應的是:src/main/resources/webroot/index.html。web
其次,目前的Web應用的URL不多會直接出現「……/xxx.html」。按照向Spring MVC或Grails這裏框架的作法,通常是通過一個action,而後將瀏覽器導向某個頁面。在作這個例子時,其實沒有這麼複雜的邏輯,讓瀏覽器直接去加載某個頁面(如configure.html)就能夠了。但這樣會出現一個讓人很不爽的Path:「/configure.html」,而其餘的路徑由於主要是負責處理Ajax請求,都是形如「/mine」這樣的路徑。sql
爲了統一路徑風格,這裏採用了一個小技巧:RoutingContext.reroute。參考代碼以下:數據庫
router.get("/configure").handler({ rc: RoutingContext -> rc.reroute("/configure.html") })
Vert.x JDBC client缺省的鏈接池提供者是c3p0,但它也支持其餘其餘的鏈接池,好比大名鼎鼎的Hikari。但遺憾的是,文檔中沒有給出一個完整的代碼示例。對於想換用其餘鏈接池的同窗,能夠參考下面的代碼:編程
JDBCClient.createShared(vertx, new JsonObject() .put('provider_class', 'io.vertx.ext.jdbc.spi.impl.HikariCPDataSourceProvider') .put('driverClassName', 'org.postgresql.Driver') .put('jdbcUrl', jdbcUrl) .put('username', username) .put('password', password) .put('maximumPoolSize', maximumPoolSize) .put('minimumIdle', minimumIdle) .put('cachePrepStmts', true) .put('prepStmtCacheSize', 250) .put('prepStmtCacheSqlLimit', 2048));
對於用Postgresql的同窗,還能夠看看Reactive Postgres Client,一個高性能的輕量級jdbc client同時自帶鏈接池,做者也是vertx的貢獻者。
使用Vert.x的最大好處就是極大簡化了多線程編程的複雜性,大部分時候你幾乎不須要去操心,這部份內容分別在文檔的Standard verticles和Worker verticles有描述。
但文檔中並無專門闡述這一原則是否對於回調函數也適用,畢竟回調函數執行的時機不肯定而且典型的Vert.x程序充斥着回調。對於這個問題,簡單地說:一樣適用。下面的示例代碼能夠驗證這一點:
public class Vert1 extends AbstractVerticle { long count = 0; @Override public void start() { HttpClient httpClient = vertx.createHttpClient(); for (int i = 0; i < 20; i++) { httpClient.getAbs("http://www.baidu.com/", response -> { count++; System.out.println(count); }).end(); } } }
從輸出來看,徹底正確。做爲對比,你能夠在groovyConsole中運行下面的代碼(多按幾回ctrl - r):
int count = 0 def c = { 10.times { count++ println count } } def t1 = new Thread(c) def t2 = new Thread(c) t1.start() t2.start()
而且,經過調研Vert.x源代碼,你能夠(在HttpClientRequestBase中)發現:
void handleResponse(HttpClientResponseImpl resp) { synchronized (getLock()) { // If an exception occurred (e.g. a timeout fired) we won't receive the response. if (exceptionOccurred == null) { long timeoutMS = currentTimeoutMs; cancelOutstandingTimeoutTimer(); try { doHandleResponse(resp, timeoutMS); } catch (Throwable t) { handleException(t); } } } }
很明顯,Vert.x內部已經爲你提早預防了,這就是框架的力量!若是你還在用Netty,不妨考慮Vert.x這個建構於它之上的高層工具吧。
Subrouter是個好東東,可API設計有個問題:只有mount,沒有unmount!通常狀況下,unmount的確用不上,但你一旦想實現動態路由時,它就是萬萬不可缺乏的了。
好在我本身摸索出了下面的方法:
public static void unMountSubRouter(Router router, String root) { router.getRoutes().stream() .filter(route -> route.getPath() != null && route.getPath().startsWith(root)) .forEach(route -> route.remove()); }
有趣的是,Vert.x的開發者曾經以爲subrouter用處不大,並動了把它在將來拿掉的念頭。當這個想法被提出來徵求社區意見時,立馬有人跳出來講:「subrouter的設計很是好,哥的程序嚴重依賴它,請繼續保留。」
我曾經不止一次看到初學者在問相似這樣的問題:
要回答這些問題,須要首先搞清楚幾個事實。
而且,經過觀察其餘人寫的Vert.x代碼(包括Vert.x本身的那些子項目),能夠總結出來幾個套路。
這是最多見的結構:
標準Verticle和Worker Verticle之間經過eventbus進行交互,整個架構其實也很簡單:
request <---> standard verticles <---> worker verticle
這裏的一個典型反模式,尤爲是初學者會大機率犯的錯誤:將本該worker乾的活,交給了標準verticle,即將圖中後兩個組件合二爲一。這種狀況在寫Vertx Web時很是容易出現,尤爲受傳統MVC框架的影響,無心識地將原來的編程套路給照搬過來了:在Handler中進行了大量操做。我本身也不例外,走過這段彎路。
Don't block me!
以Vert.x Web應用爲例,因爲Handler其實是在eventloop上執行,若它被阻塞,即致使後續請求所有沒法獲得處理。所以,最合適的作法就是:
利用Vert.x的特色,將IO操做封裝成異步庫。
用Vert.x將業務功能封裝成微服務,而後利用現成的基礎設施與其餘應用交互:
這也是我最喜歡用的模式,輕量,簡單,部署方便。我不太喜歡在一個原本就已經含有複雜業務邏輯的Grails應用中再包含一個Vert.x Verticle了。
或許有同窗對於上面的最後一項,感到疑惑。其實這個很簡單,以Postgresql爲例,能夠採用兩種模式:
行了,本篇寫到這裏也差很少了。最後給你們推薦一個網頁:Awesome Vert.x,上面有很多不錯的資源。
本系列其餘文章: