热备份 Java 客户端指南


Red Hat Data Grid 8.3

配置和使用 Hot Rod Java 客户端

Red Hat Customer Content Services

摘要

热备份 Java 客户端为您提供对 Data Grid 集群的高性能远程访问。

Red Hat Data Grid

数据网格是一个高性能、分布式内存数据存储。

无架构数据结构
灵活性将不同的对象存储为键值对。
基于网格的数据存储
设计为在集群间分发和复制数据。
弹性扩展
动态调整节点数,以在不中断服务的情况下满足需求。
数据互操作性
在来自不同端点的网格中存储、检索和查询数据。

data Grid 文档

有关 Data Grid 的文档可以通过红帽客户门户网站获得。

数据网格下载

访问 Data Grid Software Downloads on the Red Hat Customer Portal(Red Hat Customer Portal)。

注意

您必须有一个红帽帐户才能访问和下载数据网格软件。

使开源包含更多

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。我们从这四个术语开始: master、slave、blacklist 和 whitelist。由于此项工作十分艰巨,这些更改将在即将推出的几个发行版本中逐步实施。详情请查看 CTO Chris Wright 的信息

第 1 章 热备份 Java 客户端

通过 Hot Rod Java 客户端 API 远程访问 Data Grid。

1.1. 热环协议

热备份是一个二进制 TCP 协议,Data Grid 提供高性能客户端-服务器与以下功能交互:

  • 负载平衡。热备份客户端可以使用不同策略在 Data Grid 集群中发送请求。
  • 故障转移.热备份客户端可以监控 Data Grid 集群拓扑更改,并自动切换到可用节点。
  • 有效的数据位置。热备份客户端可以查找密钥所有者,并直接向那些节点发出请求,从而缩短延迟。

1.2. 客户端 Intelligence

热备份客户端使用智能机制高效地向 Data Grid Server 集群发送请求。默认情况下,Hot Rod 协议启用了 HASH_DISTRIBUTION_AWARE 智能机制。

BASIC 智能

客户端没有收到 Data Grid 集群的拓扑更改事件,如加入或离开节点,且只使用您添加到客户端配置中的 Data Grid 服务器网络位置列表。

注意

当 Data Grid Server 没有向 Hot Rod 客户端拓扑发送内部集群拓扑时,启用 BASIC 智能来使用 Hot Rod 客户端配置。

TOPOLOGY_AWARE 智能

客户端接收和存储 Data Grid 集群的拓扑更改事件,以动态跟踪网络上的 Data Grid 服务器。

要接收集群拓扑,客户端需要网络位置(即 IP 地址或主机名),其中至少一个 Hot Rod 服务器在启动时。连接客户端后,Data Grid Server 将拓扑传送到客户端。当 Data Grid Server 节点加入或离开集群时,Data Grid 会将更新的拓扑传送到客户端。

HASH_DISTRIBUTION_AWARE intelligence

除哈希信息外,客户端还接收和存储拓扑更改事件,让客户端能够识别哪些节点存储特定密钥。

例如,考虑一个 put(k,v) 操作。客户端计算键的哈希值,以便它可以找到数据所在内容的确切 Data Grid Server 节点。然后,客户端可以直接连接到那个节点来执行读写操作。

HASH_DISTRIBUTION_AWARE 智能的好处是 Data Grid Server 不需要根据关键散列(使用服务器外资源)查找值。另一个好处是,Data Grid 服务器可以更快地响应客户端请求,因为它们不需要额外的网络往返。

Configuration

ConfigurationBuilder

ConfigurationBuilder builder = new ConfigurationBuilder();
builder.clientIntelligence(ClientIntelligence.BASIC);
Copy to Clipboard Toggle word wrap

hotrod-client.properties

infinispan.client.hotrod.client_intelligence=BASIC
Copy to Clipboard Toggle word wrap

1.3. 请求平衡

热备份 Java 客户端将请求平衡到 Data Grid Server 集群,以便读写操作分散到节点间。

使用 BASICTOPOLOGY_AWARE 智能的客户端使用所有请求的请求平衡。使用 HASH_DISTRIBUTION_AWARE 智能将请求直接发送到存储所需密钥的节点的客户端。如果节点没有响应,客户端会回退到请求平衡。

默认平衡策略是循环的,因此 Hot Rod 客户端执行请求平衡,例如: s1s2s3 是 Data Grid 集群中的节点:

// Connect to the Data Grid cluster
RemoteCacheManager cacheManager = new RemoteCacheManager(builder.build());
// Obtain the remote cache
RemoteCache<String, String> cache = cacheManager.getCache("test");

//Hot Rod client sends a request to the "s1" node
cache.put("key1", "aValue");
//Hot Rod client sends a request to the "s2" node
cache.put("key2", "aValue");
//Hot Rod client sends a request to the "s3" node
String value = cache.get("key1");
//Hot Rod client sends the next request to the "s1" node again
cache.remove("key2");
Copy to Clipboard Toggle word wrap

自定义平衡策略

如果您在 Hot Rod 客户端配置中添加类,您可以使用自定义 FailoverRequestBalancingStrategy 实现。

ConfigurationBuilder

ConfigurationBuilder builder = new ConfigurationBuilder();
builder.addServer()
         .host("127.0.0.1")
         .port(11222)
         .balancingStrategy(new MyCustomBalancingStrategy());
Copy to Clipboard Toggle word wrap

hotrod-client.properties

infinispan.client.hotrod.request_balancing_strategy=my.package.MyCustomBalancingStrategy
Copy to Clipboard Toggle word wrap

1.4. 客户端故障切换

当 Data Grid 集群拓扑变化时,热 Rod 客户端可以自动故障转移。例如,是拓扑感知的 Hot Rod 客户端可以检测到一个或多个 Data Grid 服务器何时失败。

除了在集群 Data Grid 服务器间故障转移外,Hot Rod 客户端还可在 Data Grid 集群间故障转移。

例如,您有一个 Data Grid 集群在 New York(NYC)中运行,另一个在伦敦运行的集群(LON)。将请求发送到 NYC 的客户端检测到没有节点可用,因此它们会在 LON 中切换到集群。然后,客户端会维护到 LON 的连接,直到手动切换集群或故障转移为止。

带有故障转移的事务缓存

条件操作,如 putIfAbsent(), replace(), remove() 具有严格的方法返回保障。同样,一些操作可能需要返回以前的值。

尽管 Hot Rod 客户端可以故障转移,但应使用事务缓存来确保操作不部分完成,并在不同的节点上保留冲突的条目。

1.5. 热备份客户端与 Data Grid 服务器兼容

Data Grid Server 允许您将 Hot Rod 客户端与不同版本连接。例如,在迁移或升级到您的 Data Grid 集群期间,Hot Rod 客户端版本可能比 Data Grid Server 低。

提示

数据网格建议使用最新的 Hot Rod 客户端版本来受益于最新功能和安全性增强。

data Grid 8 及更新的版本

热环协议版本 3.x 自动协商通过 Data Grid Server 进行客户端的最大版本。

data Grid 7.3 及更早版本

使用比 Data Grid Server 版本高的 Hot Rod 协议版本的客户端必须设置 infinispan.client.hotrod.protocol_version 属性。

第 2 章 配置 Data Grid Maven 存储库

数据网格 Java 发行版可通过 Maven 获得。

您可以从客户门户网站下载 Data Grid Maven 存储库,或者从公共 Red Hat Enterprise Maven 存储库拉取 Data Grid 依赖项。

2.1. 下载 Data Grid Maven 存储库

如果您不想使用公共 Red Hat Enterprise Maven 软件仓库,将 Data Grid Maven 存储库下载并安装到本地文件系统、Apache HTTP 服务器或 Maven 存储库管理器。

流程

  1. 登录红帽客户门户。
  2. 导航到 Data Grid 的 Software Downloads
  3. 下载 Red Hat Data Grid 8.3 Maven 存储库。
  4. 将存档的 Maven 存储库提取到本地文件系统。
  5. 打开 README.md 文件并遵循相应的安装说明。

2.2. 添加 Red Hat Maven 软件仓库

在 Maven 构建环境中包括 Red Hat GA 存储库,以获取 Data Grid 工件和依赖项。

流程

  • 将 Red Hat GA 存储库添加到您的 Maven 设置文件,通常通常 ~/.m2/settings.xml 或直接在项目的 pom.xml 文件中。

    <repositories>
      <repository>
        <id>redhat-ga-repository</id>
        <name>Red Hat GA Repository</name>
        <url>https://maven.repository.redhat.com/ga/</url>
      </repository>
    </repositories>
    <pluginRepositories>
      <pluginRepository>
        <id>redhat-ga-repository</id>
        <name>Red Hat GA Repository</name>
        <url>https://maven.repository.redhat.com/ga/</url>
      </pluginRepository>
    </pluginRepositories>
    Copy to Clipboard Toggle word wrap

