最开始以为这个系列也就最多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,上面有很多不错的资源。
本系列其余文章: