第三章 重构为独立可重用的Verticle
版权声明:本文为博主自主翻译,转载请标明出处。 https://blog.csdn.net/elinespace/article/details/80381261
相应的代码位于本指南代码仓库的step-2目录下
通过第一次迭代,我们得到了一个可工作的Wiki应用。然而它的实现存在以下问题:
- HTTP请求处理和数据库访问代码交织在相同的方法中。
- 大量配置数据(如端口号、JDBC驱动等)是代码中的硬编码字符串。
3.1 架构和技术选择
第二次迭代是关于重构代码为独立可重用Verticle的:
我们将部署两个Verticle来处理HTTP请求,一个Verticle封装数据库持久化。由此产生的Verticle将没有相互的直接引用,它们将只商定事件总线中的目的地名称以及消息格式。这种方式提供了一个简单但有效的解耦。
发送到事件总线的消息将解码为JSON。虽然Vert.x的事件总线支持灵活的串行化方案用于高要求或者高度定制的上下文,但是使用JSON数据通常是明智的选择。使用JSON的另一个优势是它是一种语言无关的格式。由于Vert.x是支持多语言的,对于使用不同语言编写的Verticle之间的通讯,JSON是非常理想的。
3.2 HTTP Server Verticle
Verticle类开端及start方法看起来如下:
public class HttpServerVerticle extends AbstractVerticle {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpServerVerticle.class);
public static final String CONFIG_HTTP_SERVER_PORT = "http.server.port"; ①
public static final String CONFIG_WIKIDB_QUEUE = "wikidb.queue";
private String wikiDbQueue = "wikidb.queue";
@Override
public void start(Future<Void> startFuture) throws Exception {
wikiDbQueue = config().getString(CONFIG_WIKIDB_QUEUE, "wikidb.queue"); ②
HttpServer server = vertx.createHttpServer();
Router router = Router.router(vertx);
router.get("/").handler(this::indexHandler);
router.get("/wiki/:page").handler(this::pageRenderingHandler);
router.post().handler(BodyHandler.create());
router.post("/save").handler(this::pageUpdateHandler);
router.post("/create").handler(this::pageCreateHandler);
router.post("/delete").handler(this::pageDeletionHandler);
int portNumber = config().getInteger(CONFIG_HTTP_SERVER_PORT, 8080); ③
server
.requestHandler(router::accept)
.listen(portNumber, ar -> {
if (ar.succeeded()) {
LOGGER.info("HTTP server running on port " + portNumber);
startFuture.complete();
} else {
LOGGER.error("Could not start a HTTP server", ar.cause());
startFuture.fail(ar.cause());
}
});
}
// (...)
① 我们暴露了公开的常量用于Verticle配置参数:HTTP端口号以及发送消息到数据库Verticle的事件总线目的地名称。
② AbstractVerticle#config()方法允许访问已提供的Verticle配置。对于没有指定值的情况,第二个参数是默认值。
③ 配置值不只可以是字符串,也可以是整数、布尔值以及复杂的JSON数据等。
该类剩余部分主要是提取HTTP部分的代码,以前的数据库代码通过事件总线消息替换。这是indexHandler方法的代码:
private final FreeMarkerTemplateEngine templateEngine = FreeMarkerTemplateEngine.create();
private void indexHandler(RoutingContext context) {
DeliveryOptions options = new DeliveryOptions().addHeader("action", "all-pages"); ②
vertx.eventBus().send(wikiDbQueue, new JsonObject(), options, reply -> { ①
if (reply.succeeded()) {
JsonObject body = (JsonObject) reply.result().body(); ③
context.put("title", "Wiki home");
context.put("pages", body.getJsonArray("pages").getList());
templateEngine.render(context, "templates", "/index.ftl", ar -> {
if (ar.succeeded()) {
context.response().putHeader("Content-Type", "text/html");
context.response().end(ar.result());
} else {
context.fail(ar.cause());
}
});
} else {
context.fail(reply.cause());
}
});
}
① vertx对象提供了对事件总线的访问,我们发送一个消息到数据库Verticle的队列。
② 传递选项(DeliveryOptions)允许我们指定头、有效载荷(payload)编解码器和超时时间。
③ 一旦成功,回复包含有效载荷。
正如我们所看到的,事件总线消息由一个消息体和选项组成,它可以选择性地期待一个答复。对于没有预期答复的情况,有一个send方法的变体,它没有Handler参数。
我们将有效载荷编码为JSON对象,并通过一个称为action的消息头指定数据库Verticle应该执行哪个操作。
Verticle的剩余代码就是路由器处理器,同样使用事件总线获取和存储数据:
private