2.3. 配置您的数据网格 POM

Maven 使用名为 Project Object Model(POM)文件的配置文件来定义项目并管理构建。POM 文件采用 XML 格式,并描述所生成项目打包和输出的模块和组件依赖关系、构建顺序和目标。

流程

  1. 打开您的项目 pom.xml 进行编辑。
  2. 使用正确的 Data Grid 版本定义 version.infinispan 属性。
  3. dependencyManagement 部分中包含 infinispan-bom

    Bill of Materials(BOM)控制依赖关系版本,从而避免了版本冲突,这意味着您不需要为您添加为依赖项的每个 Data Grid 构件设置版本。

  4. 保存并关闭 pom.xml

以下示例显示了 Data Grid 版本和 BOM:

<properties>
  <version.infinispan>13.0.10.Final-redhat-00001</version.infinispan>
</properties>

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.infinispan</groupId>
      <artifactId>infinispan-bom</artifactId>
      <version>${version.infinispan}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
Copy to Clipboard Toggle word wrap

后续步骤

根据需要,将 Data Grid 工件作为依赖项添加到您的 pom.xml 中。

第 3 章 热备份 Java 客户端配置

Data Grid 提供了一个 Hot Rod Java 客户端配置 API,用于公开配置属性。

3.1. 添加 Hot Rod Java 客户端依赖项

添加 Hot Rod Java 客户端依赖项,将其包含在您的项目中。

先决条件

  • Java 8 或 Java 11

流程

  • pom.xml 中添加 infinispan-client-hotrod 工件作为依赖项,如下所示:
<dependency>
  <groupId>org.infinispan</groupId>
  <artifactId>infinispan-client-hotrod</artifactId>
</dependency>
Copy to Clipboard Toggle word wrap

3.2. 配置 Hot Rod 客户端连接

配置与 Data Grid Server 的 Hot Rod Java 客户端连接。

流程

  • 使用 ConfigurationBuilder 类生成不可变配置对象,您可以传递给 RemoteCacheManager 或在应用程序类路径中使用 hotrod-client.properties 文件。

ConfigurationBuilder

ConfigurationBuilder builder = new ConfigurationBuilder();
builder.addServer()
         .host("127.0.0.1")
         .port(ConfigurationProperties.DEFAULT_HOTROD_PORT)
       .addServer()
         .host("192.0.2.0")
         .port(ConfigurationProperties.DEFAULT_HOTROD_PORT)
       .security().authentication()
         .username("username")
         .password("changeme")
         .realm("default")
         .saslMechanism("SCRAM-SHA-512");
RemoteCacheManager cacheManager = new RemoteCacheManager(builder.build());
Copy to Clipboard Toggle word wrap

hotrod-client.properties

infinispan.client.hotrod.server_list = 127.0.0.1:11222,192.0.2.0:11222
infinispan.client.hotrod.auth_username = username
infinispan.client.hotrod.auth_password = changeme
infinispan.client.hotrod.auth_realm = default
infinispan.client.hotrod.sasl_mechanism = SCRAM-SHA-512
Copy to Clipboard Toggle word wrap

配置 Hot Rod URI

您还可以使用 URI 配置 Hot Rod 客户端连接,如下所示:

ConfigurationBuilder

ConfigurationBuilder builder = new ConfigurationBuilder();
builder.uri("hotrod://username:changeme@127.0.0.1:11222,192.0.2.0:11222?auth_realm=default&sasl_mechanism=SCRAM-SHA-512");
RemoteCacheManager cacheManager = new RemoteCacheManager(builder.build());
Copy to Clipboard Toggle word wrap

hotrod-client.properties

infinispan.client.hotrod.uri = hotrod://username:changeme@127.0.0.1:11222,192.0.2.0:11222?auth_realm=default&sasl_mechanism=SCRAM-SHA-512
Copy to Clipboard Toggle word wrap

在类路径外添加属性

如果 hotrod-client.properties 文件不在应用程序类路径上,那么您需要指定位置,如下例所示:

ConfigurationBuilder builder = new ConfigurationBuilder();
Properties p = new Properties();
try(Reader r = new FileReader("/path/to/hotrod-client.properties")) {
   p.load(r);
   builder.withProperties(p);
}
RemoteCacheManager cacheManager = new RemoteCacheManager(builder.build());
Copy to Clipboard Toggle word wrap

3.2.1. 在客户端配置中定义 Data Grid 集群

在 Hot Rod 客户端配置中提供 Data Grid 集群的位置。

流程

  • 至少提供一个 Data Grid 集群名称以及至少有一个节点及 ClusterConfigurationBuilder 类的主机名称和端口。

    如果要将集群定义为默认,以便客户端始终先尝试连接它,然后使用 addServers("<host_name>:<port>; <host_name>:<port>") 方法定义服务器列表。

多个集群连接

ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
clientBuilder.addCluster("siteA")
               .addClusterNode("hostA1", 11222)
               .addClusterNode("hostA2", 11222)
             .addCluster("siteB")
               .addClusterNodes("hostB1:11222; hostB2:11222");
RemoteCacheManager remoteCacheManager = new RemoteCacheManager(clientBuilder.build());
Copy to Clipboard Toggle word wrap

带有故障转移集群的默认服务器列表

ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
clientBuilder.addServers("hostA1:11222; hostA2:11222")
             .addCluster("siteB")
               .addClusterNodes("hostB1:11222; hostB2:11223");
RemoteCacheManager remoteCacheManager = new RemoteCacheManager(clientBuilder.build());
Copy to Clipboard Toggle word wrap

3.2.2. 手动切换数据网格集群

在 Data Grid 集群间手动切换 Hot Rod Java 客户端连接。

流程

  • RemoteCacheManager 类中调用以下任一方法:

    switchToCluster(clusterName) 切换到客户端配置中定义的特定集群。

    switchToDefaultCluster() 切换到客户端配置的默认集群,该配置定义为 Data Grid 服务器列表。

3.2.3. 配置连接池

热备份 Java 客户端保持与 Data Grid 服务器持久连接的池,以便重复使用 TCP 连接,而不是在每个请求中创建它们。

流程

  • 配置 Hot Rod 客户端连接池设置,如下例所示:

ConfigurationBuilder

ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
clientBuilder.addServer()
               .host("127.0.0.1")
               .port(11222)
             .connectionPool()
               .maxActive(10)
               exhaustedAction(ExhaustedAction.valueOf("WAIT"))
               .maxWait(1)
               .minIdle(20)
               .minEvictableIdleTime(300000)
               .maxPendingRequests(20);
RemoteCacheManager remoteCacheManager = new RemoteCacheManager(clientBuilder.build());
Copy to Clipboard Toggle word wrap

hotrod-client.properties

infinispan.client.hotrod.server_list = 127.0.0.1:11222
infinispan.client.hotrod.connection_pool.max_active = 10
infinispan.client.hotrod.connection_pool.exhausted_action = WAIT
infinispan.client.hotrod.connection_pool.max_wait = 1
infinispan.client.hotrod.connection_pool.min_idle = 20
infinispan.client.hotrod.connection_pool.min_evictable_idle_time = 300000
infinispan.client.hotrod.connection_pool.max_pending_requests = 20
Copy to Clipboard Toggle word wrap

3.3. 为 Hot Rod 客户端配置身份验证机制

数据网格服务器使用不同的机制来验证 Hot Rod 客户端连接。

流程

  • 使用 AuthenticationConfigurationBuilder 类中的 saslMechanism() 方法指定身份验证机制,或使用 infinispan.client.hotrod.sasl_mechanism 属性。

SCRAM

ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
clientBuilder.addServer()
               .host("127.0.0.1")
               .port(11222)
             .security()
               .authentication()
                 .saslMechanism("SCRAM-SHA-512")
                 .username("myuser")
                 .password("qwer1234!");
Copy to Clipboard Toggle word wrap

摘要

ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
clientBuilder.addServer()
               .host("127.0.0.1")
               .port(11222)
             .security()
               .authentication()
                 .saslMechanism("DIGEST-MD5")
                 .username("myuser")
                 .password("qwer1234!");
Copy to Clipboard Toggle word wrap

PLAIN

ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
clientBuilder.addServer()
               .host("127.0.0.1")
               .port(11222)
             .security()
               .authentication()
                 .saslMechanism("PLAIN")
                 .username("myuser")
                 .password("qwer1234!");
Copy to Clipboard Toggle word wrap

OAUTHBEARER

String token = "..."; // Obtain the token from your OAuth2 provider
ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
clientBuilder.addServer()
               .host("127.0.0.1")
               .port(11222)
             .security()
               .authentication()
                 .saslMechanism("OAUTHBEARER")
                 .token(token);
Copy to Clipboard Toggle word wrap

