Using the Cassandra Client

Apache Cassandra® 是一款免费且开源的分布式宽列式存储、NoSQL 数据库管理系统,旨在跨多个商用服务器处理海量的数据,并提供高可用性,且无单点故障。

Apache Cassandra® is a free and open-source, distributed, wide column store, NoSQL database management system designed to handle large amounts of data across many commodity servers, providing high availability with no single point of failure.

在本指南中,我们将了解如何让您的 REST 服务使用 Cassandra 数据库。

In this guide, we will see how you can get your REST services to use a Cassandra database. Unresolved directive in cassandra.adoc - include::{includes}/platform-include.adoc[]

Prerequisites

include::{includes}/prerequisites.adoc[]* 正在运行的 Apache Cassandra, DataStax Enterprise (DSE) 或 DataStax Astra 数据库;或者,干净的 Docker 安装。

Unresolved directive in cassandra.adoc - include::{includes}/prerequisites.adoc[] * A running Apache Cassandra, DataStax Enterprise (DSE) or DataStax Astra database; or alternatively, a fresh Docker installation.

Architecture

本快速入门指南显示了如何使用 Cassandra Quarkus extension构建 REST 应用程序,此应用程序允许您连接到 Apache Cassandra、DataStax Enterprise (DSE) 或 DataStax Astra 数据库,使用 DataStax Java driver

This quickstart guide shows how to build a REST application using the Cassandra Quarkus extension, which allows you to connect to an Apache Cassandra, DataStax Enterprise (DSE) or DataStax Astra database, using the DataStax Java driver.

本指南还将使用 DataStax Object Mapper– 一个功能强大的 Java 到 CQL 映射框架,通过避免您手动编写 CQL 查询的麻烦,极大地简化了您的应用程序数据访问层代码。

This guide will also use the DataStax Object Mapper – a powerful Java-to-CQL mapping framework that greatly simplifies your application’s data access layer code by sparing you the hassle of writing your CQL queries by hand.

在本快速入门指南中构建的应用程序非常简单:用户可以使用表单在列表中添加元素,然后更新项目列表。浏览器和服务器之间所有信息都采用 JSON 格式,并且这些元素存储在 Cassandra 数据库中。

The application built in this quickstart guide is quite simple: the user can add elements in a list using a form, and the items list is updated. All the information between the browser and the server is formatted as JSON, and the elements are stored in the Cassandra database.

Solution

我们建议您按照以下部分中的说明进行操作,并逐步创建应用程序。但是,您可以直接转到完成的示例。

We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.

该解决方案位于 Cassandra Quarkus 扩展 GitHub 存储库的 quickstart directory中。

The solution is located in the quickstart directory of the Cassandra Quarkus extension GitHub repository.

Creating a Blank Maven Project

首先,创建一个新的 Maven 项目,并复制 `pom.xml`中存在的 `quickstart`目录中的文件。

First, create a new Maven project and copy the pom.xml file that is present in the quickstart directory.

`pom.xml`将导入您所需的所有 Quarkus 扩展和依赖项。

The pom.xml is importing all the Quarkus extensions and dependencies you need.

Creating the Data Model and Data Access Objects

在此示例中,我们将创建一个应用程序来管理水果列表。

In this example, we will create an application to manage a list of fruits.

首先,让我们创建我们的数据模型(由 `Fruit`类表示),如下所示:

First, let’s create our data model – represented by the Fruit class – as follows:

@Entity
@PropertyStrategy(mutable = false)
public class Fruit {

    @PartitionKey
    private final String name;

    private final String description;

    public Fruit(String name, String description) {
      this.name = name;
      this.description = description;
    }
  // getters, hashCode, equals, toString methods omitted for brevity
}

如上所述,我们正在使用 DataStax Object Mapper。换句话说,我们不会手动编写我们的 CQL 查询;相反,我们将使用一些注释对我们的数据模型进行注释,映射器会在下面生成适当的 CQL 查询。

As stated above, we are using the DataStax Object Mapper. In other words, we are not going to write our CQL queries manually; instead, we will annotate our data model with a few annotations, and the mapper will generate proper CQL queries underneath.

这就是 `Fruit`类使用 `@Entity`注释的原因:此注释将其标记为映射到 Cassandra 表的 entity class。它的实例不会主动持久到 Cassandra 数据库,或从其检索。此处,表名称将从类名称 `fruit`中推断。

This is why the Fruit class is annotated with @Entity: this annotation marks it as an entity class that is mapped to a Cassandra table. Its instances are meant to be automatically persisted into, and retrieved from, the Cassandra database. Here, the table name will be inferred from the class name: fruit.

此外,`name`字段表示 Cassandra 分区键,因此我们使用来自 Object Mapper 库的另一个注释 `@PartitionKey`对其进行注释。

Also, the name field represents a Cassandra partition key, and so we are annotating it with @PartitionKey – another annotation from the Object Mapper library.

实体类通常需要有一个默认的无参数构造函数,除非它们使用 `@PropertyStrategy(mutable = false)`注释(此处即采用这种方式)。

Entity classes are normally required to have a default no-arg constructor, unless they are annotated with @PropertyStrategy(mutable = false), which is the case here.

