Infinispan Client Extension Reference Guide

Infinispan 是一个分布式的内存中键/值存储,为 Quarkus 应用程序提供了高度可配置且可独立扩展的数据层。此扩展为您提供了将运行在 Quarkus 上的应用程序与远程 Infinispan 集群连接的客户端功能。若要开始使用 Infinispan,我们建议您:

Infinispan is a distributed, in-memory key/value store that provides Quarkus applications with a highly configurable and independently scalable data layer. This extension gives you client functionality that connects applications running on Quarkus with remote Infinispan clusters. To get started with Infinispan, we recommend:

  1. Following the Get Started Tutorial (5 minutes).

  2. Running the remote cache simple code tutorials.

Infinispan documentation 中了解详情。

Learn more in the Infinispan documentation.

Installation

在您的 Quarkus 项目的基本目录中运行以下命令,以添加 infinispan-client 扩展:

Run the following command in the base directory of your Quarkus project to add the infinispan-client extension:

Unresolved directive in infinispan-client-reference.adoc - include::{includes}/devtools/extension-add.adoc[]

此命令会将以下依赖项添加到您的构建文件:

This command adds the following dependency to your build file:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-infinispan-client</artifactId>
</dependency>
build.gradle
implementation 'io.quarkus:quarkus-infinispan-client'
annotationProcessor 'org.infinispan.protostream:protostream-processor:{infinispan-protostream-version}' 1
1 Mandatory in the Gradle build to enable the generation of the files in the annotation based serialization

Connection to Infinispan

Running the server

您至少需要运行一个 Infinispan Server 实例。

You need at least one running instance of the Infinispan Server.

Development mode

如果您正在运行 Docker 实例,则可以使用 Infinispan Dev Services 并无需配置连接。

Development mode

If you are running a Docker instance, you can use Infinispan Dev Services and connect without configuration.

如果您想使用 Docker 亲自运行服务器,请查看 5 分钟的 Getting stated with Infinispan 教程来运行 Infinispan Server。

If you want to run the server yourself using Docker, check out the 5-minute Getting stated with Infinispan tutorial to run Infinispan Server

您还可以 download {infinispan-version} Server 裸机分发包,并从 distribution 文件夹中运行以下命令。

You can also download {infinispan-version} Server bare metal distribution and run the following command from the distribution folder.

$ ./bin/server.sh

Infinispan Server enables authentication and security authorization by default,所以您需要创建一个具有权限的用户。

Infinispan Server enables authentication and security authorization by default, so you need to create a user with permissions.

  • If you run the Infinispan Server image, pass the USER="admin" and PASS="password" parameters.

  • If you run the bare metal distribution, use the Command Line Interface (CLI) as follows:[source, bash]

$ ./bin/cli.sh user create admin -p password
Production mode

在 Kubernetes 中,我们建议使用 Infinispan Operator。此外,请看一下 Cross Site Replication 教程。您将学会如何使用 docker compose(用于本地开发)和 Operator 运行两个单独的 Infinispan 集群。

Production mode

In Kubernetes, we recommend the Infinispan Operator. Additionally, grab a look to the Cross Site Replication tutorial. You will learn how to run two separate Infinispan Clusters with docker compose (for local dev) and the Operator.

Configuring the connection

如果您运行的是 Infinispan Server,请添加以下属性以在 src/main/resources 目录中的 application.properties 文件中进行连接。

If you are running an Infinispan Server, add the following properties to connect in the application.properties file in the src/main/resources directory.

quarkus.infinispan-client.hosts=localhost:11222 1

quarkus.infinispan-client.username=admin 2
quarkus.infinispan-client.password=password 3
1 Sets Infinispan Server address list, separated with commas
2 Sets the authentication username
3 Sets the authentication password

或者,您可以通过提供单个连接属性使用 uri 连接

Alternatively, you can use uri connection by providing a single connection property

quarkus.infinispan-client.uri=hotrod://admin:password@localhost:11222 1
1 Sets Infinispan URI connection. The following properties will be ignored: hosts, username and password.

使用 Infinispan Dev Services 运行服务器并在无配置的情况下进行连接。

Use Infinispan Dev Services to run a server and connect without configuration.

Client intelligence

Infinispan 客户端使用智能机制有效地向 Infinispan Server 群集发送请求。默认情况下,启用了 HASH_DISTRIBUTION_AWARE 智能机制。不过,在本地使用 Docker for Mac 时,您可能会遇到连接问题。在这种情况下,将客户端智能配置为 BASIC

Infinispan client uses intelligence mechanisms to efficiently send requests to Infinispan Server clusters. By default, the HASH_DISTRIBUTION_AWARE intelligence mechanism is enabled. However, locally with Docker for Mac, you might experience connectivity issues. In this case, configure the client intelligence to BASIC.

Infinispan documentation 中了解更多信息。

Learn more in the Infinispan documentation.

quarkus.infinispan-client.client-intelligence=BASIC 1
1 Docker for Mac workaround.

默认情况下,请勿在生产环境中使用 BASIC,否则可能会影响性能。

Don’t use BASIC in production environments by default, performance might be impacted.

Configuring backup clusters in Cross-Site Replication