EXTERNAL

ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
clientBuilder
   .addServer()
      .host("127.0.0.1")
      .port(11222)
   .security()
      .ssl()
         // TrustStore stores trusted CA certificates for the server.
         .trustStoreFileName("/path/to/truststore")
         .trustStorePassword("truststorepassword".toCharArray())
         .trustStoreType("PCKS12")
         // KeyStore stores valid client certificates.
         .keyStoreFileName("/path/to/keystore")
         .keyStorePassword("keystorepassword".toCharArray())
         .keyStoreType("PCKS12")
      .authentication()
         .saslMechanism("EXTERNAL");
remoteCacheManager = new RemoteCacheManager(clientBuilder.build());
RemoteCache<String, String> cache = remoteCacheManager.getCache("secured");
Copy to Clipboard Toggle word wrap

GSSAPI

LoginContext lc = new LoginContext("GssExample", new BasicCallbackHandler("krb_user", "krb_password".toCharArray()));
lc.login();
Subject clientSubject = lc.getSubject();

ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
clientBuilder.addServer()
               .host("127.0.0.1")
               .port(11222)
             .security()
               .authentication()
                 .saslMechanism("GSSAPI")
                 .clientSubject(clientSubject)
                 .callbackHandler(new BasicCallbackHandler());
Copy to Clipboard Toggle word wrap

基本回调处理程序

BasicCallbackHandler (如 GSSAPI 示例所示)调用以下回调:

  • NameCallbackPasswordCallback 构建客户端主题。
  • 在 SASL 身份验证过程中调用 AuthorizeCallback
带有令牌回调处理程序的 OAUTHBEARER

使用 TokenCallbackHandler 在 OAuth2 令牌过期前刷新 OAuth2 令牌,如下例所示:

String token = "..."; // Obtain the token from your OAuth2 provider
TokenCallbackHandler tokenHandler = new TokenCallbackHandler(token);
ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
clientBuilder.addServer()
               .host("127.0.0.1")
               .port(11222)
             .security()
               .authentication()
               .saslMechanism("OAUTHBEARER")
               .callbackHandler(tokenHandler);
remoteCacheManager = new RemoteCacheManager(clientBuilder.build());
RemoteCache<String, String> cache = remoteCacheManager.getCache("secured");
// Refresh the token
tokenHandler.setToken("newToken");
Copy to Clipboard Toggle word wrap
Custom CallbackHandler

热备份客户端设置默认 CallbackHandler,将凭据传递给 SASL 机制。在某些情况下,您可能需要提供自定义 回调处理程序,如下例所示:

public class MyCallbackHandler implements CallbackHandler {
   final private String username;
   final private char[] password;
   final private String realm;

   public MyCallbackHandler(String username, String realm, char[] password) {
      this.username = username;
      this.password = password;
      this.realm = realm;
   }

   @Override
   public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
      for (Callback callback : callbacks) {
         if (callback instanceof NameCallback) {
            NameCallback nameCallback = (NameCallback) callback;
            nameCallback.setName(username);
         } else if (callback instanceof PasswordCallback) {
            PasswordCallback passwordCallback = (PasswordCallback) callback;
            passwordCallback.setPassword(password);
         } else if (callback instanceof AuthorizeCallback) {
            AuthorizeCallback authorizeCallback = (AuthorizeCallback) callback;
            authorizeCallback.setAuthorized(authorizeCallback.getAuthenticationID().equals(
                  authorizeCallback.getAuthorizationID()));
         } else if (callback instanceof RealmCallback) {
            RealmCallback realmCallback = (RealmCallback) callback;
            realmCallback.setText(realm);
         } else {
            throw new UnsupportedCallbackException(callback);
         }
      }
   }
}

ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
clientBuilder.addServer()
               .host("127.0.0.1")
               .port(11222)
             .security().authentication()
               .serverName("myhotrodserver")
               .saslMechanism("DIGEST-MD5")
               .callbackHandler(new MyCallbackHandler("myuser","default","qwer1234!".toCharArray()));
Copy to Clipboard Toggle word wrap
注意

自定义 回调处理程序 需要处理特定于您使用的验证机制的回调。但是,这超出了本文档的范围,可提供各种可能的回调类型示例。

3.3.1. 创建 GSSAPI 登录上下文

要使用 GSSAPI 机制,您必须创建一个 LoginContext,以便 Hot Rod 客户端可以获取 Ticket Granting Ticket(TGT)。

流程

  1. 在登录配置文件中定义登录模块。

    gss.conf

    GssExample {
        com.sun.security.auth.module.Krb5LoginModule required client=TRUE;
    };
    Copy to Clipboard Toggle word wrap

    对于 IBM JDK:

    gss-ibm.conf

    GssExample {
        com.ibm.security.auth.module.Krb5LoginModule required client=TRUE;
    };
    Copy to Clipboard Toggle word wrap

  2. 设置以下系统属性:

    java.security.auth.login.config=gss.conf
    
    java.security.krb5.conf=/etc/krb5.conf
    Copy to Clipboard Toggle word wrap
    注意

    krb5.conf 提供您的 KDC 的位置。使用 kinit 命令通过 Kerberos 进行身份验证并验证 krb5.conf

3.3.2. SASL 验证机制

Data Grid Server 使用 Hot Rod 端点支持以下 SASL 验证机制:

Expand
验证机制描述Security realm 类型相关详情

PLAIN

以纯文本格式使用凭证。您应该只对加密连接使用 PLAIN 身份验证。

属性 realm 和 LDAP 域

BASIC HTTP 机制类似。

DIGEST-*

使用哈希算法和非ce 值。热 Rod 连接器支持 DIGEST-MD5、 DIGEST-SHA -256、DIGEST-SHA-256DIGEST-SHA-SHA-384DIGEST-SHA-512 哈希算法,按优势顺序。

属性 realm 和 LDAP 域

Digest HTTP 机制类似。

SCRAM-*

除了哈希算法和非ce 值外,还使用 salt 值。热 Rod 连接器支持 SCRAM-SHASCRAM-SHA-256SCRAM-SHA-384SCRAM-SHA-512 哈希算法,按顺序支持 SCRAM-SHA-512 哈希算法。

属性 realm 和 LDAP 域

Digest HTTP 机制类似。

GSSAPI

使用 Kerberos 票据且需要 Kerberos 域控制器。您必须在域配置中添加对应的 kerberos 服务器身份。在大多数情况下,您还可指定 ldap-realm 来提供用户成员资格信息。

Kerberos realm

SPNEGO HTTP 机制类似。

GS2-KRB5

使用 Kerberos 票据且需要 Kerberos 域控制器。您必须在域配置中添加对应的 kerberos 服务器身份。在大多数情况下,您还可指定 ldap-realm 来提供用户成员资格信息。

Kerberos realm

SPNEGO HTTP 机制类似。

EXTERNAL

使用客户端证书。

信任存储域

CLIENT_CERT HTTP 机制类似。

OAUTHBEARER

使用 OAuth 令牌,需要 token-realm 配置。

令牌域

BEARER_TOKEN HTTP 机制类似。

3.4. 配置 Hot Rod 客户端加密

数据网格服务器可以实施 SSL/TLS 加密,并展示带有证书的 Hot Rod 客户端,以建立信任并协商安全连接。

要验证向 Data Grid Server 发布的证书,Hot Rod 客户端需要完整证书链或以 Root CA 开头的部分链。您可以将服务器证书提供给 Hot Rod 客户端,作为信任存储。

提示

或者提供信任存储,您可以使用共享系统证书。

先决条件

  • 创建一个信任存储,Hot Rod 客户端可用于验证 Data Grid 服务器身份。
  • 如果您配置 Data Grid Server 以验证或验证客户端证书,请根据需要创建一个密钥存储。

流程

  1. 使用 trustStoreFileName()trustStorePassword() 方法或对应的属性将信任存储添加到客户端配置中。
  2. 如果配置客户端证书身份验证,请执行以下操作:

    1. 使用 keyStoreFileName()keyStorePassword() 方法或对应的属性将密钥存储添加到客户端配置中。
    2. 配置客户端以使用 EXTERNAL 身份验证机制。

ConfigurationBuilder

ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
clientBuilder
   .addServer()
      .host("127.0.0.1")
      .port(11222)
   .security()
      .ssl()
         // Server SNI hostname.
         .sniHostName("myservername")
         // Keystore that contains the public keys for Data Grid Server.
         // Clients use the trust store to verify Data Grid Server identities.
         .trustStoreFileName("/path/to/server/truststore")
         .trustStorePassword("truststorepassword".toCharArray())
         .trustStoreType("PCKS12")
         // Keystore that contains client certificates.
         // Clients present these certificates to Data Grid Server.
         .keyStoreFileName("/path/to/client/keystore")
         .keyStorePassword("keystorepassword".toCharArray())
         .keyStoreType("PCKS12")
      .authentication()
         // Clients must use the EXTERNAL mechanism for certificate authentication.
         .saslMechanism("EXTERNAL");