下一步是创建一个 DAO(数据访问对象)接口,此接口将管理`Fruit`实体的实例:

The next step is to create a DAO (Data Access Object) interface that will manage instances of Fruit entities:

@Dao
public interface FruitDao {
  @Update
  void update(Fruit fruit);

  @Select
  PagingIterable<Fruit> findAll();
}

此接口公开将在我们的 REST 服务中使用的操作。同样,注释`@Dao`来自 DataStax Object Mapper,它还将自动为您生成此接口的一个实现。

This interface exposes operations that will be used in our REST service. Again, the annotation @Dao comes from the DataStax Object Mapper, which will also automatically generate an implementation of this interface for you.

另请注意 findAll 方法的特殊返回类型 PagingIterable: 它是驱动程序返回的结果集的基本类型。

Note also the special return type of the findAll method, PagingIterable: it’s the base type of result sets returned by the driver.

最后,让我们创建一个 Mapper 接口:

Finally, let’s create a Mapper interface:

@Mapper
public interface FruitMapper {
  @DaoFactory
  FruitDao fruitDao();
}

@Mapper 注解是 DataStax Object Mapper 识别的另一个注解。amapper 负责构建 DAO 实例——本例中,我们的 mapper 正在构建我们唯一的 DAO FruitDao 的实例。

The @Mapper annotation is yet another annotation recognized by the DataStax Object Mapper. A mapper is responsible for constructing DAO instances – in this case, out mapper is constructing an instance of our only DAO, FruitDao.

将 mapper 接口视为 DAO bean 的工厂。如果你打算在自己的代码中构建和注入特定的 DAO bean,那么首先必须在 @Mapper 接口中为其添加 @DaoFactory 方法。

Think of the mapper interface as a factory for DAO beans. If you intend to construct and inject a specific DAO bean in your own code, then you first must add a @DaoFactory method for it in a @Mapper interface.

@DaoFactory 方法名称无关紧要。

@DaoFactory method names are irrelevant.

@DaoFactory 方法应返回以下类型的 bean:

@DaoFactory methods should return beans of the following types:

  • Any @Dao-annotated interface, e.g. FruitDao;

  • A CompletionStage of any @Dao-annotated interface, e.g. CompletionStage<FruitDao>.

  • A Uni of any @Dao-annotated interface, e.g. Uni<FruitDao>.

Uni 是 Mutiny 库中的一个类型,它是 Quarkus 使用的响应式编程库。有关详情,请参见下方的“响应式编程”部分。

Uni is a type from the Mutiny library, which is the reactive programming library used by Quarkus. This will be explained in more detail in the "Reactive Programming" section below.

Generating the DAO and mapper implementations

你可能已经猜到了,我们不会实现上述接口。相反,Object Mapper 将为我们生成此类实现。

As you probably guessed already, we are not going to implement the interfaces above. Instead, the Object Mapper will generate such implementations for us.

Object Mapper 由 2 部分组成:

The Object Mapper is composed of 2 pieces:

  1. A (compile-time) annotation processor that scans the classpath for classes annotated with @Mapper, @Dao or @Entity, and generates code and CQL queries for them; and

  2. A runtime module that contains the logic to execute the generated queries.

因此,启用 Object Mapper 需要两个步骤:

Therefore, enabling the Object Mapper requires two steps:

  1. Declare the cassandra-quarkus-mapper-processor annotation processor. With Maven, this is done by modifying the compiler plugin configuration in the project’s pom.xml file as follows:

<plugin>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.10.1</version>
  <configuration>
    <source>${java.version}</source>
    <target>${java.version}</target>
    <annotationProcessorPaths>
      <path>
        <groupId>com.datastax.oss.quarkus</groupId>
        <artifactId>cassandra-quarkus-mapper-processor</artifactId>
        <version>${cassandra-quarkus.version}</version>
      </path>
    </annotationProcessorPaths>
  </configuration>
</plugin>

使用 Gradle,可以通过向 build.gradle 文件添加以下行来完成此操作:

With Gradle, this is done by adding the following line to the build.gradle file:

annotationProcessor "com.datastax.oss.quarkus:cassandra-quarkus-mapper-processor:${cassandra-quarkus.version}"

验证你是否正在启用正确的注解处理器!Cassandra 驱动程序附带称为 java-driver-mapper-processor 的 Object Mapper 注解处理器。但是,Cassandra Quarkus 扩展也附带其自己的注解处理器: cassandra-quarkus-mapper-processor ,它比驱动程序的注解处理器有更多功能。此注解处理器是唯一适用于 Quarkus 应用程序的注解处理器,因此请检查正在使用的是否是此注解处理器。此外,切勿同时使用两个注解处理器。

Verify that you are enabling the right annotation processor! The Cassandra driver ships with its Object Mapper annotation processor, called java-driver-mapper-processor. But the Cassandra Quarkus extension also ships with its own annotation processor: cassandra-quarkus-mapper-processor, which has more capabilities than the driver’s. This annotation processor is the only one suitable for use in a Quarkus application, so check that this is the one in use. Also, never use both annotation processors together.

  1. Declare the java-driver-mapper-runtime dependency in compile scope in the project’s pom.xml file as follows:

<dependency>
  <groupId>com.datastax.oss</groupId>
  <artifactId>java-driver-mapper-runtime</artifactId>
</dependency>

尽管这个模块称为“运行时”,但它必须在编译范围内声明。

Although this module is called "runtime", it must be declared in compile scope.

如果正确设置了项目,现在你应该能够毫无错误地编译它,并且你应该能看到生成的代码位于 target/generated-sources/annotations 目录(如果你使用 Maven 的话)。你不必熟悉生成的代码,因为它主要是与数据库交互的内部机制。

If your project is correctly set up, you should now be able to compile it without errors, and you should see the generated code in the target/generated-sources/annotations directory (if you are using Maven). It’s not required to get familiar with the generated code though, as it is mostly internal machinery to interact with the database.

Creating a service & JSON REST endpoint

现在让我们创建一个 FruitService 为我们应用程序的业务层,以及从 Cassandra 数据库存储/加载水果。

Now let’s create a FruitService that will be the business layer of our application and store/load the fruits from the Cassandra database.

@ApplicationScoped
public class FruitService {

  @Inject FruitDao dao;

  public void save(Fruit fruit) {
    dao.update(fruit);
  }

  public List<Fruit> getAll() {
    return dao.findAll().all();
  }
}

注意服务如何注入一个 FruitDao 实例。由于生成的实现,DAO 实例会自动注入。

Note how the service is being injected a FruitDao instance. This DAO instance is injected automatically, thanks to the generated implementations.

Cassandra Quarkus 扩展允许你在组件中注入下列任何 Bean:

The Cassandra Quarkus extension allows you to inject any of the following beans in your own components:

  • All @Mapper-annotated interfaces in your project.

  • You can also inject a CompletionStage or Uni of any @Mapper-annotated interface.

  • Any bean returned by a @DaoFactory method (see above for possible bean types).

  • The QuarkusCqlSession bean: this application-scoped, singleton bean is your main entry point to the Cassandra client; it is a specialized Cassandra driver session instance with a few methods tailored especially for Quarkus. Read its javadocs carefully!

  • You can also inject CompletionStage<QuarkusCqlSession> or Uni<QuarkusCqlSession>.

在我们的示例中,FruitMapperFruitDao 都可以在任何地方注入。我们选择在 FruitService 中注入 FruitDao

In our example, both FruitMapper and FruitDao could be injected anywhere. We chose to inject FruitDao in FruitService.

最后缺失的部分是公开 GET 和 POST 方法的 REST API:

The last missing piece is the REST API that will expose GET and POST methods:

@Path("/fruits")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class FruitResource {

  @Inject FruitService fruitService;

  @GET
  public List<FruitDto> getAll() {
    return fruitService.getAll().stream().map(this::convertToDto).collect(Collectors.toList());
  }

  @POST
  public void add(FruitDto fruit) {
    fruitService.save(convertFromDto(fruit));
  }

  private FruitDto convertToDto(Fruit fruit) {
    return new FruitDto(fruit.getName(), fruit.getDescription());
  }

  private Fruit convertFromDto(FruitDto fruitDto) {
    return new Fruit(fruitDto.getName(), fruitDto.getDescription());
  }
}

注意 FruitResource 如何自动注入一个 FruitService 实例。

Notice how FruitResource is being injected a FruitService instance automatically.

一般不建议在 REST API 和数据访问层之间使用同一实体对象。这些层实际上应该分离,并使用不同的 API,以允许每个 API 独立于另一个 API 发展。这就是我们的 REST API 使用不同对象的原因:FruitDto 类——DTO 一词代表“数据传输对象”。该 DTO 对象会在 HTTP 消息中自动转换为 JSON,反之亦然:

It is generally not recommended using the same entity object between the REST API and the data access layer. These layers should indeed be decoupled and use distinct APIs in order to allow each API to evolve independently of the other. This is the reason why our REST API is using a different object: the FruitDto class – the word DTO stands for "Data Transfer Object". This DTO object will be automatically converted to and from JSON in HTTP messages:

public class FruitDto {

  private String name;
  private String description;

  public FruitDto() {}

  public FruitDto(String name, String description) {
    this.name = name;
    this.description = description;
  }
  // getters and setters omitted for brevity
}

将 JSON 转换为 JSON 或反之亦然的过程由 Quarkus REST(原名 RESTEasy Reactive)扩展自动完成,它包含在本指南的 pom.xml 文件中。如果你想将其手动添加到应用程序,请将以下片段添加到应用程序的 pom.xml 文件中:

The translation to and from JSON is done automatically by the Quarkus REST (formerly RESTEasy Reactive) extension, which is included in this guide’s pom.xml file. If you want to add it manually to your application, add the below snippet to your application’s pom.xml file:

<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-rest</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-rest-jackson</artifactId>
</dependency>

JSON 序列化层使用的 DTO 类需要有一个默认的无参数构造函数。

DTO classes used by the JSON serialization layer are required to have a default no-arg constructor.

