Infinispan Client Extension Reference Guide

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

  1. 按照 Get Started Tutorial (5 分钟)。

  2. 运行 remote cache simple code tutorials

Infinispan documentation 中了解详情。

Installation

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

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

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

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>  在 Gradle 构建中强制要求启用基于注释序列化的文件生成

Connection to Infinispan

Running the server

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

Development mode

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

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

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

$ ./bin/server.sh

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

  • 如果您运行 Infinispan Server image,请传递 USER="admin"PASS="password" 参数。

  • 如果您运行 bare metal distribution,请按照以下步骤使用命令行界面 (CLI):[source, bash]

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

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

Configuring the connection

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

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

quarkus.infinispan-client.username=admin 2
quarkus.infinispan-client.password=password 3
<1>  设置 Infinispan 服务器地址列表,以逗号分隔
<1>  Sets the authentication username
<1>  Sets the authentication password

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

quarkus.infinispan-client.uri=hotrod://admin:password@localhost:11222 1
<1>  设置 Infinispan URI 连接。将忽略以下属性:主机、用户名和密码。

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

Client intelligence

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

Infinispan documentation 中了解更多信息。

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

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

Configuring backup clusters in Cross-Site Replication

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

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>  设置 Infinispan Server 地址列表,并用逗号分隔。这是默认群集。
<1>  使用提供的地址列表配置备份站点“nyc-site”
<1>  使用提供的客户端智能配置备份站点“nyc-site”
<1>  使用提供的地址列表配置附加备份站点“lon-site”

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

@ApplicationScoped
public class InfinispanExample {
    @Inject
    RemoteCacheManager cacheManager;

    public void doSomething() {
       cacheManager.switchToCluster("nyc-site"); (1)
       cacheManager.switchToCluster("lon-site"); (2)
       cacheManager.switchToDefaultCluster(); (3)
    }
}
<1>  客户端连接到“nyc-site”。
<1>  客户端连接到“lon-site”。
<1>  客户端连接到默认站点。

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

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

Default and named connections

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

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

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

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 限定符:

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

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

Infinispan Health Check

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

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

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

Tracing with OpenTelemetry

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

Creating caches from the client

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

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>  'magazine' 的 xml 中的配置(yaml 和 json 也受支持)
<1>  位于 `resources` 文件夹下的、包含 'books' 缓存配置的文件名
<1>  提供的文件 URI。文件 URI 也可以是资源下的文件

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

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

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

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

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 客户端使用以下身份验证机制:

  • DIGEST-MD5

  • PLAIN(仅推荐与 TLS 加密结合使用)

  • EXTERNAL

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

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

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

Serialization (Key Value types support)

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

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

Infinispan 缓存可以在不同编码中存储键和值,但建议使用 Protocol Buffers (Protobuf)。 有关更多信息,请参阅我们的 Cache Encoding and Marshalling 指南。

Annotation based Serialization

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

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

Author.java
@Proto (1)
public record Author(String name, String surname) { (2)
}
<1>  自 Protostream 5.0 以来,一个注释就足够生成默认映射
<1>  Protostream 5.0 起支持记录
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>  指示该实体将被索引。执行分布式全文查询操作需要这样做。
<1>  指示 `title` 应作为文本被索引
<1>  指示 `description` 字段字段应作为关键字被索引。
<1>  Collections are supported
<1>  Protostream 为常用类型(例如 `BigDecimal`)提供了默认的 Protobuf 映射器,包含在 `org.infinispan.protostream.types` 包中。

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

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 注释覆盖默认编组。

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

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

Custom serialization

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

Protobuf schema

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

  1. Proto 文件,你可以将 .proto 文件放入项目的 META-INF 目录中。这些文件将在初始化时自动选取。.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; }

 .  在代码中,或者你可以通过定义 `org.infinispan.protostream.schema.Schema` 类型的一个生成的 bean,在用户代码中直接定义 proto 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 类。

[NOTE]
======
类型名称必须与 `&lt;protobuf package&gt;.&lt;protobuf message&gt;` 完全匹配!
======
[source, java]
.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);
   }
}
[source, java]
.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:
[source, java]
@Produces
MessageMarshaller authorMarshaller() {
   return new AuthorMarshaller();
}
@Produces
MessageMarshaller bookMarshaller() {
   return new BookMarshaller();
}
[NOTE]
======
上述产生的 Marshaller 方法一定返回 `MessageMarshaller` 类型,否则它将不会被找到。
======

[id="_dependency_injection"]
== Dependency Injection

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

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

