使用Vert.x开发响应式微服务
响应式系统是一种建立分布式应用程序新方式,利用现代的处理器架构的优势,更有效地使用资源。结合微服务,它提供了一个惊人的灵活性,每个组件单独开发,发布,部署,更新和退出。建设微服架构却不容易。需要管理许多方面,如部署设施、服务发现、服务之间的相互作用、弹性模式、可扩展性等。
Eclipse Vert.x是一个建立微服务的工具。它给你塑造你的系统的自由,确保其响应性,弹性。你的组件之间通信是异步的,利用非堵塞异步性Vert.x。
Vert.x是一个简单的,可扩展的,类似actor模型的可部署和并发模型。Verticles是一些代码块,能部署和运行在Vert中。Vert.x应用程序通常是由许多Verticles实例运行在同一个Vert.x实例中。Vert.x实例彼此之间通过事件总线发送消息实现异步交互。
默认Verticles是运行在Vert.x事件循环中,必须不能有堵塞代码,Vert.x确保每个Verticles总是被同样线程执行。
Verticles可由许多语言支持,Java需要拓展AbstractVerticle,它的start和stop方法对应Verticles部署和卸载的生命周期阶段。
import io.vertx.core.AbstractVerticle;
public class MyVerticle extends AbstractVerticle {
@Override public void start() throws Exception { // Executed when the verticle is deployed
}
@Override public void stop() throws Exception { // Executed when the verticle is undeployed } }
一个verticle 能通过传入配置部署其他verticle ,可以创建很多verticle 实例,每个实例运行在不同的event loop事件循环中,Vert.x会平衡这些实例负载,这样就能充分利用你的CPU多核威力。
public class MyDeployingVerticle extends AbstractVerticle {
@Override public void start() throws Exception { // Pass a configuration JsonObject config = new JsonObject().put("key", "value"); vertx.deployVerticle(io.vertx.starter.MyVerticle.class.getName(), new DeploymentOptions().setConfig(config));
// Set the number of instances vertx.deployVerticle(io.vertx.starter.MyVerticle.class.getName(), new DeploymentOptions().setInstances(2)); } }
创建第一个Vert.x项目
创建Maven项目:
git clone https://github.com/vert-x3/vertx-maven-starter.git PROJECT_NAME
解压目录运行:./redeploy.sh(redeploy.bat),浏览器打开 http://localhost:8080, 你会看到第一个Vert.x项目。
实现REST API
看看使用 Vert.x Web如何实现简单REST API ,增加依赖到你的pom.xml:
‹dependency› ‹groupId›io.vertx‹/groupId› ‹artifactId›vertx-web‹/artifactId› ‹/dependency›
Vert.x Web是一个基于Vert.x让你方便实现REST API的组件,提供服务器端模板 静态文件支持 错误页面。提供路由Router概念,每个route有一个处理器处理请求建立响应。
下面是以讹增加名称的简单案例:
package io.vertx.starter;
import io.vertx.core.AbstractVerticle; import io.vertx.core.json.Json; import io.vertx.ext.web.Router; import io.vertx.ext.web.handler.BodyHandler; import java.util.ArrayList; import java.util.List;
public class MyRestAPIVerticle extends AbstractVerticle {
// Maintain a simple list of names维持名称简单列表 private List names = new ArrayList<>();
@Override public void start() { // Create a Vert.x web router Router router = Router.router(vertx); // Register a simple first route on / 注册一个简单首个route在根路径/ router.get("/").handler(rc -> { rc.response().end("Welcome"); });
// Register a second router retrieving all stored names as JSON注册第二个router返回名称列表 router.get("/names").handler( // Just encode the list as JSON and return. rc -> rc.response() .putHeader("content-type", "application/json") .end(Json.encode(names)));
// Register a body handler indicating that other routes need // to read the request body router.route().handler(BodyHandler.create());
// Register a third route to add names注册第三个router增加名称 router.post("/names").handler( rc -> { // Read the body String name = rc.getBody().toString(); if (name.isEmpty()) { // Invalid body -> Bad request rc.response().setStatusCode(400).end(); } else if (names.contains(name)) { // Already included name -> Conflict rc.response().setStatusCode(409).end(); } else { // Add the name to the list -> Created names.add(name); rc.response().setStatusCode(201).end(name); } });
vertx.createHttpServer() // Pass the router's accept method as request handler .requestHandler(router::accept) .listen(8080); } }
消费REST API
当建立微服务时,你需要使用调用服务,也就是需要消费REST API,vert.x提供异步HTTP客户端。
package io.vertx.starter; import io.vertx.core.AsyncResult; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.json.JsonArray;
public class MyRestAPIClient {
private HttpClient client; public MyRestAPIClient(Vertx vertx) { // Create the HTTP client and configure the host and post. client = vertx.createHttpClient(new HttpClientOptions() .setDefaultHost("localhost") .setDefaultPort(8080) ); }
public void close() { // Don't forget to close the client when you are done. client.close(); }
public void getNames(Handler<asyncresult> handler) { // Emit a HTTP GET client.get("/names", response -> // Handler called when the response is received // We register a second handler to retrieve the body response.bodyHandler(body -> { // When the body is read, invoke the result handler handler.handle(Future.succeededFuture(body.toJsonArray())); })) .exceptionHandler(t -> { // If something bad happen, report the failure to the passed handler handler.handle(Future.failedFuture(t)); }) // Call end to send the request .end(); }
public void addName(String name, Handler<asyncresult> handler) { // Emit a HTTP POST client.post("/names", response -> { // Check the status code and act accordingly if (response.statusCode() == 200) { handler.handle(Future.succeededFuture()); } else { handler.handle(Future.failedFuture(response.statusMessage())); } }) .exceptionHandler(t -> handler.handle(Future.failedFuture(t))) // Pass the name we want to add .end(name); } }
下面代码显示上面客户端代码是具体如何被调用:
Vertx vertx = Vertx.vertx(); MyRestAPIClient client = new MyRestAPIClient(vertx); client.getNames(ar -> { if (ar.succeeded()) { System.out.println("Names: " + ar.result().encode()); } else { System.out.println("Unable to retrieve the list of names: " + ar.cause().getMessage()); } });