在高可用性生产部署中,通常会存在多个 Infinispan 群集,它们分布在全球各地不同的数据中心。Infinispan 提供了连接这些群集并配置它们之间的备份的能力。这可以通过使用单一连接通过自动/手动方法在群集之间无缝切换来实现。为了实现这一点,必须将客户端配置为直接连接到备份群集。

In High Availability production deployments, it is common to have multiple Infinispan Clusters that are distributed across various Data Centers worldwide. Infinispan offers the capability to connect these clusters and configure backups between them. This enables seamless switching between clusters through both automated and manual methods using a single connection. To achieve this, it is necessary to configure the client to direct to the backup clusters.

quarkus.infinispan-client.hosts=host1:11222,host2:3122 1
quarkus.infinispan-client.username=admin
quarkus.infinispan-client.password=password
quarkus.infinispan-client.backup-cluster.nyc-site.hosts=nyc1:11222,nyc2:21222,nyc3:31222 2
quarkus.infinispan-client.backup-cluster.nyc-site.client-intelligence=BASIC 3
quarkus.infinispan-client.backup-cluster.lon-site.hosts=lon1:11222,lon2:21222,lon3:31222 4
1 Sets Infinispan Server address list, separated with commas. This is the default cluster.
2 Configures a backup site 'nyc-site' with the provided address list
3 Configures a backup site 'nyc-site' with the provided client intelligence
4 Configures an additional backup site 'lon-site' with the provided address list

基于提供的配置,在默认群集变得不可用时,客户端将无缝切换到可访问的备份群集中的一个。此外,还可以选择手动将客户端切换到备用群集:

Based on the provided configuration, in the event of the default cluster becoming unavailable, the client will seamlessly transition to one of the accessible backup clusters. Additionally, there is also the option to manually switch the client to an alternate cluster:

@ApplicationScoped
public class InfinispanExample {
    @Inject
    RemoteCacheManager cacheManager;

    public void doSomething() {
       cacheManager.switchToCluster("nyc-site"); (1)
       cacheManager.switchToCluster("lon-site"); (2)
       cacheManager.switchToDefaultCluster(); (3)
    }
}
1 The client connects to the 'nyc-site'.
2 The client connects to the 'lon-site'.
3 The client connects to the default site.

默认情况下,Protobuf 架构也会上传到备份群集。但是,可能需要手动处理注册,因为架构在生产中使用时可能会随着时间而演变,因此可以通过将 quarkus.infinispan-client.backup-cluster.YOUR_SITE_NAME.use-schema-registration 配置为 false 来禁止在每个备份站点中发生这种情况。如果 use-schema-registration 全局属性为 false,则此属性的值将被忽略。

By default, Protobuf Schemas will also be uploaded to the backup clusters. However, it might be required to handle the registration manually as a schema may evolve over time when used in production, so you can disable this from occurring in each backup site by configuring the quarkus.infinispan-client.backup-cluster.YOUR_SITE_NAME.use-schema-registration to false. The value of this property will be ignored if the use-schema-registration global property is false.

跨站点复制是 Infinispan 提供的一项强大功能,它可以促进地理位置分散的数据中心(甚至跨越不同的云提供商)之间的群集数据备份。在 Infinispan documentation 中了解更多信息。

Cross-site replication is a powerful feature offered by Infinispan that facilitates data backup between clusters situated in geographically diverse data centers, even spanning across various cloud providers. Learn more in the Infinispan documentation.

Default and named connections

此扩展程序允许您配置多个 default Infinispan 客户端连接和 named 连接。命名连接对于连接到多个 Infinispan 群集至关重要。

This extension lets you configure a default Infinispan client connections and named ones. Named connections are essential to connect to multiple Infinispan clusters.

默认连接使用如上所示的 quarkus.infinispan-client.* 属性进行配置。使用默认连接时,你可以使用 plain @Inject 注入:

The default connection is configured using the quarkus.infinispan-client.* properties as seen above. When using the default connection, you can inject using a plain @Inject:

Named 客户端使用 quarkus.infinispan-client.<name>.* 属性进行配置:

Named clients are configured using the quarkus.infinispan-client.<name>.* properties:

quarkus.infinispan-client.site-lon.hosts=localhost:11222
quarkus.infinispan-client.site-lon.username=admin
quarkus.infinispan-client.site-lon.password=password

quarkus.infinispan-client.site-nyc.hosts=localhost:31222
quarkus.infinispan-client.site-nyc.username=admin
quarkus.infinispan-client.site-nyc.password=password

使用依赖注入的 @InfinispanClientName 限定符:

Use the @InfinispanClientName qualifier with dependency injection:

@ApplicationScoped
public class InfinispanExample {
    @Inject
    @InfinispanClientName("site-lon")
    RemoteCacheManager rcmLon;

    @Inject
    @InfinispanClientName("site-nyc")
    RemoteCacheManager rmcNyc;
}

Infinispan Health Check

如果你正在使用 quarkus-smallrye-health 扩展,Infinispan 客户端扩展将自动添加一个准备就绪运行状况检查来验证连接。

If you are using the quarkus-smallrye-health extension, the Infinispan client extensions will automatically add a readiness health check to validate the connection.

当你访问应用程序的 /q/health/ready 终结点时,你将获得服务器连接和可用缓存相关的信息。

When you access the /q/health/ready endpoint of your application you will have information about the server connection and available caches.