从 DTO 转换为 JSON 的过程对我们来说是自动处理的,但我们仍然必须从 Fruit 转换为 FruitDto ,反之亦然。这必须手动完成,这就是为什么我们在 FruitResource 中声明了两个转换方法:convertToDtoconvertFromDto

The conversion from DTO to JSON is handled automatically for us, but we still must convert from Fruit to FruitDto and vice versa. This must be done manually, which is why we have two conversion methods declared in FruitResource: convertToDto and convertFromDto.

在我们的示例中,FruitFruitDto 非常相似,所以你可能会想为什么不在所有地方都使用 Fruit 。但是在实际情况下,看到 DTO 和实体具有非常不同的结构并不少见。

In our example, Fruit and FruitDto are very similar, so you might wonder why not use Fruit everywhere. In real life cases though, it’s not uncommon to see DTOs and entities having very different structures.

Connecting to the Cassandra Database

Connecting to Apache Cassandra or DataStax Enterprise (DSE)

要配置的主要属性是:contact-points,用于访问 Cassandra 数据库;local-datacenter,这是驱动程序所必需的;以及——可选择——要绑定的键空间。

The main properties to configure are: contact-points, to access the Cassandra database; local-datacenter, which is required by the driver; and – optionally – the keyspace to bind to.

示例配置如下所示:

A sample configuration should look like this:

quarkus.cassandra.contact-points={cassandra_ip}:9042
quarkus.cassandra.local-datacenter={dc_name}
quarkus.cassandra.keyspace={keyspace}

在这个示例中,我们使用在本地主机上运行的单个实例,并且包含我们数据的键空间是 k1

In this example, we are using a single instance running on localhost, and the keyspace containing our data is k1:

quarkus.cassandra.contact-points=127.0.0.1:9042
quarkus.cassandra.local-datacenter=datacenter1
quarkus.cassandra.keyspace=k1

如果你的集群需要纯文本身份验证,你必须还提供更多两个设置: usernamepassword

If your cluster requires plain text authentication, you must also provide two more settings: username and password.

quarkus.cassandra.auth.username=john
quarkus.cassandra.auth.password=s3cr3t

Connecting to a DataStax Astra Cloud Database

当连接到 DataStax Astra 时,应该提供所谓的 secure connect bundle,而不是提供一个联系点和一个数据中心,这应该指向 Astra 安全连接束文件的一个有效路径。你可以在 Astra 网页控制台中下载你的安全连接束。

When connecting to DataStax Astra, instead of providing a contact point and a datacenter, you should provide a so-called secure connect bundle, which should point to a valid path to an Astra secure connect bundle file. You can download your secure connect bundle from the Astra web console.

你还需要提供一个用户名和密码,因为 Astra 集群总是需要身份验证。

You will also need to provide a username and password, since authentication is always required on Astra clusters.

适用于 DataStax Astra 的一个示例配置应该是这样的:

A sample configuration for DataStax Astra should look like this:

quarkus.cassandra.cloud.secure-connect-bundle=/path/to/secure-connect-bundle.zip
quarkus.cassandra.auth.username=john
quarkus.cassandra.auth.password=s3cr3t
quarkus.cassandra.keyspace=k1

Advanced Driver Configuration

你可以使用 application.confapplication.json 文件配置其他 Java 驱动程序设置。它们需要位于你的应用程序的类路径中。所有设置都将自动传递给底层驱动程序配置机制。在 application.properties 中用 quarkus.cassandra 前缀定义的设置将优先于在 application.confapplication.json 中定义的设置。

You can configure other Java driver settings using application.conf or application.json files. They need to be located in the classpath of your application. All settings will be passed automatically to the underlying driver configuration mechanism. Settings defined in application.properties with the quarkus.cassandra prefix will have priority over settings defined in application.conf or application.json.

要查看全部设置列表,请参阅 driver settings reference

To see the full list of settings, please refer to the driver settings reference.

Running a Local Cassandra Database

默认情况下,Cassandra 客户端被配置为访问端口 9042 上的本地 Cassandra 数据库(默认 Cassandra 端口)。

By default, the Cassandra client is configured to access a local Cassandra database on port 9042 (the default Cassandra port).

确保设置 quarkus.cassandra.local-datacenter 匹配你的 Cassandra 集群的数据中心。

Make sure that the setting quarkus.cassandra.local-datacenter matches the datacenter of your Cassandra cluster.

如果你不知道本地数据中心的名称,可以通过运行以下 CQL 查询找到这个值: SELECT data_center FROM system.local

If you don’t know the name of your local datacenter, this value can be found by running the following CQL query: SELECT data_center FROM system.local.

如果你想使用 Docker 运行 Cassandra 数据库,你可以使用以下命令在后台启动一个:

If you want to use Docker to run a Cassandra database, you can use the following command to launch one in the background:

docker run --name local-cassandra-instance -p 9042:9042 -d cassandra

接下来,你需要创建你的应用程序将使用的键空间和表。如果你使用 Docker,运行以下命令:

Next you need to create the keyspace and table that will be used by your application. If you are using Docker, run the following commands:

