摘要:主要是避免引入太多的复杂性,并且出于灵活部署的需要。以应用为例,由于实际上是在上执行,若它被阻塞,即导致后续请求全部无法得到处理。因此,最合适的做法就是对于简单业务,采用异步库。本系列其他文章入坑须知入坑须知入坑须知
最开始觉得这个系列也就最多3篇了不起了(因为事不过三嘛),没曾想居然迎来了第四篇!
Kotlin由于最近决定投身到区块链的学习当中的缘故,出于更好的理解它的基本概念,自己动手参考文章写了一个迷你区块链的例子。采用了kotlin + vertx的工具选择。这次尝试再次验证了我在本系列一开篇所说:建议以Java语言开发为主。原因很简单,因为这个是基础,所以各方面支持(包括文档和功能方面)肯定是Java语言优先。
在做这个区块链的例子时,Vertx Kotlin的文档让我有极为糟糕的体验:从整篇文档中,你找不到一个完整的用kotlin书写Verticle的例子,阅读的时候就感觉内容有跳跃。虽然你可以猜出应该是继承AbstractVerticle,但你肯定还是希望文档中明确指出来。
当然啦,尽管有这样的问题,写代码的体验还是不错的。就build.gradle而言,跟Java + Groovy组合的差别不大。唯一需要注意的是,你可能需要将kotlin的jvmTarget设置为“1.8”。具体的配置可以参考工程的build.gradle。
总之,发现文档有问题,就先查Java文档。
静态资源上面的区块链的例子由两部分组成:前端静态页面 + 后端的Verticle,前端静态页面通过Ajax请求与后端的Verticle交互。这其实就是通过Vertx-Web的StaticHandler来实现的,很简单。这里只提两个需要留心的小地方。
首先,静态资源的根路径默认情况下是:src/main/resources/webroot。即当你请求“http://localhost:8080/index.html”时,其实对应的是:src/main/resources/webroot/index.html。
其次,目前的Web应用的URL很少会直接出现“……/xxx.html”。按照向Spring MVC或Grails这里框架的做法,一般是经过一个action,然后将浏览器导向某个页面。在做这个例子时,其实没有这么复杂的逻辑,让浏览器直接去加载某个页面(如configure.html)就可以了。但这样会出现一个让人很不爽的Path:“/configure.html”,而其他的路径因为主要是负责处理Ajax请求,都是形如“/mine”这样的路径。
为了统一路径风格,这里采用了一个小技巧:RoutingContext.reroute。参考代码如下:
router.get("/configure").handler({ rc: RoutingContext -> rc.reroute("/configure.html") })JDBC连接池
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这个建构于它之上的高层工具吧。
Unmount SubroutersSubrouter是个好东东,可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的设计非常好,哥的程序严重依赖它,请继续保留。”
应用架构我曾经不止一次看到初学者在问类似这样的问题:
应该创建多少Verticle实例?
Vert.x的应用该怎么去设计?
怎么跟现有的框架结合?
……
要回答这些问题,需要首先搞清楚几个事实。
不要将Verticle和Thread混为一谈,它们不是一类东西。简单的说,可以这样理解:Verticle由Thread来执行。
Vert.x中的Verticle由种类之分,不同类型的Verticle适用于不同场景,这一点在文档中已经有详细的阐述。
Vert.x本身是一个库,并不妨碍它跟其他框架(如Grails)的结合。只不过就我个人而言,更偏好将Vert.x应用多带带使用。主要是避免引入太多的复杂性,并且出于灵活部署的需要。
并且,通过观察其他人写的Vert.x代码(包括Vert.x自己的那些子项目),可以总结出来几个套路。
Master - Worker这是最常见的结构:
标准Verticle,负责接收外部请求,完成请求分派和结果收集
Worker Verticle,负责脏活累活
标准Verticle和Worker Verticle之间通过eventbus进行交互,整个架构其实也很简单:
request <---> standard verticles <---> worker verticle
这里的一个典型反模式,尤其是初学者会大概率犯的错误:将本该worker干的活,交给了标准verticle,即将图中后两个组件合二为一。这种情况在写Vertx Web时非常容易出现,尤其受传统MVC框架的影响,无意识地将原来的编程套路给照搬过来了:在Handler中进行了大量操作。我自己也不例外,走过这段弯路。
Don"t block me!
以Vert.x Web应用为例,由于Handler实际上是在eventloop上执行,若它被阻塞,即导致后续请求全部无法得到处理。因此,最合适的做法就是:
对于简单业务,采用异步库。
对于复杂业务,干脆交给worker去处理。
异步工具库利用Vert.x的特点,将IO操作封装成异步库。
微服务用Vert.x将业务功能封装成微服务,然后利用现成的基础设施与其他应用交互:
利用kafka队列实现服务间的交互
利用TCP Bridge实现与tcp client的交互
利用Eventbus Bridge实现与页面的交互
利用数据库实现与应用的简单交互
……
这也是我最喜欢用的模式,轻量,简单,部署方便。我不太喜欢在一个本来就已经含有复杂业务逻辑的Grails应用中再包含一个Vert.x Verticle了。
或许有同学对于上面的最后一项,感到疑惑。其实这个很简单,以Postgresql为例,可以采用两种模式:
简单的“定时任务+表”的方式,通过表的某个字段实现服务间的集成
利用PG自身支持的pub/sub功能
行了,本篇写到这里也差不多了。最后给大家推荐一个网页:Awesome Vert.x,上面有不少不错的资源。
本系列其他文章:
Vert.x入坑须知(1)
Vert.x入坑须知(2)
Vert.x入坑须知(3)
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/69249.html
摘要:对于集成测试,直接模拟实际的环境,再加上合适的,目前看来也还不错。这里给出两个例子集成测试单元测试都是基于写的,各位可以体验其酸爽度。好啦,本期内容就此结束,请保持关注,期待下期继续本系列其他文章入坑须知入坑须知 随着Vert.x进化到3.5.0,本系列也迎来了新篇章。 CORS的新变化 对于CORS,搞Web开发(不论你是前端,还是后端)的同志应该不陌生,尤其是如今微服务盛行的时代,...
摘要:轻量级,部署简单。此外,本文也不是入门文档,而是为了预防陷坑而给出的指导意见,故在阅读本文之前还请先仔细阅读的文档。可视作的一个最小部署和运行单元,简单的说,可类比为。,主,负责部署程序中其他的。严格来讲,之后,上述第一点并不完全正确。 一直以来早有将这些年用Vert.x的经验整理一下的想法,奈何天生不是勤快人,直到最近扶墙老师问起,遂成此文。 选择理由 现在想想,我们应该算是国内用V...
摘要:这一点其实是非常不妥的,有潜在的安全问题。这次,在项目中终于采用了以它为基础的集群方案。相反,使用一个周期,但针对每个生成一个一次性的,模拟随机发送。同时,要记得用完之后立即释放。 当初创建简书账号的时候曾立下宏愿,希望保持周更,无奈现实残酷,整个5月都处于忙忙碌碌的状态,居然令这个本来并不算太宏伟的目标难以为继,最终导致5月份交了白卷!【好吧,我承认,是我意志不够坚定,太懒了,;)】...
摘要:而不是开始,将服务使用多线程的请求重量级的容器。是启动多个轻便单线程的服务器和流量路由到他们。亮点应用程序是事件驱动,异步和单线程的。通过使用事件总线传递消息通信。为了建立一个消息系统,则需要获得该事件总线。 摘要 如果你对Node.js感兴趣,Vert.x可能是你的下一个大事件:一个建立在JVM上一个类似的架构企业制度。 这一部分介绍Vert.x是通过两个动手的例子(基于Vert.x...
摘要:上部分蓝图教程中我们一起探索了如何用开发一个基于消息的应用。对部分来说,如果看过我们之前的蓝图待办事项服务开发教程的话,你应该对这一部分非常熟悉了,因此这里我们就不详细解释了。有关使用实现的教程可参考蓝图待办事项服务开发教程。 上部分蓝图教程中我们一起探索了如何用Vert.x开发一个基于消息的应用。在这部分教程中,我们将粗略地探索一下kue-http模块的实现。 Vert.x Kue ...
阅读 990·2021-11-16 11:44
阅读 1344·2019-08-30 13:12
阅读 2357·2019-08-29 16:05
阅读 3025·2019-08-28 18:29
阅读 865·2019-08-26 13:41
阅读 3180·2019-08-26 13:34
阅读 2560·2019-08-26 10:35
阅读 910·2019-08-26 10:28