可以通过 quarkus.infinispan-client.health.enabled 属性禁用此行为。

This behavior can be disabled via the property quarkus.infinispan-client.health.enabled.

Tracing with OpenTelemetry

Infinispan 支持通过 OpenTelemetry 检测服务器。拥有 quarkus-opentelemetry 扩展将把跟踪从 Infinispan 客户端传播到服务器。可以通过 quarkus.infinispan-client.tracing.propagation.enabled 属性禁用此行为。

Infinispan supports instrumentation of the server via OpenTelemetry. Having the quarkus-opentelemetry extension will propagate the traces from the Infinispan Client to the Server. This behavior can be disabled via the property quarkus.infinispan-client.tracing.propagation.enabled

Creating caches from the client

当从客户端访问缓存时,如果缓存不存在于 Infinispan 服务器中,并且你希望在首次访问时创建该缓存,请使用以下属性之一:

When a cache is accessed from the client, if the cache does not exist in the Infinispan Server and you want to create it on first access, use one of the following properties:

quarkus.infinispan-client.cache.magazine.configuration=<distributed-cache><encoding media-type="application/x-protostream"/></distributed-cache> 1
quarkus.infinispan-client.cache.books.configuration-resource=booksDistributedCache.json 2
quarkus.infinispan-client.cache.authors.configuration-uri=/file/authorsIndexedCache.yaml 3
1 The configuration in xml of the 'magazine' (yaml and json are also supported)
2 The file name located under the resources folder that contains the configuration of the 'books' cache
3 A provided file URI. The file URI can also be a file under resources

如果 configuration-resourceconfigurationconfiguration-uri 为同个缓存配置了同一个 Quarkus 配置,configuration-uri 优先级最高,高于 configuration-resourceconfigurationconfiguration-resource 优先级高于 configuration

If configuration-resource, configuration and configuration-uri are configured for the same cache with the same Quarkus profile, configuration-uri gets the highest preference, over configuration-resource and configuration. configuration-resource gets preference over configuration.

configuration-resource 是构建时属性,文件将自动包含在原生版本中。configuration-uri 也可以指向 resources 文件夹下的文件。不过除非你配置 quarkus.native.resources.includes 属性,否则文件不会自动包含在原生可执行文件中。

The configuration-resource is build time property and the file will be included in the native build automatically. configuration-uri can also point to a file under the resources folder. However, the file won’t be automatically included in the native executable, unless you configure the property quarkus.native.resources.includes.

缓存配置可以用 XML、JSON 或 YAML 提供。使用 Infinispan 控制台和缓存配置向导来了解有关 Infinispan 缓存的更多信息,并创建指导性配置。

Cache configuration can be provided in XML, JSON or YAML. Use the Infinispan Console and the cache configuration Wizard to learn more about Infinispan Caches and create guided configurations.

如果为特定缓存没有配置任何内容,它将使用以下基本配置创建:

If nothing is configured for a particular cache, it will be created with the following basic configuration:

XML
<distributed-cache>
    <encoding media-type="application/x-protostream"/>
</distributed-cache>
JSON
{
    "distributed-cache": {
        "encoding": {
            "media-type": "application/x-protostream"
        }
    }
}
YAML
distributedCache:
  encoding:
    mediaType: "application/x-protostream"

Authentication mechanisms

你可以对 Infinispan 客户端使用以下身份验证机制:

You can use the following authentication mechanisms with the Infinispan client:

  • DIGEST-MD5

  • PLAIN (recommended only in combination with TLS encryption)

  • EXTERNAL

其他身份验证机制,如 SCRAM 和 GSSAPI,尚未在 Infinispan 客户端中经过验证。

Other authentication mechanisms, such as SCRAM and GSSAPI, are not yet verified with the Infinispan client.

你可以在 Hot Rod Endpoint Authentication Mechanisms 中找到有关配置身份验证的更多信息。

You can find more information on configuring authentication in Hot Rod Endpoint Authentication Mechanisms.

如果你使用依赖注入,则你必须在 hotrod-client.properties 文件中配置身份验证。

You must configure authentication in the hotrod-client.properties file if you use Dependency Injection.

Serialization (Key Value types support)

默认情况下,客户端将支持以下类型的键和值:byte[]、primitive 包装器(例如 Integer、Long、Double)、String、Date 和 Instant。用户类型要求一些额外的步骤,这些步骤在此处详细介绍。我们假设有以下用户类:

By default, the client will support keys and values of the following types: byte[], primitive wrappers (e.g. Integer, Long, Double), String, Date and Instant. User types require some additional steps that are detailed here. Let’s say we have the following user classes:

Author.java
public record Author(String name, String surname) {
}
Book.java
public record Book(String title,
                   String description,
                   int publicationYear,
                   Set<Author> authors,
                   Type bookType,
                   BigDecimal price) {
}

用户类型序列化使用一个基于 protobuf 的库,称为 Protostream

Serialization of user types uses a library based on protobuf, called Protostream.

Infinispan 缓存可以在不同编码中存储键和值,但建议使用 Protocol Buffers (Protobuf)

Infinispan caches can store keys and values in different encodings, but recommend using Protocol Buffers (Protobuf).

有关更多信息,请参阅我们的 Cache Encoding and Marshalling 指南。