[source, java]
.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`。

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

[source, java]
.SomeClass.java
@Inject
@InfinispanClientName("lon-site")
@Remote("books")
RemoteCache<String, Book> lonBooks;
@Inject
@InfinispanClientName("nyc-site")
@Remote("books")
RemoteCache<String, Book> nycBooks;
[NOTE]
======
其他类型也可以支持注入,详情请参见其他章节
======

[id="_mock_support"]
=== Mock Support

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

RemoteCacheManager 和 RemoteCache 可以被模拟。

[source, java]
.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();
   }
}
[style="arabic"]

 <1>  使用依赖注入连接到 books 缓存


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

[source, java]
.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)
    }
}
[style="arabic"]

 <1>  注入一个模拟,而不是 RemoteCache bean
 <1>  使用 Mockito 模拟 RemoteCache 的调用
 <1>  Assert the service call


[id="_registering_protobuf_schemas_with_infinispan_server"]
=== Registering Protobuf Schemas with Infinispan Server

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


[TIP]
======
你可以通过 `http://SERVER_HOST:SERVER_PORT` 中的 Infinispan 控制台中(例如 `http://localhost:11222`)登录到 `Schemas` 选项卡下的 schema,来查看已存在的 schema。
检查 xref:infinispan-dev-services.adoc[Infinispan Dev Services Guide] 以连接到 InfinispanDev 服务服务器。
======

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

要手动配置模式,请使用 link:https://infinispan.org/docs/infinispan-operator/main/operator.html[Infinispan Operator] 进行 Kubernetes 部署、Infinispan 控制台、 link:https://infinispan.org/docs/stable/titles/rest/rest.html#rest_v2_protobuf_schemas[REST API] 或 link:https://infinispan.org/docs/stable/titles/encoding/encoding.html#registering-sci-remote-caches_marshalling[Hot Rod Java Client]。

[id="infinispan-annotations-api"]
=== Caching using annotations

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

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

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

[id="_cacheresult"]
=== @CacheResult

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

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


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

[id="_cacheinvalidate"]
=== @CacheInvalidate

从缓存中删除一个条目。

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

[id="_cacheinvalidateall"]
=== @CacheInvalidateAll

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

[id="_querying"]
== Querying

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

[source, xml]
.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>

[source, json]
.JSON

{ "books": { "distributed-cache": { …​ "indexing": { "enabled": true, "storage": "filesystem", "startupMode": "PURGE", "indexed-entities": [ "book_sample.Book" ] } } } }

[source, yaml]
.YAML

distributedCache: # other configuration indexing: enabled: "true" storage: "filesystem" startupMode: "PURGE" indexedEntities: - "book_sample.Book"

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

[source, java]
.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;
}
...
[style="arabic"]

 <1>  `@Indexed` 注释使 POJO 可索引
 <1>  `@Basic` 注释用于没有特殊转换的已索引字段
 <1>  `@Keyword` 注释用于对文本字段应用归一化器


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

[source, java]
.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();

[style="arabic"]

 <1>  Inject the books cache
 <1>  对书籍作者姓名执行全文查询



[NOTE]
======
你可以在 Infinispan 文档中阅读更多有关 link:https://infinispan.org/docs/stable/titles/query/query.html[querying] 的信息。
======

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

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

[style="arabic"]

 <1>  Breaking change in 3.9

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

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

======

[id="_counters"]
== Counters

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

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

[source, java]

@Inject CounterManager counterManager;

你可以在 Infinispan 文档中阅读有关 link:https://infinispan.org/docs/stable/titles/developing/developing.html#clustered_counters[clustered counters] 的更多信息。

[id="_near_caching"]
== Near Caching

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

[source, 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

[style="arabic"]

 <1>  通过将模式设置为 `INVALIDATED`,为“books”缓存启用临近缓存
 <1>  设置“books”缓存临近缓存可在逐出发生之前容纳的最大条目数
 <1>  为“books”缓存启用布隆过滤器


[id="_bounded_near_caching"]
=== Bounded near caching

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

[id="_bloom_filters"]
=== Bloom filters

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

[id="_encryption"]
== Encryption

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

第一步是配置 `application.properties` 文件以指向你的可信存储库和/或密钥库。这在 link:https://infinispan.org/docs/stable/titles/hotrod_java/hotrod_java.html#hotrod_encryption[here] 中有更详细的说明。

默认情况下,Infinispan 客户端扩展启用 SSL/TLS。你可以在 xref:native-and-ssl.adoc[Using SSL With Native Executables] 中阅读有关此内容的更多信息。

[id="_ssl_host_name_validation"]
== SSL Host Name Validation

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

[source, properties]

quarkus.infinispan-client.sni-host-name=localhost 1

[style="arabic"]

 <1>  设置 SNI 主机名


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

[source, properties]

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

[style="arabic"]

 <1>  禁用 ssl 主机名验证


[id="_additional_features"]
== Additional Features

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

[id="dev-services"]
== Dev Services for Infinispan

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

[id="_enabling_disabling_dev_services_for_infinispan"]
=== Enabling / Disabling Dev Services for Infinispan


[NOTE]
======
在 xref:infinispan-dev-services.adoc[Infinispan Dev Services guide] 中了解更多信息。
======

[id="_shared_server"]
== Shared server

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


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

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

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

[id="_setting_the_port"]
== Setting the port

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

[id="_configuration_reference"]
== Configuration Reference

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