Copy to Clipboard Toggle word wrap

hotrod-client.properties

infinispan.client.hotrod.server_list = 127.0.0.1:11222
infinispan.client.hotrod.use_ssl = true
infinispan.client.hotrod.sni_host_name = myservername
# Keystore that contains the public keys for Data Grid Server.
# Clients use the trust store to verify Data Grid Server identities.
infinispan.client.hotrod.trust_store_file_name = server_truststore.pkcs12
infinispan.client.hotrod.trust_store_password = changeme
infinispan.client.hotrod.trust_store_type = PCKS12
# Keystore that contains client certificates.
# Clients present these certificates to Data Grid Server.
infinispan.client.hotrod.key_store_file_name = client_keystore.pkcs12
infinispan.client.hotrod.key_store_password = changeme
infinispan.client.hotrod.key_store_type = PCKS12
# Clients must use the EXTERNAL mechanism for certificate authentication.
infinispan.client.hotrod.sasl_mechanism = EXTERNAL
Copy to Clipboard Toggle word wrap

后续步骤

将客户端信任存储添加到 $RHDG_HOME/server/conf 目录中,并在必要时配置 Data Grid Server 来使用它。

3.5. 启用 Hot Rod 客户端统计信息

热备份 Java 客户端可以提供统计数据,其中包括远程缓存和近缓存命中,以及连接池使用量的未命中。

流程

  1. 打开您的 Hot Rod Java 客户端配置以进行编辑。
  2. true 设置为 statistics 属性的值,或调用 statistics().enable() 方法。
  3. 使用 jmx 和 jmx_domain 属性或调用 jmx Enable()jmxDomain() 方法,为您的 Hot Rod 客户端导出 JMX MBeans。
  4. 保存并关闭客户端配置。
热备份 Java 客户端统计

ConfigurationBuilder

ConfigurationBuilder builder = new ConfigurationBuilder();
builder.statistics().enable()
         .jmxEnable()
         .jmxDomain("my.domain.org")
       .addServer()
         .host("127.0.0.1")
         .port(11222);
RemoteCacheManager remoteCacheManager = new RemoteCacheManager(builder.build());
Copy to Clipboard Toggle word wrap

hotrod-client.properties

infinispan.client.hotrod.statistics = true
infinispan.client.hotrod.jmx = true
infinispan.client.hotrod.jmx_domain = my.domain.org
Copy to Clipboard Toggle word wrap

3.6. 近缓存

接近缓存对 Hot Rod 客户端是本地的,并存储最近使用的数据,因此每个读取操作不需要遍历网络,这会显著提高性能。

接近缓存:

  • 使用 read 操作填充,对 get()getVersioned() 方法的调用。
    在以下示例中,put() 调用不会填充接近缓存,并且只对条目无效(如果已存在)无效:

    cache.put("k1", "v1");
    cache.get("k1");
    Copy to Clipboard Toggle word wrap
  • 在 Data Grid Server 上的远程缓存中更新或删除条目时,将客户端监听程序注册为无效条目。
    如果条目无效后请求,客户端必须再次从远程缓存中检索这些条目。
  • 当客户端切换到不同的服务器时,将被清除。

接近缓存的绑定

您应该始终通过指定可以包含的最大条目数来使用绑定近缓存。当接近缓存达到最大条目数时,驱除会自动发生以删除旧条目。这意味着您不需要手动将缓存大小放在客户端 JVM 的边界内。

重要

不要将最大空闲过期用于接近缓存,因为 near-cache 读取不会传播上次访问时间的条目。

Bloom 过滤器

Bloom 过滤器通过减少失效消息总数来优化写入操作的性能。

Bloom 过滤器:

  • 驻留在 Data Grid Server 上,并跟踪客户端所请求的条目。
  • 需要每个服务器具有最多活跃连接的连接的连接,并使用 WAIT 耗尽的操作。
  • 无法用于未绑定近的缓存。

3.6.1. 配置 Near Cache

使用近缓存配置 Hot Rod Java 客户端,将最近使用的数据存储在客户端 JVM 中。

流程

  1. 打开您的 Hot Rod Java 客户端配置。
  2. 配置每个缓存,以使用 nearCacheMode(NearCacheMode.INVALIDATED) 方法执行近缓存。

    注意

    数据网格提供全局近缓存配置属性。但是,这些属性已弃用,您不应该使用它们,而是根据缓存配置近缓存。

  3. 指定使用 nearCacheMaxEntries() 方法驱除前近缓存可以容纳的最大条目数。
  4. 使用 nearCacheUseBloomFilter() 方法启用用于接近缓存的 bloom 过滤器。
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
import org.infinispan.client.hotrod.configuration.NearCacheMode;
import org.infinispan.client.hotrod.configuration.ExhaustedAction;

ConfigurationBuilder builder = new ConfigurationBuilder();
builder.addServer()
    .host("127.0.0.1")
    .port(ConfigurationProperties.DEFAULT_HOTROD_PORT)
  .security().authentication()
    .username("username")
    .password("password")
    .realm("default")
    .saslMechanism("SCRAM-SHA-512")
  // Configure the connection pool for bloom filters.
  .connectionPool()
    .maxActive(1)
    .exhaustedAction(ExhaustedAction.WAIT);
// Configure near caching for specific caches
builder.remoteCache("books")
    .nearCacheMode(NearCacheMode.INVALIDATED)
    .nearCacheMaxEntries(100)
    .nearCacheUseBloomFilter(false);
builder.remoteCache("authors")
    .nearCacheMode(NearCacheMode.INVALIDATED)
    .nearCacheMaxEntries(200)
    .nearCacheUseBloomFilter(true);
Copy to Clipboard Toggle word wrap

3.7. 强制返回值

为了避免不必要的数据,在远程缓存中写入操作会返回 null 而不是前面的值。

例如,以下方法调用不会为键返回以前的值:

V remove(Object key);
V put(K key, V value);
Copy to Clipboard Toggle word wrap

但是,您可以更改默认行为,以便调用会返回之前键的值。

流程

  • 配置 Hot Rod 客户端,以以下方法之一调用之前键的值:

FORCE_RETURN_VALUE 标记

cache.withFlags(Flag.FORCE_RETURN_VALUE).put("aKey", "newValue")
Copy to Clipboard Toggle word wrap

per-cache

ConfigurationBuilder builder = new ConfigurationBuilder();
// Return previous values for keys for invocations for a specific cache.
builder.remoteCache("mycache")
       .forceReturnValues(true);
Copy to Clipboard Toggle word wrap

hotrod-client.properties

# Use the "*" wildcard in the cache name to return previous values
# for all caches that start with the "somecaches" string.

infinispan.client.hotrod.cache.somecaches*.force_return_values = true
Copy to Clipboard Toggle word wrap

3.8. 从 Hot Rod 客户端创建远程缓存

使用 Data Grid Hot Rod API 从 Java、C++、.NET/C#、JS 客户端等在 Data Grid Server 上创建远程缓存。

此步骤显示如何使用 Hot Rod Java 客户端在首次访问时创建远程缓存。您可以在 Data Grid Tutorials 中找到其他 Hot Rod 客户端的代码示例。

先决条件

  • 创建具有 admin 权限的 Data Grid 用户。
  • 至少一个 Data Grid Server 实例。
  • 具有数据网格缓存配置。

流程

  • 作为 ConfigurationBuilder 的一部分调用 remoteCache() 方法。
  • 在 classpath 上的 hotrod-client.properties 文件中设置 configurationconfiguration_uri 属性。

ConfigurationBuilder

File file = new File("path/to/infinispan.xml")
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.remoteCache("another-cache")
       .configuration("<distributed-cache name=\"another-cache\"/>");
builder.remoteCache("my.other.cache")
       .configurationURI(file.toURI());
Copy to Clipboard Toggle word wrap

hotrod-client.properties

infinispan.client.hotrod.cache.another-cache.configuration=<distributed-cache name=\"another-cache\"/>
infinispan.client.hotrod.cache.[my.other.cache].configuration_uri=file:///path/to/infinispan.xml
Copy to Clipboard Toggle word wrap

重要

如果您的远程缓存的名称包含 . 字符,那么在使用 hotrod-client.properties 文件时,您必须用方括号括起来。

第 4 章 热备份客户端 API

Data Grid Hot Rod 客户端 API 提供了接口,用于远程创建缓存、操作数据、监控集群缓存拓扑等。

4.1. RemoteCache API

集合方法 keySetentrySet 由远程缓存支持。也就是说,每个方法都会重新调用到 RemoteCache。这很有用,因为它允许检索各种密钥、条目或值,而且如果用户不需要同时存储在客户端内存中。

这些集合遵循了添加和 add Allmap 规格,但不支持所有其他方法。

要注意的是,Iterator.removeSet.removeCollection.remove 方法需要超过 1 轮向服务器进行操作。您可以检查 RemoteCache Javadoc,以查看这些文档及其他方法的详情。