For more information see our Cache Encoding and Marshalling guide.

Annotation based Serialization

这可以通过向用户类添加 protostream 注释自动完成。此外,还需要一个用 Initializer 注释的单个接口,该接口控制如何生成支持类。

This can be done automatically by adding protostream annotations to your user classes. In addition, a single Initializer annotated interface is required which controls how the supporting classes are generated.

下面是上一个类应该如何更改的示例:

Here is an example of how the preceding classes should be changed:

Author.java
@Proto (1)
public record Author(String name, String surname) { (2)
}
1 Since Protostream 5.0, a single annotation is needed to generate a default mapping
2 Since Protostream 5.0, records are supported
Type.java
@Proto
public enum Type { (1)
    FANTASY,
    PROGRAMMING
}
1 Enums are supported
Book.java
@Proto
@Indexed (1)
public record Book(@Text String title, (2)
                   @Keyword(projectable = true, sortable = true, normalizer = "lowercase", indexNullAs = "unnamed", norms = false) (3)
                   String description,
                   int publicationYear,
                   Set<Author> authors, (4)
                   Type bookType,
                   BigDecimal price) { (5)
}
1 Indicates that the entity will be indexed. Necessary to perform distributed full-text query operations.
2 Indicates the title should be indexed as text
3 Indicates the description field field should be indexed as a Keyword.
4 Collections are supported
5 Protostream provides default Protobuf mappers for commonly used types as BigDecimal, included in the org.infinispan.protostream.types package.

然后所需的就是一个非常简单的 GeneratedSchema 接口,其上带有注释以指定配置设置

Then all that is required is a very simple GeneratedSchema interface with an annotation on it to specify configuration settings

BooksSchema.java
import org.infinispan.protostream.GeneratedSchema;
import org.infinispan.protostream.annotations.ProtoSchema;
import org.infinispan.protostream.types.java.math.BigDecimalAdapter;

@ProtoSchema(includeClasses = { Book.class, Author.class, BigDecimalAdapter.class }, schemaPackageName = "book_sample")
interface BookStoreSchema extends GeneratedSchema {
}

你可以使用 basePackages 属性扫描一个包含你的类的完整包。你可以使用 @Protofield 注释覆盖默认编组。

You can use the basePackages property to scan a full package containing your classes. You can override the default marshalling by using the @Protofield annotation.

因此,在这种情况下,我们将自动生成已包含类的 marshaller 和 schema,并自动将它们放在 schema 包中。不必提供这个包,但如果你使用 Infinispan 搜索功能,你必须知道生成的包。

So in this case we will automatically generate the marshaller and schemas for the included classes and place them in the schema package automatically. The package does not have to be provided, but if you use Infinispan search capabilities, you must know the generated package.

在 Quarkus 中,不应该在 ProtoSchema 注释中设置 schemaFileNameschemaFilePath 属性。设置任何一个属性都会导致本机运行时错误。

In Quarkus the schemaFileName and schemaFilePath attributes should NOT be set on the ProtoSchema annotation. Setting either attributes causes native runtime errors.

Custom serialization

当用户可以注释其类时,建议使用前一种方法。不幸的是,用户可能无法注释他们要放入缓存中的所有类。在这种情况下,你必须定义自己的 schema 并自己创建自己的 Marshaller。

The previous method is suggested for any case when the user can annotate their classes. Unfortunately the user may not be able to annotate all classes they will put in the cache. In this case you must define your schema and create your own Marshaller(s) yourself.

Protobuf schema

You can supply a protobuf schema through either one of two ways.

  1. Proto File

You can put the .proto file in the META-INF directory of the project. These files will automatically be picked up at initialization time..library.proto

package book_sample;

message Book {
  required string title = 1;
  required string description = 2;
  required int32 publicationYear = 3; // no native Date type available in Protobuf
  repeated Author authors = 4;
  requited double price = 5; // no native BigDecimal type available in Protobuf but you can use the adapter
}

message Author {
  required string name = 1;
  required string surname = 2;
}
  1. In Code

Or you can define the proto schema directly in user code by defining a produced bean of type org.infinispan.protostream.schema.Schema.[source, java]

   @Produces
   Schema bookSchema() {
        return new Schema.Builder("book.proto")
                .packageName("book_sample")
                .addMessage("Author")
                     .addField(Type.Scalar.STRING, "name", 1)
                     .addField(Type.Scalar.STRING, "surname", 2)
                .addMessage("Book")
                     .addField(Type.Scalar.STRING, "title", 1)
                     .addField(Type.Scalar.STRING, "description", 2)
                     .addField(Type.Scalar.INT32, "publicationYear", 3)
                     .addRepeatedField(Type.create("Author"), "author", 4)
                     .addField(Type.Scalar.DOUBLE, "price", 5)
                .build();
    }
User Marshaller

The last thing to do is to provide a org.infinispan.protostream.MessageMarshaller implementation for each user class defined in the proto schema. This class is then provided via @Produces in a similar fashion to the code based proto schema definition above.

下面是我们的 Author 和 Book 类的 Marshaller 类。

Here is the Marshaller class for our Author & Book classes.

类型名称必须与 <protobuf package>.<protobuf message> 完全匹配!

The type name must match the <protobuf package>.<protobuf message> exactly!

