事件总线

使用服务代理创建 Vert.x 事件总线服务

本文档将向您展示如何使用 Vert.x 服务代理模块来启动一个事件总线服务。您会发现,当您希望使用纯 Java 接口在事件总线上设计和消费服务时,这种方法特别有用。

您将构建什么

您将构建一个 Vert.x 应用程序,它暴露并使用 BarmanService,这是一个事件总线服务,用于生成啤酒和管理客户账单。我们将部署两个 verticle:暴露服务的 BarmanVerticle 和消费服务的 DrunkVerticle

您需要什么

  • 文本编辑器或 IDE

  • Java 11 或更高版本

  • Maven

创建项目

以下是您应该使用的 pom.xml 文件的内容

您必须同时导入带有 processor 分类器的 vertx-codegenvertx-service-proxy。我们将使用 vertx-web-client 来加载啤酒名称。

设计 BarmanService

首先,我们必须定义我们的服务接口。在底层,代码生成器会分析它并生成事件总线消息处理器和代理类。

这是 BarmanService 接口

package io.vertx.howtos.ebservice.beers;

import io.vertx.codegen.annotations.ProxyGen;
import io.vertx.codegen.annotations.VertxGen;
import io.vertx.core.Future;
import io.vertx.core.Vertx;

@VertxGen
@ProxyGen (1)
public interface BarmanService {

  Future<Beer> giveMeARandomBeer(String customerName); (2)

  Future<Integer> getMyBill(String customerName); (3)

  void payMyBill(String customerName); (4)

  static BarmanService createProxy(Vertx vertx, String address) { (5)
    return new BarmanServiceVertxEBProxy(vertx, address);
  }

}
1 添加 @VertxGen@ProxyGen 以触发代码生成
2 定义生成新啤酒并将其添加到指定客户账单的方法。当啤酒准备好后,Future 将完成。
3 定义检索账单的方法。
4 定义结账的方法。这是一个即发即弃的方法。
5 指定创建新代理的方法。BarmanServiceVertxEBProxy 是代码生成器将为我们创建的代理的类名。

定义服务接口时必须遵循一些规则。有关更多信息,请参阅服务接口的限制。在我们的接口中,我们使用了一些基本类型和 Beer,一个被 @DataObject 注解的 POJO。如果您想在接口内部使用被 DataObject 注解的类型,它们必须定义一个仅带 JsonObject 参数的构造函数和一个返回 JsonObjecttoJson() 方法。

要触发代码生成,您还必须在与接口相同的包中添加一个带有 @ModuleGen 注解的 package-info.java

@ModuleGen(groupPackage = "io.vertx.howtos.ebservice", name = "beers")
package io.vertx.howtos.ebservice.beers;

import io.vertx.codegen.annotations.ModuleGen;

实现 BarmanService

现在您可以实现 BarmanService

对于 giveMeARandomBeer()

@Override
public Future<Beer> giveMeARandomBeer(String customerName) {
  return webClient
    .get(443, "www.craftbeernamegenerator.com", "/api/api.php?type=classic") (1)
    .ssl(true)
    .send()
    .expecting(HttpResponseExpectation.SC_OK) (2)
    .map(bufferHttpResponse -> {
      JsonObject result = bufferHttpResponse.bodyAsJsonObject();
      Beer beer = new Beer( (3)
        result.getJsonObject("data").getString("name"),
        result.getJsonObject("data").getString("style"),
        3 + random.nextInt(5)
      );
      System.out.println("Generated a new Beer! " + beer);
      bills.merge(customerName, beer.getPrice(), Integer::sum); (4)
      return beer;
    });
}
1 精酿啤酒名称生成器服务发送请求
2 如果对外部服务的请求失败,则使异步方法失败
3 创建新的 Beer
4 将啤酒添加到客户账单

对于 getMyBill()

@Override
public Future<Integer> getMyBill(String customerName) {
  return Future.succeededFuture(bills.get(customerName));
}

对于 settleMyBill()