docker exec -it local-cassandra-instance cqlsh -e "CREATE KEYSPACE IF NOT EXISTS k1 WITH replication = {'class':'SimpleStrategy', 'replication_factor':1}"
docker exec -it local-cassandra-instance cqlsh -e "CREATE TABLE IF NOT EXISTS k1.fruit(name text PRIMARY KEY, description text)"

你也可以使用 CQLSH 实用程序交互地询问你的数据库:

You can also use the CQLSH utility to interactively interrogate your database:

docker exec -it local-cassandra-instance cqlsh

Testing the REST API

在项目根目录中:

In the project root directory:

  • Run mvn clean package and then java -jar ./target/cassandra-quarkus-quickstart-*-runner.jar to start the application;

  • Or better yet, run the application in dev mode: mvn clean quarkus:dev.

现在,你可以使用 curl 命令与底层的 REST API 交互。

Now you can use curl commands to interact with the underlying REST API.

要创建一个水果:

To create a fruit:

curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"name":"apple","description":"red and tasty"}' \
  http://localhost:8080/fruits

要检索水果:

To retrieve fruits:

curl -X GET http://localhost:8080/fruits

Creating a Frontend

现在,让我们添加一个简单的网页来与我们的 FruitResource 进行交互。

Now let’s add a simple web page to interact with our FruitResource.

Quarkus 会自动提供位于 META-INF/resources 目录下的静态资源。在 src/main/resources/META-INF/resources 目录中,添加一个 fruits.html 文件,其中包含 this file 中的内容。

Quarkus automatically serves static resources located under the META-INF/resources directory. In the src/main/resources/META-INF/resources directory, add a fruits.html file with the contents from this file in it.

你现在可以与你的 REST 服务进行交互:

You can now interact with your REST service:

  • If you haven’t done yet, start your application with mvn clean quarkus:dev;

  • Point your browser to http://localhost:8080/fruits.html;

  • Add new fruits to the list via the form.

Reactive Programming with the Cassandra Client

QuarkusCqlSession interface 可以使用一系列响应式方法,这些方法可以与 Quarkus 及其响应式框架 Mutiny 无缝集成。

The QuarkusCqlSession interface gives you access to a series of reactive methods that integrate seamlessly with Quarkus and its reactive framework, Mutiny.

如果您不熟悉 Mutiny,请查看 Mutiny - an intuitive reactive programming library

If you are not familiar with Mutiny, please check Mutiny - an intuitive reactive programming library.

让我们使用 Mutiny 的响应式编程重写我们的应用程序。

Let’s rewrite our application using reactive programming with Mutiny.

首先,让我们声明另一个以响应式方式工作的 DAO 接口:

First, let’s declare another DAO interface that works in a reactive way:

@Dao
public interface ReactiveFruitDao {

  @Update
  Uni<Void> updateAsync(Fruit fruit);

  @Select
  MutinyMappedReactiveResultSet<Fruit> findAll();
}

请注意使用 MutinyMappedReactiveResultSet——它是一种专门的 Mutiny 类型,从驱动程序返回的原始 Publisher 转换而来,它还公开了一些额外的方法,例如获取查询执行信息。如果您不需要该接口中的任何内容,您还可以简单地声明您的方法返回 MultiMulti<Fruit> findAll()

Note the usage of MutinyMappedReactiveResultSet - it is a specialized Mutiny type converted from the original Publisher returned by the driver, which also exposes a few extra methods, e.g. to obtain the query execution info. If you don’t need anything in that interface, you can also simply declare your method to return Multi: Multi<Fruit> findAll(),

同样,方法 updateAsync 返回一个 Uni——它会从驱动程序返回的原始结果集自动转换而来。

Similarly, the method updateAsync returns a Uni - it is automatically converted from the original result set returned by the driver.

Cassandra 驱动程序使用响应式流 Publisher API 進行响应式调用。但是,Quarkus 框架使用 Mutiny。因此,CqlQuarkusSession 接口会透明地将驱动程序返回的 Publisher 实例转换为响应式类型 MultiCqlQuarkusSession 也可以将 Publisher 转换为 Uni——在这种情况下,发布者预期至多发送一行,然后完成。这适用于写查询(它们不返回任何行),或保证至多返回一行的读查询(例如,计数查询)。

The Cassandra driver uses the Reactive Streams Publisher API for reactive calls. The Quarkus framework however uses Mutiny. Because of that, the CqlQuarkusSession interface transparently converts the Publisher instances returned by the driver into the reactive type Multi. CqlQuarkusSession is also capable of converting a Publisher into a Uni – in this case, the publisher is expected to emit at most one row, then complete. This is suitable for write queries (they return no rows), or for read queries guaranteed to return one row at most (count queries, for example).

接下来,我们需要调整 FruitMapper 来构造一个 ReactiveFruitDao 实例:

Next, we need to adapt the FruitMapper to construct a ReactiveFruitDao instance:

@Mapper
public interface FruitMapper {
  // the existing method omitted

  @DaoFactory
  ReactiveFruitDao reactiveFruitDao();
}

现在,我们可以创建一个利用我们的响应式 DAO 的 ReactiveFruitService

Now, we can create a ReactiveFruitService that leverages our reactive DAO:

@ApplicationScoped
public class ReactiveFruitService {

  @Inject ReactiveFruitDao fruitDao;

  public Uni<Void> add(Fruit fruit) {
    return fruitDao.update(fruit);
  }

  public Multi<Fruit> getAll() {
    return fruitDao.findAll();
  }
}

最后,我们可以创建一个 ReactiveFruitResource

Finally, we can create a ReactiveFruitResource:

@Path("/reactive-fruits")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ReactiveFruitResource {

  @Inject ReactiveFruitService service;

  @GET
  public Multi<FruitDto> getAll() {
    return service.getAll().map(this::convertToDto);
  }

  @POST
  public Uni<Void> add(FruitDto fruitDto) {
    return service.add(convertFromDto(fruitDto));
  }

  private FruitDto convertToDto(Fruit fruit) {
    return new FruitDto(fruit.getName(), fruit.getDescription());
  }

  private Fruit convertFromDto(FruitDto fruitDto) {
    return new Fruit(fruitDto.getName(), fruitDto.getDescription());
  }
}

上述资源公开了一个新的端点 reactive-fruits。它的功能与我们之前用 FruitResource 创建的功能相同,但一切都以响应式的方式处理,没有任何阻塞操作。

The above resource is exposing a new endpoint, reactive-fruits. Its capabilities are identical to the ones that we created before with FruitResource, but everything is handled in a reactive fashion, without any blocking operation.

上面的 getAll() 方法返回 Multi,而 add() 方法返回 Uni。这些类型与我们之前遇到的 Mutiny 类型相同;Quarkus 响应式 REST API 会自动识别它们,因此我们不必自己将其转换为 JSON。

The getAll() method above returns Multi, and the add() method returns Uni. These types are the same Mutiny types that we met before; they are automatically recognized by the Quarkus reactive REST API, so we don’t need to convert them into JSON ourselves.

Quarkus REST 本机支持 Mutiny 响应式类型,例如 UniMulti

Quarkus REST natively supports the Mutiny reactive types e.g. Uni and Multi.

此依赖项已包含在此指南的 pom.xml 中,但如果您要从头开始一个新项目,请确保包含它。

This dependency is already included in this guide’s pom.xml, but if you are starting a new project from scratch, make sure to include it.

Testing the Reactive REST API

如上文所述,以开发模式运行该应用程序,然后你就可以使用 curl 命令与底层 REST API 交互。

Run the application in dev mode as explained above, then you can use curl commands to interact with the underlying REST API.

使用反应式 REST 端点创建水果:

To create a fruit using the reactive REST endpoint:

curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"name":"banana","description":"yellow and sweet"}' \
  http://localhost:8080/reactive-fruits

使用反应式 REST 端点检索水果:

To retrieve fruits with the reactive REST endpoint:

curl -X GET http://localhost:8080/reactive-fruits

Creating a Reactive Frontend

现在,我们来添加一个简单的网页以与我们的 ReactiveFruitResource 进行交互。在 src/main/resources/META-INF/resources 目录中,使用 this file 中的内容添加一个 reactive-fruits.html 文件。

Now let’s add a simple web page to interact with our ReactiveFruitResource. In the src/main/resources/META-INF/resources directory, add a reactive-fruits.html file with the contents from this file in it.

你现可与你的反应式 REST 服务进行交互:

You can now interact with your reactive REST service:

Health Checks

如果你使用的是 Quarkus SmallRye Health 扩展,那么 Cassandra 客户端将自动添加一个准备就绪运行状况检查来验证与 Cassandra 集群的连接。此扩展已包括在本指南的 pom.xml 中,但如果你需要在应用程序中手动将其包含在内,请添加以下内容:

If you are using the Quarkus SmallRye Health extension, then the Cassandra client will automatically add a readiness health check to validate the connection to the Cassandra cluster. This extension is already included in this guide’s pom.xml, but if you need to include it manually in your application, add the following:

<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-smallrye-health</artifactId>
</dependency>

当运行状况检查可用时,你可以访问应用程序的 /health/ready 端点,并获得有關连接验证状态的信息。

When health checks are available, you can access the /health/ready endpoint of your application and have information about the connection validation status.

mvn clean quarkus:dev 的开发模式下运行,如果你将浏览器指向 [role="bare"] [role="bare"]http://localhost:8080/health/ready ,应该会看到类似于以下内容的输出:

Running in dev mode with mvn clean quarkus:dev, if you point your browser to [role="bare"]http://localhost:8080/health/ready you should see an output similar to the following one:

{
    "status": "UP",
    "checks": [
        {
            "name": "DataStax Apache Cassandra Driver health check",
            "status": "UP",
            "data": {
                "cqlVersion": "3.4.4",
                "releaseVersion": "3.11.7",
                "clusterName": "Test Cluster",
                "datacenter": "datacenter1",
                "numberOfNodes": 1
            }
        }
    ]
}

如果你需要在应用程序中全局启用运行状况检查,但不希望激活 Cassandra 运行状况检查,你可以通过在 application.properties 中将 quarkus.cassandra.health.enabled 属性设置为 false 来禁用 Cassandra 运行状况检查。