AuthorMarshaller.java
public class AuthorMarshaller implements MessageMarshaller<Author> {

   @Override
   public String getTypeName() {
      return "book_sample.Author";
   }

   @Override
   public Class<? extends Author> getJavaClass() {
      return Author.class;
   }

   @Override
   public void writeTo(ProtoStreamWriter writer, Author author) throws IOException {
      writer.writeString("name", author.getName());
      writer.writeString("surname", author.getSurname());
   }

   @Override
   public Author readFrom(ProtoStreamReader reader) throws IOException {
      String name = reader.readString("name");
      String surname = reader.readString("surname");
      return new Author(name, surname);
   }
}
BookMarshaller.java
public class BookMarshaller implements MessageMarshaller<Book> {

   @Override
   public String getTypeName() {
      return "book_sample.Book";
   }

   @Override
   public Class<? extends Book> getJavaClass() {
      return Book.class;
   }

   @Override
   public void writeTo(ProtoStreamWriter writer, Book book) throws IOException {
      writer.writeString("title", book.getTitle());
      writer.writeString("description", book.getDescription());
      writer.writeInt("publicationYear", book.getPublicationYear());
      writer.writeCollection("authors", book.getAuthors(), Author.class);
      writer.writeDouble("price", book.getPrice().doubleValue());
   }

   @Override
   public Book readFrom(ProtoStreamReader reader) throws IOException {
      String title = reader.readString("title");
      String description = reader.readString("description");
      int publicationYear = reader.readInt("publicationYear");
      Set<Author> authors = reader.readCollection("authors", new HashSet<>(), Author.class);
      BigDecimal price = BigDecimal.valueOf(reader.readDouble("price"));
      return new Book(title, description, publicationYear, authors, price);
   }
}

你可以通过定义以下内容来传递 marshaller:

And you pass the marshaller by defining the following:

   @Produces
   MessageMarshaller authorMarshaller() {
      return new AuthorMarshaller();
   }

   @Produces
   MessageMarshaller bookMarshaller() {
      return new BookMarshaller();
   }

上述产生的 Marshaller 方法一定返回 MessageMarshaller 类型,否则它将不会被找到。

The above produced Marshaller method MUST return MessageMarshaller without types or else it will not be found.

Dependency Injection

如你所见,我们支持用户注入 Marshaller 配置。你可以使用 Infinispan 客户端扩展反转此操作,为 RemoteCacheManagerRemoteCache 对象提供注入。上面各章节设置的所有配置参数将采用一个全局 RemoteCacheManager

As you saw above we support the user injecting Marshaller configuration. You can do the inverse with the Infinispan client extension providing injection for RemoteCacheManager and RemoteCache objects. There is one global RemoteCacheManager that takes all the configuration parameters setup in the above sections.

注入这些组件非常简单。你需要做的全部工作就是将 @Inject 注释添加到字段、构造函数或方法。在下面的代码中,我们使用了字段和构造函数注入。

It is very simple to inject these components. All you need to do is to add the @Inject annotation to the field, constructor or method. In the below code we utilize field and constructor injection.

SomeClass.java
    @Inject
    SomeClass(RemoteCacheManager remoteCacheManager) {
       this.remoteCacheManager = remoteCacheManager;
    }

    @Inject
    @Remote("myCache")
    RemoteCache<String, Book> cache;

    RemoteCacheManager remoteCacheManager;

如果你注意到,RemoteCache 声明有一个名为 Remote 的其他注释。这是一个 qualifier 注释,它允许你指定将注入的命名缓存。此注释不是必需的,如果没有提供,则将注入默认的缓存。RemoteCacheManager 和 RemoteCache bean 范围为 @ApplicationScoped

If you notice the RemoteCache declaration has an additional annotation named Remote. This is a qualifier annotation allowing you to specify which named cache that will be injected. This annotation is not required and if it is not supplied, the default cache will be injected. The RemoteCacheManager and RemoteCache bean scope is @ApplicationScoped.

对于非默认连接,请结合限定符 @InfinispanClientName@Remote

For non default connections, combine the qualifier @InfinispanClientName and @Remote.

SomeClass.java
    @Inject
    @InfinispanClientName("lon-site")
    @Remote("books")
    RemoteCache<String, Book> lonBooks;

    @Inject
    @InfinispanClientName("nyc-site")
    @Remote("books")
    RemoteCache<String, Book> nycBooks;

其他类型也可以支持注入,详情请参见其他章节

Other types may be supported for injection, please see other sections for more information

Mock Support

Quarkus 支持使用 mock 对象,有两种不同的方法。你可以使用 CDI 备用选项来模拟所有测试类的一个 bean,或使用 QuarkusMock 来逐个测试模拟 bean。查看 Getting started with testing guide 以获取更多信息。

Quarkus supports the use of mock objects using two different approaches. You can either use CDI alternatives to mock out a bean for all test classes, or use QuarkusMock to mock out beans on a per test basis. Check the Getting started with testing guide for more information.

RemoteCacheManager 和 RemoteCache 可以被模拟。

RemoteCacheManager and RemoteCache can be mocked.

BookService.java
@ApplicationScoped
public class BookService {

   @Inject
   @Remote("books")
   RemoteCache<String, Book> books; (1)