迭代器使用情况

这些集合的迭代方法在内部使用 retrieveEntries,如下所述。如果您注意到 检索条目 使用批处理大小的参数。无法向迭代器提供这个方法。因此,在配置 RemoteCacheManager 时,可以通过系统属性 infinispan.client.hotrod.batch_sizeConfigurationBuilder 来配置批处理大小。

另外,返回的 retrieveEntries itator 为 Closeable,因为 keySetentrySetvalues 中的迭代程序返回 AutoCloseable 变体。因此,当您使用它们时,您应始终关闭这些"迭代"。

try (CloseableIterator<Map.Entry<K, V>> iterator = remoteCache.entrySet().iterator()) {

      }
Copy to Clipboard Toggle word wrap

如果我想要一个深度副本而不是后备集合,会怎么办?

早期版本的 RemoteCache 允许检索 keySet 的深度副本。使用新的后备映射仍有可能,您只需要自行复制内容。您还可以使用 entrySet 进行此操作,在之前我们不支持它。

Set<K> keysCopy = remoteCache.keySet().stream().collect(Collectors.toSet());
Copy to Clipboard Toggle word wrap

4.1.1. 不支持的方法

Data Grid RemoteCache API 不支持 Cache API 中所有可用的方法,并在调用不支持的方法时抛出 UnsupportedOperationException

这些方法中的大多数方法不适用于远程缓存(例如,侦听器管理操作),或者对应于本地缓存不支持的方法(例如,包含Value)。

RemoteCache API 不支持从 ConcurrentMap 继承的特定 atomic 操作,例如:

boolean remove(Object key, Object value);
boolean replace(Object key, Object value);
boolean replace(Object key, Object oldValue, Object value);
Copy to Clipboard Toggle word wrap

但是,remoteCache 为这些原子操作提供了替代版本的方法,它们通过网络而不是整个值对象发送版本标识符。

4.2. 远程迭代器 API

Data Grid 提供了一个远程迭代器 API,用于检索内存资源受限或计划进行服务器端过滤或转换的条目。

// Retrieve all entries in batches of 1000
int batchSize = 1000;
try (CloseableIterator<Entry<Object, Object>> iterator = remoteCache.retrieveEntries(null, batchSize)) {
     while(iterator.hasNext()) {
        // Do something
     }
}

// Filter by segment
Set<Integer> segments = ...
try (CloseableIterator<Entry<Object, Object>> iterator = remoteCache.retrieveEntries(null, segments, batchSize)) {
     while(iterator.hasNext()) {
        // Do something
     }
}

// Filter by custom filter
try (CloseableIterator<Entry<Object, Object>> iterator = remoteCache.retrieveEntries("myFilterConverterFactory", segments, batchSize)) {
     while(iterator.hasNext()) {
        // Do something
     }
}
Copy to Clipboard Toggle word wrap

4.2.1. 将自定义过滤器部署到数据网格服务器

为 Data Grid 服务器实例部署自定义过滤器。

流程

  1. 创建可扩展 KeyValueFilterConverterFactory 的工厂。

    import java.io.Serializable;
    
    import org.infinispan.filter.AbstractKeyValueFilterConverter;
    import org.infinispan.filter.KeyValueFilterConverter;
    import org.infinispan.filter.KeyValueFilterConverterFactory;
    import org.infinispan.filter.NamedFactory;
    import org.infinispan.metadata.Metadata;
    
    //@NamedFactory annotation defines the factory name
    @NamedFactory(name = "myFilterConverterFactory")
    public class MyKeyValueFilterConverterFactory implements KeyValueFilterConverterFactory {
    
       @Override
       public KeyValueFilterConverter<String, SampleEntity1, SampleEntity2> getFilterConverter() {
          return new MyKeyValueFilterConverter();
       }
       // Filter implementation. Should be serializable or externalizable for DIST caches
       static class MyKeyValueFilterConverter extends AbstractKeyValueFilterConverter<String, SampleEntity1, SampleEntity2> implements Serializable {
          @Override
          public SampleEntity2 filterAndConvert(String key, SampleEntity1 entity, Metadata metadata) {
             // returning null will case the entry to be filtered out
             // return SampleEntity2 will convert from the cache type SampleEntity1
          }
    
          @Override
          public MediaType format() {
             // returns the MediaType that data should be presented to this converter.
             // When omitted, the server will use "application/x-java-object".
             // Returning null will cause the filter/converter to be done in the storage format.
          }
       }
    }
    Copy to Clipboard Toggle word wrap
  2. 创建一个 JAR,其中包含 META-INF/services/org.infinispan.filter.KeyValueFilterConverterFactory 文件。此文件应包含过滤器工厂类实施的完全限定类名称。

    如果过滤器使用自定义键/值类,您必须将它们包含在 JAR 文件中,以便过滤器可以正确取消摘要键和/或值实例。

  3. 将 JAR 文件添加到 Data Grid 服务器安装目录的 server/lib 目录中。

4.3. MetadataValue API

为版本控制操作使用 MetadataValue 接口。

以下示例显示了只有在条目值没有改变时才会发生删除操作:

RemoteCacheManager remoteCacheManager = new RemoteCacheManager();
      RemoteCache<String, String> remoteCache = remoteCacheManager.getCache();

      remoteCache.put("car", "ferrari");
      VersionedValue valueBinary = remoteCache.getWithMetadata("car");

      assert remoteCache.remove("car", valueBinary.getVersion());
      assert !remoteCache.containsKey("car");
Copy to Clipboard Toggle word wrap

4.4. streaming API

Data Grid 提供了一个流化 API,它实现返回 InputStreamOutputStream 实例的方法,以便您可以在 Hot Rod 客户端和 Data Grid 服务器之间流化大型对象。

考虑以下大型对象示例:

StreamingRemoteCache<String> streamingCache = remoteCache.streaming();
OutputStream os = streamingCache.put("a_large_object");
os.write(...);
os.close();
Copy to Clipboard Toggle word wrap

您可以按以下方式通过流读取对象:

StreamingRemoteCache<String> streamingCache = remoteCache.streaming();
InputStream is = streamingCache.get("a_large_object");
for(int b = is.read(); b >= 0; b = is.read()) {
   // iterate
}
is.close();
Copy to Clipboard Toggle word wrap
注意

Streaming API 没有 marshall 值,这意味着您无法同时使用 Streaming 和 Non-Streaming API 访问同一条目。但是,您可以实施自定义 marshaller 来应对这种情况。

RemoteStreamingCache.get(K key) 方法返回的 InputStream 实现了 VersionedMetadata 接口,以便您可以检索版本和过期信息,如下所示:

StreamingRemoteCache<String> streamingCache = remoteCache.streaming();
InputStream is = streamingCache.get("a_large_object");
long version = ((VersionedMetadata) is).getVersion();
for(int b = is.read(); b >= 0; b = is.read()) {
   // iterate
}
is.close();
Copy to Clipboard Toggle word wrap
注意

条件写入方法(putIfAbsent () )在值完全发送到服务器后执行实际条件检查。换句话说,在 OutputStream 上调用 close() 方法。

4.5. 计数器 API

CounterManager 接口是定义、检索和删除计数器的入口点。

热备份客户端可以检索 CounterManager 接口,如下例所示:

// create or obtain your RemoteCacheManager
RemoteCacheManager manager = ...;

// retrieve the CounterManager
CounterManager counterManager = RemoteCounterManagerFactory.asCounterManager(manager);
Copy to Clipboard Toggle word wrap

参考

4.6. 创建事件 Listener

Java Hot Rod 客户端可以注册监听程序以接收缓存进入级别事件。支持创建、修改和删除的缓存条目。

创建客户端监听程序与嵌入式监听器非常相似,但使用了不同的注释和事件类。以下是打印接收的每个事件的客户端监听程序示例:

import org.infinispan.client.hotrod.annotation.*;
import org.infinispan.client.hotrod.event.*;

@ClientListener(converterFactoryName = "static-converter")
public class EventPrintListener {

   @ClientCacheEntryCreated
   public void handleCreatedEvent(ClientCacheEntryCreatedEvent e) {
      System.out.println(e);
   }

   @ClientCacheEntryModified
   public void handleModifiedEvent(ClientCacheEntryModifiedEvent e) {
      System.out.println(e);
   }

   @ClientCacheEntryRemoved
   public void handleRemovedEvent(ClientCacheEntryRemovedEvent e) {
      System.out.println(e);
   }

}
Copy to Clipboard Toggle word wrap

ClientCacheEntryCreatedEventClientCacheEntryModifiedEvent 实例提供有关受影响密钥和条目版本的信息。此版本可用于调用服务器上的条件操作,如 replaceWithVersionremoveWithVersion