@Override
public void payMyBill(String customerName) {
  bills.remove(customerName);
  System.out.println("Removed debt of " + customerName);
}

暴露服务

现在我们可以使用 ServiceBinder 类在 BarmanVerticle 中暴露服务

package io.vertx.howtos.ebservice;

import io.vertx.core.Future;
import io.vertx.core.VerticleBase;
import io.vertx.core.eventbus.MessageConsumer;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.WebClient;
import io.vertx.howtos.ebservice.beers.BarmanService;
import io.vertx.howtos.ebservice.beers.impl.BarmanServiceImpl;
import io.vertx.serviceproxy.ServiceBinder;

public class BarmanVerticle extends VerticleBase {

  @Override
  public Future<?> start() {
    BarmanService service = new BarmanServiceImpl(WebClient.create(vertx)); (1)

    MessageConsumer<JsonObject> consumer = new ServiceBinder(vertx) (2)
      .setAddress("beers.services.myapplication") (3)
      .register(BarmanService.class, service);(4)

    return consumer.completion();
  }
}
1 实例化 BarmanServiceImpl
2 实例化 ServiceBinder
3 配置服务地址
4 在事件总线上注册服务

使用服务

现在我们可以使用生成的代理来使用服务。出于演示目的,我们将在另一个名为 DrunkVerticle 的 verticle 中使用它

package io.vertx.howtos.ebservice;

import io.vertx.core.Future;
import io.vertx.core.VerticleBase;
import io.vertx.howtos.ebservice.beers.BarmanService;

public class DrunkVerticle extends VerticleBase {

  @Override
  public Future<?> start() {
    BarmanService barmanService = BarmanService.createProxy(vertx, "beers.services.myapplication"); (1)
    return barmanService.giveMeARandomBeer("homer") (2)
      .onSuccess(beer1 -> System.out.println("My first beer is a " + beer1.getName() + " and it costs " + beer1.getPrice() + ""))  (3)
      .compose(v -> vertx.timer(1500))
      .compose(v -> barmanService.giveMeARandomBeer("homer")) (4)
      .onSuccess(beer2 -> System.out.println("My second beer is a " + beer2.getName() + " and it costs " + beer2.getPrice() + "")) (5)
      .compose(v -> barmanService.getMyBill("homer")) (6)
      .onSuccess(bill -> {
        System.out.println("My bill with the bar is " + bill + "");
        barmanService.payMyBill("homer"); (7)
      });
  }
}
1 实例化代理
2 我们来尝尝第一杯啤酒 🍺
3 喝第一杯 🍻
4 再来一杯 🍺
5 喝第二杯 🍻
6 该回家了。给我账单
7 支付账单

运行应用程序

要运行应用程序,请部署 BarmanVerticle,然后部署 DrunkVerticle

package io.vertx.howtos.ebservice;

import io.vertx.core.Vertx;

public class BeersApplication {

  public static void main(String[] args) {
    Vertx vertx = Vertx.vertx();
    vertx.deployVerticle(new BarmanVerticle()).await();
    System.out.println("The barman is ready to serve you");
    vertx.deployVerticle(new DrunkVerticle()).await();
    vertx.close().await();
  }
}

您可以从以下方式运行应用程序

  1. 在您的 IDE 中,通过运行 BeersApplication 类中的 main 方法,或者

  2. 使用 Maven:mvn compile exec:java

您应该会看到类似这样的内容

The barman is ready to serve you
Generated a new Beer! Manticore's Shorthand Barleywine (Barleywine)
My first beer is a Manticore's Shorthand Barleywine and it costs 6€
Generated a new Beer! Fortunate Pretzel (Cream Ale)
My second beer is a Fortunate Pretzel and it costs 6€
My bill with the bar is 12€
Removed debt of homer

总结

本操作指南向您解释了

  1. 如何使用 Vert.x 服务代理设计和实现事件总线服务,以及

  2. 如何在事件总线上暴露服务,以及

  3. 如何使用服务