   public String getBookDescriptionById(String id) {
      Book book = books.get(id);
      if (book == null) {
         return "default";
      }

      return book.getDescription();
   }
}
1 Use dependency injection to connect to the books cache

在测试类中,可以模拟 RemoteCache。

In the test class, the RemoteCache can be mocked.

BookServiceTest.java
@QuarkusTest
public class BookServiceTest {

    @Inject
    BookService bookService;

    @InjectMock (1)
    @Remote("books")
    RemoteCache<String, Book> bookRemoteCache;

    @Test
    public void mockRemoteCache() {
        Mockito.when(bookRemoteCache.get("harry_potter")).thenReturn(new Book(... "Best saga ever");(2)

        Assertions.assertThat(bookService.getBookDescriptionById("harry_potter")).isEqualTo("Best saga ever");(3)
    }
}
1 Inject a mock instead of the RemoteCache bean
2 Use Mockito to mock the call of the RemoteCache
3 Assert the service call

Registering Protobuf Schemas with Infinispan Server

你需要将生成的 Protobuf schema 注册到 Infinispan 服务器,才能执行查询或从 Protobuf 转换为其他媒体类型,例如 JSON

You need to register the generated Protobuf schemas with Infinispan Server to perform queries or convert from Protobuf to other media types such as JSON.

你可以通过 http://SERVER_HOST:SERVER_PORT 中的 Infinispan 控制台中(例如 http://localhost:11222)登录到 Schemas 选项卡下的 schema,来查看已存在的 schema。

You can check the schemas that exist under the Schemas tab by logging into Infinispan Console at http://SERVER_HOST:SERVER_PORT (for example http://localhost:11222).

检查 Infinispan Dev Services Guide 以连接到 InfinispanDev 服务服务器。

Check the Infinispan Dev Services Guide to connect to the Infinispan Dev Services server.

默认情况下,通过这种方式生成的 Protobuf 模式将在客户端首次连接时由此扩展注册。但是,可能需要手动处理注册,因为模式在生产中使用时可能会随着时间的推移而演变,因此可以通过将 quarkus.infinispan-client.use-schema-registration 配置为 false 来禁用此操作。

By default, Protobuf schemas generated this way will be registered by this extension when the client first connects. However, it might be required to handle the registration manually as a schema may evolve over time when used in production, so you can disable this from occurring by configuring the quarkus.infinispan-client.use-schema-registration to false.

要手动配置模式,请使用 Infinispan Operator 进行 Kubernetes 部署、Infinispan 控制台、 REST APIHot Rod Java Client

To configure the schema manually please use Infinispan Operator for Kubernetes deployments, Infinispan Console, REST API or the Hot Rod Java Client.

Caching using annotations

Infinispan 缓存注释已弃用 in this extension 且将被删除。使用或替换注释,方法是使用 Infinispan Cache extension。将你的导入语句更新为使用来自 io.quarkus.cache 包的注释,而不是 io.quarkus.infinispan.client

Infinispan Caching annotations are deprecated in this extension and will be removed. Use or replace your annotations by using the Infinispan Cache extension. Update your import statements to use the annotations from io.quarkus.cache package instead of io.quarkus.infinispan.client.

Infinispan 客户端扩展提供了可在 CDI 托管 Bean 中使用的注释集,以通过 Infinispan 启用缓存功能。

The Infinispan Client extension offers a set of annotations that can be used in a CDI managed bean to enable caching abilities with Infinispan.

在私有方法中不允许使用缓存注解。它们适用于任何其他访问修饰符,包括包私有(无显式修饰符)。

Caching annotations are not allowed on private methods. They will work fine with any other access modifier including package-private (no explicit modifier).

@CacheResult

尽可能在不执行方法体的情况下从缓存中加载方法结果。

Loads a method result from the cache without executing the method body whenever possible.

当使用 @CacheResult 注释的方法被调用时,Quarkus 将使用方法参数作为缓存键,并在缓存中检查该方法是否已调用。不允许使用多个参数的方法。对于复合键,定义一个将保存多个值 Protobuf 模式。如果在缓存中找到了某个值,则返回该值且从不实际执行带注释的方法。如果没有找到值,则调用带注释的方法并将返回值使用计算出的键存储在缓存中。此注释不能用于返回 void 的方法。

When a method annotated with @CacheResult is invoked, Quarkus will use the method argument as the cache key and check in the cache whether the method has been already invoked. Methods with multiple parameters are not allowed. For composite keys, define a Protobuf schema that will hold multiple values. If a value is found in the cache, it is returned and the annotated method is never actually executed. If no value is found, the annotated method is invoked and the returned value is stored in the cache using the computed key. This annotation cannot be used on a method returning void.

与 Quarkus-Cache 扩展不同,Infinispan 客户端扩展目前无法缓存 null 值。

Infinispan Client extension is not able yet to cache null values unlike the Quarkus-Cache extension.

@CacheInvalidate

从缓存中删除一个条目。

Removes an entry from the cache.

当使用 @CacheInvalidate 注释的方法被调用时,Infinispan 将使用方法参数作为缓存键,尝试从缓存中移除现有的条目。如果键未识别任何缓存条目,则什么都不会发生。

When a method annotated with @CacheInvalidate is invoked, Infinispan will use the method argument as a cache key to try to remove an existing entry from the cache. If the key does not identify any cache entry, nothing will happen.

@CacheInvalidateAll

当使用 @CacheInvalidateAll 注释的方法被调用时,Infinispan 将从缓存中移除所有条目。

When a method annotated with @CacheInvalidateAll is invoked, Infinispan will remove all entries from the cache.

Querying

只要上述 ProtoStreamMarshaller 已配置,Infinispan 客户端就支持已索引和未索引的搜索。这允许用户根据 proto 模式的属性查询 keysvaluesIndexed queries are preferred for performance reasons

The Infinispan client supports both indexed and non-indexed search as long as the ProtoStreamMarshaller is configured above. This allows the user to query on keys or values based on the properties of the proto schema. Indexed queries are preferred for performance reasons.

XML
<distributed-cache name="books" statistics="true">
    <!-- other configuration -->
	<indexing enabled="true" storage="filesystem" startup-mode="PURGE">
		<indexed-entities>
			<indexed-entity>book_sample.Book</indexed-entity>
		</indexed-entities>
	</indexing>
</distributed-cache>
JSON
{
  "books": {
    "distributed-cache": {
      ...
      "indexing": {
        "enabled": true,
        "storage": "filesystem",
        "startupMode": "PURGE",
        "indexed-entities": [
          "book_sample.Book"
        ]
      }
    }
  }
}
YAML
distributedCache:
  # other configuration
  indexing:
    enabled: "true"
    storage: "filesystem"
    startupMode: "PURGE"
    indexedEntities:
      - "book_sample.Book"

查询基于设置 ProtoStreamMarshaller 时可以配置的 proto 定义。上述两种序列化方法都会在启动时自动向服务器注册模式,这意味着你将自动获得查询存储在远程 Infinispan 服务器中的对象的能力。

Query builds upon the proto definitions you can configure when setting up the ProtoStreamMarshaller. Either method of Serialization above will automatically register the schema with the server at startup, meaning that you will automatically gain the ability to query objects stored in the remote Infinispan Server.

Book.java
@Indexed 1
public class Book {