只有在删除操作成功时才会发送 ClientCacheEntryRemovedEvent 事件。换句话说,如果调用 remove 操作但未找到任何条目,则不会生成任何事件。对删除事件的用户(即使没有条目被删除),用户也可以开发事件自定义逻辑来生成这些事件。如需更多信息,请参阅自定义 客户端事件部分

所有 ClientCacheEntryCreatedEventClientCacheEntryModifiedEventClientCacheEntryRemovedEvent 事件实例也都 会提供一个布尔值为CommandRetried() 方法,如果导致这个使用了拓扑更改,则返回 true。这可能是因为此事件已被重复,或另一个事件已被替换(例如: ClientCacheEntryModifiedEvent 替换 ClientCacheEntryCreatedEvent)。

创建客户端侦听器实施后,需要将其注册到服务器。要做到这一点,请执行:

RemoteCache<?, ?> cache = ...
cache.addClientListener(new EventPrintListener());
Copy to Clipboard Toggle word wrap

4.6.1. 删除事件 Listener

当不再需要客户端事件监听程序时,可以删除它:

EventPrintListener listener = ...
cache.removeClientListener(listener);
Copy to Clipboard Toggle word wrap

4.6.2. 过滤事件

为了避免使用事件锁定客户端,用户可以提供过滤功能来限制服务器为特定客户端监听器触发的事件数量。要启用过滤,需要创建一个缓存事件过滤器工厂来生成过滤实例:

import org.infinispan.notifications.cachelistener.filter.CacheEventFilterFactory;
import org.infinispan.filter.NamedFactory;

@NamedFactory(name = "static-filter")
public static class StaticCacheEventFilterFactory implements CacheEventFilterFactory {

   @Override
   public StaticCacheEventFilter getFilter(Object[] params) {
      return new StaticCacheEventFilter();
   }
}


// Serializable, Externalizable or marshallable with Infinispan Externalizers
// needed when running in a cluster
class StaticCacheEventFilter implements CacheEventFilter<Integer, String>, Serializable {
   @Override
   public boolean accept(Integer key, String oldValue, Metadata oldMetadata,
         String newValue, Metadata newMetadata, EventType eventType) {
      if (key.equals(1)) // static key
         return true;

      return false;
   }
}
Copy to Clipboard Toggle word wrap

以上定义的缓存事件过滤器工厂实例会创建过滤实例,这些实例静态过滤出所有条目,但其键为 1 除外。

要能够使用此缓存事件过滤器注册侦听器,需要为工厂提供唯一名称,Hot Rod 服务器需要插入名称以及缓存事件过滤器实例。

  1. 创建包含过滤器实施的 JAR 文件。

    如果缓存使用自定义键/值类,这些键/值类必须包含在 JAR 中,以便可以使用正确的未总结键和/或值实例来执行回调。如果客户端监听程序启用了 useRawData,则不需要它,因为回调键/值实例将以二进制格式提供。

  2. 在 JAR 文件中创建 META-INF/services/org.infinispan.notifications.cachelistener.filter.CacheEventFilterFactory 文件,并编写过滤器类实施的完全限定类名称。
  3. 将 JAR 文件添加到 Data Grid 服务器安装目录的 server/lib 目录中。
  4. 通过将 factory 名称添加到 @ClientListener 注释,将客户端监听程序与此缓存事件过滤器连接:

    @ClientListener(filterFactoryName = "static-filter")
    public class EventPrintListener { ... }
    Copy to Clipboard Toggle word wrap
  5. 在服务器中注册监听程序:

    RemoteCache<?, ?> cache = ...
    cache.addClientListener(new EventPrintListener());
    Copy to Clipboard Toggle word wrap

您还可以根据执行监听器注册时提供的参数,注册过滤的动态过滤器实例。过滤器使用过滤器工厂接收的参数启用这个选项,例如:

import org.infinispan.notifications.cachelistener.filter.CacheEventFilterFactory;
import org.infinispan.notifications.cachelistener.filter.CacheEventFilter;

class DynamicCacheEventFilterFactory implements CacheEventFilterFactory {
   @Override
   public CacheEventFilter<Integer, String> getFilter(Object[] params) {
      return new DynamicCacheEventFilter(params);
   }
}

// Serializable, Externalizable or marshallable with Infinispan Externalizers
// needed when running in a cluster
class DynamicCacheEventFilter implements CacheEventFilter<Integer, String>, Serializable {
   final Object[] params;

   DynamicCacheEventFilter(Object[] params) {
      this.params = params;
   }

   @Override
   public boolean accept(Integer key, String oldValue, Metadata oldMetadata,
         String newValue, Metadata newMetadata, EventType eventType) {
      if (key.equals(params[0])) // dynamic key
         return true;

      return false;
   }
}
Copy to Clipboard Toggle word wrap

注册监听程序时,会提供执行过滤所需的动态参数:

RemoteCache<?, ?> cache = ...
cache.addClientListener(new EventPrintListener(), new Object[]{1}, null);
Copy to Clipboard Toggle word wrap
警告

当实例部署到集群中时,过滤实例必须可以被放入集群中,以便可以在事件生成的情况下进行过滤,即使即使是在注册侦听器的不同节点中也会生成事件。要使它们可以被扩展,请扩展 SerializableExternalizable,或为其提供自定义 外部化 器。

4.6.3. 跳过通知

在调用远程 API 方法以不从服务器获取事件通知的情况下,包含 SKIP_LISTENER_ Xmx 标志。例如,要在创建或修改值时防止监听程序通知,请按如下所示设置标记:

remoteCache.withFlags(Flag.SKIP_LISTENER_NOTIFICATION).put(1, "one");
Copy to Clipboard Toggle word wrap

4.6.4. 自定义事件

默认生成的事件仅包含与事件相关的足够信息,但可以避免生成太多的信息,以减少发送它们的成本。(可选)事件中包含的信息可以自定义,使其包含更多信息,如值,或者包含更多的信息。此自定义通过 CacheEventConverter onnectionFactoryy 生成的 CacheEventConvertertal 实例完成:

import org.infinispan.notifications.cachelistener.filter.CacheEventConverterFactory;
import org.infinispan.notifications.cachelistener.filter.CacheEventConverter;
import org.infinispan.filter.NamedFactory;

@NamedFactory(name = "static-converter")
class StaticConverterFactory implements CacheEventConverterFactory {
   final CacheEventConverter<Integer, String, CustomEvent> staticConverter = new StaticCacheEventConverter();
   public CacheEventConverter<Integer, String, CustomEvent> getConverter(final Object[] params) {
      return staticConverter;
   }
}

// Serializable, Externalizable or marshallable with Infinispan Externalizers
// needed when running in a cluster
class StaticCacheEventConverter implements CacheEventConverter<Integer, String, CustomEvent>, Serializable {
   public CustomEvent convert(Integer key, String oldValue, Metadata oldMetadata, String newValue, Metadata newMetadata, EventType eventType) {
      return new CustomEvent(key, newValue);
   }
}

// Needs to be Serializable, Externalizable or marshallable with Infinispan Externalizers
// regardless of cluster or local caches
static class CustomEvent implements Serializable {
   final Integer key;
   final String value;
   CustomEvent(Integer key, String value) {
      this.key = key;
      this.value = value;
   }
}
Copy to Clipboard Toggle word wrap

在上例中,converter 生成一个新的自定义事件,其中包含值以及事件中的键。这会导致大量事件有效负载与默认事件进行比较,但如果与过滤结合使用,它可以降低其网络带宽成本。

警告

转换器的目标类型必须是 SerializableExternalizable。在这种情况下,提供 Externalizer 时,默认将无法使用,因为默认的 Hot Rod 客户端 marshaller 不支持它们。

处理自定义事件需要稍有不同的客户端监听程序实施。要更精确,需要处理 ClientCacheEntryCustomEvent 实例:

import org.infinispan.client.hotrod.annotation.*;
import org.infinispan.client.hotrod.event.*;

@ClientListener
public class CustomEventPrintListener {

   @ClientCacheEntryCreated
   @ClientCacheEntryModified
   @ClientCacheEntryRemoved
   public void handleCustomEvent(ClientCacheEntryCustomEvent<CustomEvent> e) {
      System.out.println(e);
   }

}
Copy to Clipboard Toggle word wrap

ClientCacheEntryCustomEvent 通过 getEventData 方法接收自定义事件,而 getType 方法则提供有关生成了缓存条目创建、修改或删除事件的信息。