If you need health checks globally enabled in your application, but don’t want to activate Cassandra health checks, you can disable Cassandra health checks by setting the quarkus.cassandra.health.enabled property to false in your application.properties.

Metrics

Cassandra Quarkus 客户端可以提供有关 Cassandra 会话和各个 Cassandra 节点的指标。它支持 Micrometer 和 MicroProfile。

The Cassandra Quarkus client can provide metrics about the Cassandra session and about individual Cassandra nodes. It supports both Micrometer and MicroProfile.

启用指标的第一步是添加一些其他依赖项,具体取决于你计划使用的指标框架。

The first step to enable metrics is to add a few additional dependencies depending on the metrics framework you plan to use.

Enabling Metrics with Micrometer

Micrometer 是 Quarkus 应用程序中推荐的指标框架。

Micrometer is the recommended metrics framework in Quarkus applications.

要在应用程序中启用 Micrometer 指标,你需要在 pom.xml 中添加以下内容。

To enable Micrometer metrics in your application, you need to add the following to your pom.xml.

<dependency>
  <groupId>com.datastax.oss</groupId>
  <artifactId>java-driver-metrics-micrometer</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-micrometer-registry-prometheus</artifactId>
</dependency>

本指南使用 Micrometer,因此上述依赖项已包含在本指南的 pom.xml 中。

This guide uses Micrometer, so the above dependencies are already included in this guide’s pom.xml.

Enabling Metrics with MicroProfile Metrics

从 pom.xml 中删除对 Micrometer 的所有依赖项,然后添加以下依赖项:

Remove any dependency to Micrometer from your pom.xml, then add the following ones instead:

<dependency>
  <groupId>com.datastax.oss</groupId>
  <artifactId>java-driver-metrics-microprofile</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-smallrye-metrics</artifactId>
</dependency>

Enabling Cassandra Metrics

即使你的应用程序中启用了指标,Cassandra 客户端也不会报告任何指标,除非你选择此功能。因此你的下一步是在 application.properties 文件中启用 Cassandra 指标。

Even when metrics are enabled in your application, the Cassandra client will not report any metrics, unless you opt in for this feature. So your next step is to enable Cassandra metrics in your application.properties file.

quarkus.cassandra.metrics.enabled=true

就是这样!

That’s it!

最后(可选)的一步是自定义 Cassandra 客户端要跟踪的特定 Cassandra 指标。可以跟踪几个指标;如果你跳过此步骤,将自动跟踪一组有用的默认指标。

The final (and optional) step is to customize which specific Cassandra metrics you would like the Cassandra client to track. Several metrics can be tracked; if you skip this step, a default set of useful metrics will be automatically tracked.

有关可用指标名称的完整列表,请参阅 driver settings reference页面;搜索`advanced.metrics`部分。同时, driver manual中详细介绍了 Cassandra 驱动器指标。

For the full list of available metric names, please refer to the driver settings reference page; search for the advanced.metrics section. Also, Cassandra driver metrics are covered in detail in the driver manual.

如果您确实希望自定义要跟踪的指标,则应使用以下属性:

If you do wish to customize which metrics to track, you should use the following properties:

  • quarkus.cassandra.metrics.session.enabled should contain the session-level metrics to enable (metrics that are global to the session).

  • quarkus.cassandra.metrics.node.enabled should contain the node-level metrics to enable (metrics for which each node contacted by the Cassandra client gets its own metric value).

这两个属性都接受有效指标名称的逗号分隔列表。

Both properties accept a comma-separated list of valid metric names.

例如,假设您希望启用以下三个 Cassandra 指标:

For example, let’s assume that you wish to enable the following three Cassandra metrics:

  • Session-level: session.connected-nodes and session.bytes-sent;

  • Node-level: node.pool.open-connections.

然后,您应该将以下设置添加到`application.properties`中:

Then you should add the following settings to your application.properties:

quarkus.cassandra.metrics.enabled=true
quarkus.cassandra.metrics.session.enabled=connected-nodes,bytes-sent
quarkus.cassandra.metrics.node.enabled=pool.open-connections

本指南的`application.properties`文件已经启用了许多指标;您可以将其指标列表用作在应用程序中公开有用的 Cassandra 指标的一个良好起点。

This guide’s application.properties file has already many metrics enabled; you can use its metrics list as a good starting point for exposing useful Cassandra metrics in your application.

正确启用指标后,您可以在应用程序的`/metrics`REST 端点处获得所有已启用指标的指标报告。

When metrics are properly enabled, metric reports for all enabled metrics are available at the /metrics REST endpoint of your application.

mvn clean quarkus:dev`中运行开发模式,如果将浏览器指向`http://localhost:8080/metrics,您应该会看到指标列表;搜索其名称包含`cassandra`的指标。

Running in dev mode with mvn clean quarkus:dev, if you point your browser to http://localhost:8080/metrics you should see a list of metrics; search for metrics whose names contain cassandra.

要显示 Cassandra 指标,需要初始化和连接 Cassandra 客户端;如果您使用延迟初始化(见下文),您不会看到任何 Cassandra 指标,直到您的应用程序实际连接并首次访问数据库为止。