    @ProtoFactory
    public Book(String title, String description, int publicationYear, Set<Author> authors) {
      ...
    }

    @ProtoField(number = 1)
    @Text 2
    public String getTitle() {
        return title;
    }

    @ProtoField(number = 2)
    @Keyword(projectable = true, sortable = true, normalizer = "lowercase", indexNullAs = "unnamed", norms = false) 3
    public String getDescription() {
        return description;
    }
    ...
1 @Indexed annotation makes the POJO indexable
2 @Basic annotation is used for indexed fields without any special transformation
3 @Keyword annotation is used to apply a normalizer to a text field

你可以在 Quarkus Infinispan 客户端扩展中使用 Query DSL 或者 Ickle 查询语言。

You can use either the Query DSL or the Ickle Query language with the Quarkus Infinispan client extension.

Query.java
@Inject
@Remote("books")
RemoteCache<String, Book> booksCache; (1)

Query<Book> query = booksCache.query("from book_sample.Book b where b.authors.name like '%" + name + "%'"); (2)
List<Book> list = query.execute().list();
1 Inject the books cache
2 Perform a full text query on books author name

你可以在 Infinispan 文档中阅读更多有关 querying 的信息。

You can read more about querying in the Infinispan documentation.

在 Quarkus 3.9 和 Infinispan 15 集成之前,可以通过调用以下代码来执行查询:Query.java

Prior to Quarkus 3.9 and the Infinispan 15 integration, queries were executed by calling the following code: .Query.java

QueryFactory queryFactory = Search.getQueryFactory(booksCache); 1
Query query = queryFactory.create("from book_sample.Book");
List<Book> list = query.execute().list();
1 Breaking change in 3.9

此代码以后将不可用,因为 RemoteCache 现在是一个 @ApplicationScoped 代理 bean。Search.getQueryFactory 将抛出 ClassCastException。使用 RemoteCache API 中的 query 方法如下移除不必要的间接方法。

This code won’t work anymore since RemoteCache is now an @ApplicationScoped proxy bean. Search.getQueryFactory will raise a ClassCastException. Remove the unecessary indirection by using the query method in the RemoteCache API as follows.

Query<Book> query = booksCache.<Book>query("from book_sample.Book");
List<Book> list = query.execute().list();

Counters

Infinispan 也具有计数的概念,而 Quarkus Infinispan 客户端在开箱即用时支持这些计数。

Infinispan also has a notion of counters and the Quarkus Infinispan client supports them out of the box.

Quarkus Infinispan 客户端扩展允许对 CounterManager 直接进行依赖注入。你只需为你的字段、构造函数或方法添加注释,即可轻松实现。然后,你可以像通常一样使用计数。

The Quarkus Infinispan client extension allows for Dependency Injection of the CounterManager directly. All you need to do is annotate your field, constructor or method, and you get it with no fuss. You can then use counters as you would normally.

@Inject
CounterManager counterManager;

你可以在 Infinispan 文档中阅读有关 clustered counters 的更多信息。

You can read more about clustered counters in the Infinispan documentation.

Near Caching

默认情况下,启用或禁用临近缓存,但你可以通过配置以下属性按缓存启用它:

Near caching is disabled by default, but you can enable it on a per cache basic by configuring the following properties:

quarkus.infinispan-client.cache.books.near-cache-mode=INVALIDATED 1
quarkus.infinispan-client.cache.books.near-cache-max-entries=200 2
quarkus.infinispan-client.cache.books.near-cache-use-bloom-filter=true 3
1 Enables near caching for the 'books' cache by setting the mode to INVALIDATED
2 Sets the maximum number of entries that the near cache of the 'books' cache can hold before eviction occurs
3 Enables bloom filter for the 'books' cache

Bounded near caching

你应该始终通过指定临近缓存可以包含的最大条目数来使用有界临近缓存。

You should always use bounded near caches by specifying the maximum number of entries they can contain.

Bloom filters

如果你需要通过减少失效消息的总数来优化写入操作的性能,请启用布隆过滤器。布隆过滤器驻留在 Infinispan 服务器上并跟踪客户端请求的条目。它们不能与无界临近缓存结合使用:启用布隆过滤器时必须定义最大条目数。

If you need to optimize the performance for write operations by reducing the total number of invalidation messages, enable bloom filter. Bloom filters reside on Infinispan Server and keep track of the entries that the client has requested. They cannot be used with unbounded near cache: maximum number of entries must be defined when enabling bloom filters.

Encryption

此时加密需要额外的步骤才能开始工作。

Encryption at this point requires additional steps to get working.

第一步是配置 application.properties 文件以指向你的可信存储库和/或密钥库。这在 here 中有更详细的说明。

The first step is to configure the application.properties file to point to your truststore and/or keystore. This is further detailed here.

默认情况下,Infinispan 客户端扩展启用 SSL/TLS。你可以在 Using SSL With Native Executables 中阅读有关此内容的更多信息。

The Infinispan Client extension enables SSL/TLS by default. You can read more about this at Using SSL With Native Executables.

SSL Host Name Validation

为了防止中间人攻击,在启用 SSL 时,Infinispan 中默认启用 SSL 主机名验证。在这种情况下,必须配置 SNI 主机名才能启动客户端。

To prevent MITM attacks, when SSL is enabled, SSL host name validation is enabled by default in Infinispan. In this case, configuring the SNI host name is mandatory to start the client.

quarkus.infinispan-client.sni-host-name=localhost 1
1 Sets the SNI host name

可以通过禁用验证来更改此行为。

This behaviour can be changed by disabling the validation.

quarkus.infinispan-client.ssl-host-name-validation=false 1
1 Disables ssl host name validation

Additional Features

Infinispan 客户端还有此处未提及的其他功能。这意味着该功能未在 Quarkus 环境中进行测试,它们可能有效也可能无效。如果您需要添加这些功能,请告诉我们!

The Infinispan Client has additional features that were not mentioned here. This means this feature was not tested in a Quarkus environment, and they may or may not work. Please let us know if you need these added!

Dev Services for Infinispan

当您在开发模式或测试中使用 infinispan-client 扩展时,Quarkus 会自动启动一个 Infinispan 服务器并配置您的应用程序。

When you use the infinispan-client extension in dev mode or in test, Quarkus automatically starts an Infinispan server and configure your application.

Enabling / Disabling Dev Services for Infinispan

Infinispan Dev Services guide 中了解更多信息。

Learn more in the Infinispan Dev Services guide.

Shared server

如果您有多个应用程序在 dev 模式下运行,Quarkus 将共享 Infinispan 代理。Infinispan 的 Dev 服务将为以 dev 模式运行的多个 Quarkus 应用程序实现一个 service discovery 机制,以便共享单个代理。

Quarkus will share the Infinispan broker if you have multiple applications running in dev mode. Dev Services for Infinispan implements a service discovery mechanism for your multiple Quarkus applications running in dev mode to share a single broker.

Infinispan 的 Dev 服务使用 quarkus-dev-service-infinispan 标签启动容器,该标签用于标识容器。

Dev Services for Infinispan starts the container with the quarkus-dev-service-infinispan label which is used to identify the container.

如果您需要多个(共享的)Infinispan 服务器,您可以配置 quarkus.infinispan-client.devservices.service-name 属性并指明服务器名称。它会查找具有相同值容器,如果找不到,则启动一个新容器。默认服务名是 infinispan

If you need multiple (shared) Infinispan server, you can configure the quarkus.infinispan-client.devservices.service-name attribute and indicate the server name. It looks for a container with the same value, or starts a new one if none can be found. The default service name is infinispan.

共享在 dev 模式下默认启用,但在测试模式下禁用。您可以使用 quarkus.infinispan-client.devservices.shared=false 禁用共享。

Sharing is enabled by default in dev mode, but disabled in test mode. You can disable the sharing with quarkus.infinispan-client.devservices.shared=false.

Setting the port

默认情况下,Infinispan 的 Dev 服务会选择一个随机端口并配置应用程序。您可以通过配置 quarkus.infinispan-client.devservices.port 属性设置端口。

By default, Dev Services for Infinispan picks a random port and configures the application. You can set the port by configuring the quarkus.infinispan-client.devservices.port property.

Configuration Reference

Unresolved directive in infinispan-client-reference.adoc - include::{generated-dir}/config/quarkus-infinispan-client.adoc[]