与过滤类似,为了能使用此转换器工厂注册侦听器,需要赋予一个唯一名称,并且 Hot Rod 服务器需要用名称插入名称和缓存事件转换程序实例。

  1. 创建一个 JAR 文件,其中包含转换器实施。

    如果缓存使用自定义键/值类,这些键/值类必须包含在 JAR 中,以便可以使用正确的未总结键和/或值实例来执行回调。如果客户端监听程序启用了 useRawData,则不需要它,因为回调键/值实例将以二进制格式提供。

  2. 在 JAR 文件中创建 META-INF/services/org.infinispan.notifications.cachelistener.filter.CacheEventConverterFactory 文件,并编写转换器类实施的完全限定类名称。
  3. 将 JAR 文件添加到 Data Grid 服务器安装目录的 server/lib 目录中。
  4. 通过将 factory 名称添加到 @ClientListener 注释,将客户端监听程序与此转换器工厂连接:

    @ClientListener(converterFactoryName = "static-converter")
    public class CustomEventPrintListener { ... }
    Copy to Clipboard Toggle word wrap
  5. 在服务器中注册监听程序:

    RemoteCache<?, ?> cache = ...
    cache.addClientListener(new CustomEventPrintListener());
    Copy to Clipboard Toggle word wrap

也可以利用动态转换基于监听器提供的参数转换器实例。converters 使用转换器工厂接收的参数启用这个选项。例如:

import org.infinispan.notifications.cachelistener.filter.CacheEventConverterFactory;
import org.infinispan.notifications.cachelistener.filter.CacheEventConverter;

@NamedFactory(name = "dynamic-converter")
class DynamicCacheEventConverterFactory implements CacheEventConverterFactory {
   public CacheEventConverter<Integer, String, CustomEvent> getConverter(final Object[] params) {
      return new DynamicCacheEventConverter(params);
   }
}

// Serializable, Externalizable or marshallable with Infinispan Externalizers needed when running in a cluster
class DynamicCacheEventConverter implements CacheEventConverter<Integer, String, CustomEvent>, Serializable {
   final Object[] params;

   DynamicCacheEventConverter(Object[] params) {
      this.params = params;
   }

   public CustomEvent convert(Integer key, String oldValue, Metadata oldMetadata,
         String newValue, Metadata newMetadata, EventType eventType) {
      // If the key matches a key given via parameter, only send the key information
      if (params[0].equals(key))
         return new CustomEvent(key, null);

      return new CustomEvent(key, newValue);
   }
}
Copy to Clipboard Toggle word wrap

注册监听器时,会提供进行转换所需的动态参数:

RemoteCache<?, ?> cache = ...
cache.addClientListener(new EventPrintListener(), null, new Object[]{1});
Copy to Clipboard Toggle word wrap
警告

当转换器实例在集群中部署时,必须合并,以便可以正确生成事件,即使事件在注册了监听程序的不同节点中也会发生转换。要使它们可以被扩展,请扩展 SerializableExternalizable,或为其提供自定义 外部化 器。

4.6.5. 过滤和自定义事件

如果要进行事件过滤和自定义,则更容易实现 org.infinispan.notifications.cachelistener.filter.CacheEventFilterConverter,它可让您在单步中进行过滤和自定义。为方便起见,建议直接扩展 org.infinispan.notifications.cachelistener.filter.AbstractCacheEventFilterConverter 而非实施 org.infinispan.notifications.cachelistener.CacheEventFilterConverter。例如:

import org.infinispan.notifications.cachelistener.filter.CacheEventConverterFactory;
import org.infinispan.notifications.cachelistener.filter.CacheEventConverter;

@NamedFactory(name = "dynamic-filter-converter")
class DynamicCacheEventFilterConverterFactory implements CacheEventFilterConverterFactory {
   public CacheEventFilterConverter<Integer, String, CustomEvent> getFilterConverter(final Object[] params) {
      return new DynamicCacheEventFilterConverter(params);
   }
}

// Serializable, Externalizable or marshallable with Infinispan Externalizers needed when running in a cluster
//
class DynamicCacheEventFilterConverter extends AbstractCacheEventFilterConverter<Integer, String, CustomEvent>, Serializable {
   final Object[] params;

   DynamicCacheEventFilterConverter(Object[] params) {
      this.params = params;
   }

   public CustomEvent filterAndConvert(Integer key, String oldValue, Metadata oldMetadata,
         String newValue, Metadata newMetadata, EventType eventType) {
      // If the key matches a key given via parameter, only send the key information
      if (params[0].equals(key))
         return new CustomEvent(key, null);

      return new CustomEvent(key, newValue);
   }
}
Copy to Clipboard Toggle word wrap

与过滤器和转换器类似,为了能够使用此组合的过滤器/协调程序注册侦听器,该工厂必须通过 @NamedFactory 注释赋予一个唯一名称,并且 Hot Rod 服务器需要使用名称和缓存事件转换程序实例进行插入。

  1. 创建一个 JAR 文件,其中包含转换器实施。

    如果缓存使用自定义键/值类,这些键/值类必须包含在 JAR 中,以便可以使用正确的未总结键和/或值实例来执行回调。如果客户端监听程序启用了 useRawData,则不需要它,因为回调键/值实例将以二进制格式提供。

  2. 在 JAR 文件中创建 META-INF/services/org.infinispan.notifications.cachelistener.filter.CacheEventFilterConverter onnectionFactoryy 文件,并编写转换器类实施的完全限定类名称。
  3. 将 JAR 文件添加到 Data Grid 服务器安装目录的 server/lib 目录中。

从客户端的角度来看,为了能够使用组合过滤器和转换器类,客户端监听程序必须定义相同的过滤器工厂和转换器名称,例如:

@ClientListener(filterFactoryName = "dynamic-filter-converter", converterFactoryName = "dynamic-filter-converter")
public class CustomEventPrintListener { ... }
Copy to Clipboard Toggle word wrap

当监听器通过过滤器或转换器参数注册时,会提供上例中的动态参数。如果 filter 参数不是空的,则使用那些参数,否则使用转换器参数:

RemoteCache<?, ?> cache = ...
cache.addClientListener(new CustomEventPrintListener(), new Object[]{1}, null);
Copy to Clipboard Toggle word wrap

4.6.6. event Marshalling

热备份服务器可以以不同格式存储数据,但是在这方面,Java Hot Rod 客户端用户仍然可以开发在键入的对象上工作的 CacheEventConverterCacheEventFilter 实例。默认情况下,过滤器和转换器将使用数据作为 POJO(application/x-java-object),但可以通过覆盖 filter/converter 中的方法 format() 覆盖所需的格式。如果格式返回 null,则 filter/converter 将接收数据,因为它存储在其中。

hot Rod Java 客户端可以配置为使用不同的 org.infinispan.commons.marshall.Marshaller 实例。如果这样做并部署 CacheEventConverterCacheEventFilter 实例,若要通过 Java 对象提供过滤器/聚合内容,服务器需要能够转换对象和 marshaller 生成的二进制格式。

要部署 Marshaller 实例服务器端,请按照类似方法来部署 CacheEventConverterCacheEventFilter 实例:

  1. 创建一个 JAR 文件,其中包含转换器实施。
  2. 在 JAR 文件中创建 META-INF/services/org.infinispan.commons.marshall.Marshaller 文件,并编写 marshaller 类实施的完全限定域名。
  3. 将 JAR 文件添加到 Data Grid 服务器安装目录的 server/lib 目录中。

请注意,Marshaller 可以部署到单独的 jar 中,也可以部署到与 CacheEventConverter 和/or CacheEventFilter 实例相同的 jar 中。

4.6.6.1. 部署 Protostream Marshallers

如果缓存存储了 Protobuf 内容,因为当在 Hot Rod 客户端中使用 ProtoStream marshaller 时,不需要部署自定义摘要,因为该格式已经被服务器支持:可以使用 Protobuf 格式将其转换为最常用的格式。

将过滤器/聚合与这些缓存搭配使用时,最好将过滤器/聚合与 Java 对象一起使用,而是对二进制过程的数据一起使用,需要配置额外的 ProtoStream marshallers,以便服务器在过滤/聚合前无法排解数据。要做到这一点,您必须配置所需的 SerializationContextInitializer,作为 Data Grid 服务器配置的一部分。

如需更多信息 ,请参阅缓存编码和 Marshalling

4.6.7. 侦听器状态处理

客户端监听程序注解有一个可选的 includeCurrentState 属性,用于指定在添加监听程序时或当监听器故障转移时,状态将发送到客户端。

默认情况下,includeCurrentState 为 false,但是如果设为 true,并且已在缓存中添加客户端监听程序,服务器会迭代缓存内容,并将每个条目的事件作为 ClientCacheEntryCreated (或者配置了自定义事件)。这使得客户端能够基于现有内容构建一些本地数据结构。迭代内容后,会正常收到事件,因为收到缓存更新。如果集群缓存,则会迭代整个集群范围的内容。

4.6.8. 监听器故障处理

当 Hot Rod 客户端注册客户端侦听器时,它能在集群中的单个节点中执行此操作。如果该节点失败,Java Hot Rod 客户端检测到,通过注册到另一节点的所有监听程序来透明地失败。

