使用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());
  }
});

 

下一页