For Cassandra metrics to show up, the Cassandra client needs to be initialized and connected; if you are using lazy initialization (see below), you won’t see any Cassandra metrics until your application actually connects and hits the database for the first time.

Running in native mode

如果您安装了 GraalVM,可以使用以下方式进行build a native image

If you installed GraalVM, you can build a native image using:

mvn clean package -Dnative

请注意,本机编译可能需要大量时间!编译完成后,您可以按如下方式运行本机可执行文件:

Beware that native compilation can take a significant amount of time! Once the compilation is done, you can run the native executable as follows:

./target/cassandra-quarkus-quickstart-*-runner

然后您可以将浏览器指向 http://localhost:8080/fruits.html,并使用您的应用程序。

You can then point your browser to http://localhost:8080/fruits.html and use your application.

Choosing between eager and lazy initialization

如上所述,此扩展程序允许您注入多种类型的 Bean:

As explained above, this extension allows you to inject many types of beans:

  • A simple bean like QuarkusCqlSession or FruitDao;

  • The asynchronous version of that bean, for example CompletionStage<QuarkusCqlSession> or

    `CompletionStage<FruitDao&gt
  • The reactive version of that bean, for example Uni<QuarkusCqlSession> or Uni<FruitDao>.

最直接的方法显然是直接注入 Bean。这应该适用于大多数应用程序。但是,`QuarkusCqlSession`Bean 以及依赖于它的所有 DAO Bean 在首次使用之前可能需要一些时间初始化,并且此过程正在阻塞。

The most straightforward approach is obviously to inject the bean directly. This should work just fine for most applications. However, the QuarkusCqlSession bean, and all DAO beans that depend on it, might take some time to initialize before they can be used for the first time, and this process is blocking.

幸运的是,可以控制初始化应该何时发生:参数`quarkus.cassandra.init.eager-init`确定是否应该在第一次访问时初始化 QuarkusCqlSession`Bean(延迟)或者在应用程序启动时(热切)初始化。此参数的默认值为 `false,表示 init 过程是延迟的:`QuarkusCqlSession`Bean 将在其第一次访问时延迟初始化——例如,当有第一个 REST 请求需要与 Cassandra 数据库进行交互时。

Fortunately, it is possible to control when the initialization should happen: the quarkus.cassandra.init.eager-init parameter determines if the QuarkusCqlSession bean should be initialized on its first access (lazy) or when the application is starting (eager). The default value of this parameter is false, meaning the init process is lazy: the QuarkusCqlSession bean will be initialized lazily on its first access – for example, when there is a first REST request that needs to interact with the Cassandra database.

使用延迟初始化可以加快应用程序的启动时间,并在 Cassandra 数据库不可用时避免启动失败。但是,如果你的代码完全非阻塞,例如它使用了 reactive routes,则它也可能被证明是危险的。事实上,延迟的初始化可能会在不允许阻塞的线程上意外发生,例如 Vert.x 事件循环线程。因此,在这些上下文中应该避免将 quarkus.cassandra.init.eager-init 设置为 false 并注入 QuarkusCqlSession

Using lazy initialization speeds up your application startup time, and avoids startup failures if the Cassandra database is not available. However, it could also prove dangerous if your code is fully non-blocking, for example if it uses reactive routes. Indeed, the lazy initialization could accidentally happen on a thread that is not allowed to block, such as a Vert.x event loop thread. Therefore, setting quarkus.cassandra.init.eager-init to false and injecting QuarkusCqlSession should be avoided in these contexts.

如果你想使用 Vert.x(或任何其他非阻塞框架)并保持延迟初始化行为,则你应该仅注入所需 Bean 的 CompletionStageUni。在注入这些 Bean 时,将在后台以非阻塞方式利用 Vert.x 事件循环来延迟触发初始化过程。这样你就不会冒着阻塞 Vert.x 线程的风险。

If you want to use Vert.x (or any other non-blocking framework) and keep the lazy initialization behavior, you should instead inject only a CompletionStage or a Uni of the desired bean. When injecting these beans, the initialization process will be triggered lazily, but it will happen in the background, in a non-blocking way, leveraging the Vert.x event loop. This way you don’t risk blocking the Vert.x thread.

或者,你可以将 quarkus.cassandra.init.eager-init 设置为 true:在这种情况下,会话 Bean 和所有 DAO Bean 将在应用程序启动期间在 Quarkus 主线程上热切地初始化。这将消除阻塞 Vert.x 线程的任何风险,代价是让你的启动时间(大大)更长。

Alternatively, you can set quarkus.cassandra.init.eager-init to true: in this case the session bean and all DAO beans will be initialized eagerly during application startup, on the Quarkus main thread. This would eliminate any risk of blocking a Vert.x thread, at the cost of making your startup time (much) longer.

Conclusion

使用 Quarkus 和 Cassandra 扩展从客户端应用程序访问 Cassandra 数据库非常容易,它为 Apache Cassandra 的 DataStax Java 驱动程序提供了配置和本机支持。

Accessing a Cassandra database from a client application is easy with Quarkus and the Cassandra extension, which provides configuration and native support for the DataStax Java driver for Apache Cassandra.