在这种故障期间,客户端故障转移可能会丢失一些事件。为了避免缺少这些事件,客户端监听程序注解包含名为 includeCurrentState 的可选参数,如果出现故障转移时,缓存内容可以迭代和 ClientCacheEntryCreated 事件(或者配置了自定义事件)。默认情况下,includeCurrentState 设置为 false。

使用回调处理故障切换事件:

@ClientCacheFailover
public void handleFailover(ClientCacheFailoverEvent e) {
  ...
}
Copy to Clipboard Toggle word wrap

当客户端缓存了一些数据的情况中,当客户端缓存了一些数据的情况中,在出现问题出现问题时,要考虑到一些事件可能会丢失,它可能会决定在收到失败的事件时清除所有本地缓存的数据,因为知识在事件过后,它将接收整个缓存的内容的事件。

4.7. 热备份 Java 客户端事务

您可以在 JTA事务中配置和使用 Hot Rod 客户端。

要参与交易,Hot Rod 客户端需要与它交互的 TransactionManager 以及是否通过 SynchronizationXAResource 接口参与事务。

重要

当客户端在准备阶段获取条目的写入锁定时,交易很好。为了避免数据不一致,请务必在事务中 读取有问题的冲突

4.7.1. 配置服务器

服务器中的缓存还必须是客户端参与 JTA事务的事务性。

需要以下服务器配置,否则只进行事务回滚:

  • 隔离级别必须是 REPEATABLE_READ
  • 建议使用 PESSIMISTIC 锁定模式,但可以使用 OPTIMISTIC
  • 事务模式应该是 NON_XANON_DURABLE_XA。热环交易不应使用 FULL_XA,因为它会降低性能。

例如:

<replicated-cache name="hotrodReplTx">
  <locking isolation="REPEATABLE_READ"/>
  <transaction mode="NON_XA" locking="PESSIMISTIC"/>
</replicated-cache>
Copy to Clipboard Toggle word wrap

热 Rod 事务拥有自己的恢复机制。

4.7.2. 配置 Hot Rod 客户端

事务性 远程缓存 以每个缓存为基础配置。例外是事务 的超时 (全局),因为单个事务可以与多个 RemoteCache进行交互。

以下示例演示了如何为缓存 my-cache 配置事务远程Cache

org.infinispan.client.hotrod.configuration.ConfigurationBuilder cb = new org.infinispan.client.hotrod.configuration.ConfigurationBuilder();
//other client configuration parameters
cb.transactionTimeout(1, TimeUnit.MINUTES);
cb.remoteCache("my-cache")
   .transactionManagerLookup(GenericTransactionManagerLookup.getInstance())
   .transactionMode(TransactionMode.NON_XA);
Copy to Clipboard Toggle word wrap

如需配置参数的文档,请参阅 ConfigurationBuilderRemoteCacheConfigurationBuilder Javadoc。

您还可以使用属性文件配置 Java Hot Rod 客户端,如下例所示:

infinispan.client.hotrod.cache.my-cache.transaction.transaction_manager_lookup = org.infinispan.client.hotrod.transaction.lookup.GenericTransactionManagerLookup
infinispan.client.hotrod.cache.my-cache.transaction.transaction_mode = NON_XA
infinispan.client.hotrod.transaction.timeout = 60000
Copy to Clipboard Toggle word wrap
4.7.2.1. TransactionManagerLookup Interface

TransactionManagerLookup 提供了一个入口点,用于获取 事务管理器

TransactionManagerLookup 可用实现:

GenericTransactionManagerLookup
查找类,用于定位在 Java EE 应用服务器中运行的 TransactionManager。如果无法找到 TransactionManager,则默认为 RemoteTransactionManager。这是 Hot Rod Java 客户端的默认值。
提示

在大多数情况下,GenericTransactionManagerLookup 是合适的。但是,如果您需要集成一个自定义事务管理器,您可以实施 TransactionManager Lookup 接口。

RemoteTransactionManagerLookup
如果没有其他实施,则 basic 和 volatile(易失性) 事务管理器。请注意,这个实现在处理并发事务和恢复时会存在很大的限制。

4.7.3. 事务模式

TransactionMode 控制 远程Cache 如何与 TransactionManager 交互。

重要

在 Data Grid 服务器和客户端应用程序中配置事务模式。如果客户端试图在非事务缓存上执行事务操作,则可能会出现运行时例外。

在 Data Grid 配置和客户端设置中,事务模式是相同的。在您的客户端使用以下模式,查看服务器的 Data Grid 配置模式:

NONE
RemoteCache 不与 TransactionManager 交互。这是默认的模式,是非事务性。
NON_XA
RemoteCache 通过 SynchronizationTransactionManager 交互。
NON_DURABLE_XA
RemoteCache 通过 XAResourceTransactionManager 交互。禁用恢复功能。
FULL_XA
RemoteCache 通过 XAResourceTransactionManager 交互。启用恢复功能。调用 XaResource.recover() 方法以检索要恢复的事务。

4.7.4. 使用事务检测冲突

事务使用密钥的初始值来检测冲突。

例如,当事务开始时,"k" 的值为"v"。在准备阶段,事务从服务器获取"k"来读取值。如果值已更改,则事务回滚以避免冲突。

注意

事务使用版本来检测变化,而不是检查值相等性。

forceRe returnValue 参数控制 远程Cache 的写操作,有助于避免冲突。它具有以下值:

  • 如果为 true,则 TransactionManager 在执行写入操作前从服务器获取最新的值。但是,forceRe returnValue 参数仅适用于编写访问第一次密钥的操作。
  • 如果为 false,则 TransactionManager 在执行写入操作前不会从服务器获取最新的值。
注意

这个参数不会影响 条件 写入操作,如 replaceputIfAbsent,因为它们需要最新的值。

以下事务提供一个示例,其中 forceRe returnValue 参数可以防止有冲突的写操作:

事务 1(TX1)

RemoteCache<String, String> cache = ...
TransactionManager tm = ...

tm.begin();
cache.put("k", "v1");
tm.commit();
Copy to Clipboard Toggle word wrap

事务 2(TX2)

RemoteCache<String, String> cache = ...
TransactionManager tm = ...

tm.begin();
cache.put("k", "v2");
tm.commit();
Copy to Clipboard Toggle word wrap

在这个示例中,TX1 和 TX2 并行执行。"k"的初始值为 "v"。

  • 如果 forceRe returnValue = true,则 cache.put() 操作会从 TX1 和 TX2 中的服务器获取 "k" 的值。达到锁定的事务,然后是"k"提交。其他事务在提交阶段回滚,因为事务可以检测到"k" 的值不是"v"。
  • 如果 forceRe returnValue = false,则 cache.put() 操作不会从服务器中获取 "k" 的值并返回 null。TX1 和 TX2 都可以成功提交,这会导致冲突。这是因为交易无法检测到"k"的初始值已更改。

以下事务包括 cache.get() 操作以在执行 cache.put() 操作前读取"k"的值:

事务 1(TX1)

RemoteCache<String, String> cache = ...
TransactionManager tm = ...

tm.begin();
cache.get("k");
cache.put("k", "v1");
tm.commit();
Copy to Clipboard Toggle word wrap

事务 2(TX2)

RemoteCache<String, String> cache = ...
TransactionManager tm = ...

tm.begin();
cache.get("k");
cache.put("k", "v2");
tm.commit();
Copy to Clipboard Toggle word wrap

在上述示例中,TX1 和 TX2 都读取密钥,因此 forceRe returnValue 参数不会生效。一个事务提交,另一个回滚。但是,cache.get() 操作需要额外的服务器请求。如果您不需要对服务器请求的效率的 cache.put() 操作返回值。

以下示例演示了如何使用您在 RemoteCacheManager 中配置的 TransactionManagerTransactionMode

//Configure the transaction manager and transaction mode.
org.infinispan.client.hotrod.configuration.ConfigurationBuilder cb = new org.infinispan.client.hotrod.configuration.ConfigurationBuilder();
cb.remoteCache("my-cache")
    .transactionManagerLookup(RemoteTransactionManagerLookup.getInstance())
    .transactionMode(TransactionMode.NON_XA);

RemoteCacheManager rcm = new RemoteCacheManager(cb.build());

//The my-cache instance uses the RemoteCacheManager configuration.
RemoteCache<String, String> cache = rcm.getCache("my-cache");

//Return the transaction manager that the cache uses.
TransactionManager tm = cache.getTransactionManager();

//Perform a simple transaction.
tm.begin();
cache.put("k1", "v1");
System.out.println("K1 value is " + cache.get("k1"));
tm.commit();
Copy to Clipboard Toggle word wrap

法律通告

Copyright © 2023 Red Hat, Inc.
The text of and illustrations in this document are licensed by Red Hat under a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA is available at http://creativecommons.org/licenses/by-sa/3.0/. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you must provide the URL for the original version.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, the Red Hat logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2026 Red Hat
返回顶部