3.5. 创建嵌入式缓存
Data Grid 提供了一个 EmbeddedCacheManager
API,可让您以编程方式控制缓存管理器和嵌入式缓存生命周期。
3.5.1. 在项目中添加 Data Grid
将 Data Grid 添加到项目,以便在您的应用程序中创建嵌入的缓存。
先决条件
- 配置项目以从 Maven 存储库获取数据网格工件。
流程
-
将
infinispan-core
工件作为依赖项添加到pom.xml
中,如下所示:
<dependencies> <dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-core</artifactId> </dependency> </dependencies>
3.5.2. 创建和使用嵌入式缓存
Data Grid 提供了一个 GlobalConfigurationBuilder
API,用于控制 Cache Manager 和用于配置缓存的 ConfigurationBuilder
API。
先决条件
-
将
infinispan-core
工件添加为pom.xml
中的依赖项。
流程
初始化
CacheManager
。注意在创建缓存前,您必须始终调用
cacheManager.start ()
方法来初始化CacheManager
。默认构造器为您完成此操作,但构建器的超载版本不会为您这样做。缓存管理器也是重量的对象,Data Grid 建议实例化每个 JVM 只有一个实例。
-
使用
ConfigurationBuilder
API 定义缓存配置。 使用
getCache ()
、createCache ()
或getOrCreateCache ()
方法获取缓存。Data Grid 建议使用
getOrCreateCache ()
方法,因为它在所有节点上创建一个缓存或返回现有的缓存。-
如有必要,对缓存使用
PERMANENT
标志,以在重启后保留。 -
通过调用
cacheManager.stop ()
方法以释放 JVM 资源并正常关闭任何缓存,以停止CacheManager
。
// Set up a clustered Cache Manager. GlobalConfigurationBuilder global = GlobalConfigurationBuilder.defaultClusteredBuilder(); // Initialize the default Cache Manager. DefaultCacheManager cacheManager = new DefaultCacheManager(global.build()); // Create a distributed cache with synchronous replication. ConfigurationBuilder builder = new ConfigurationBuilder(); builder.clustering().cacheMode(CacheMode.DIST_SYNC); // Obtain a volatile cache. Cache<String, String> cache = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache("myCache", builder.build()); // Stop the Cache Manager. cacheManager.stop();
getCache ()
方法
调用 getCache (String)
方法以获取缓存,如下所示:
Cache<String, String> myCache = manager.getCache("myCache");
前面的操作会创建一个名为 myCache
的缓存(如果尚未存在),并返回它。
使用 getCache ()
方法仅在您调用方法的节点上创建缓存。换句话说,它会执行一个本地操作,它必须在集群中的每个节点上调用。通常,在多个节点间部署的应用程序会在初始化过程中获取缓存,以确保缓存都是 对称的,并在每个节点上存在。
createCache ()
方法
调用 createCache ()
方法,以在整个集群中动态创建缓存。
Cache<String, String> myCache = manager.administration().createCache("myCache", "myTemplate");
前面的操作还会在随后加入集群的任何节点上自动创建缓存。
默认情况下,您使用 createCache ()
方法创建的缓存是临时的。如果整个集群关闭,则缓存重启后不会自动创建。
PERMANENT
标志
使用 PERMANENT 标志来确保缓存可以在重启后保留。
Cache<String, String> myCache = manager.administration().withFlags(AdminFlag.PERMANENT).createCache("myCache", "myTemplate");
要使 PERMANENT 标志生效,您必须启用全局状态并设置配置存储提供程序。
有关配置存储供应商的更多信息,请参阅 GlobalStateConfigurationBuilder#configurationStorage ()。
3.5.3. Cache API
Data Grid 提供了一个 Cache 接口,它公开简单的方法来添加、检索和删除条目,包括 JDK 的 ConcurrentMap 接口公开的原子机制。根据所使用的缓存模式,调用这些方法会触发多个事情,甚至可能包括将条目复制到远程节点或从远程节点查找条目,或可能缓存存储。
对于简单使用,使用 Cache API 不应与使用 JDK Map API 不同,因此从基于映射到 Data Grid 的缓存的简单内存缓存迁移应该很简单。
Certain Map 方法的性能一致性
与 Data Grid 一起使用时,在 Map 中公开的某些方法具有一定的性能后果,如 size ()、value ()、keySet () 和 entrySet ()。有关 keySet
、value 和 entrySet
的具体方法,请参阅其 Javadoc 了解更多详情。
试图全局执行这些操作会对性能有很大的影响,并成为可扩展性瓶颈。因此,这些方法应该仅用于信息或调试目的。
应注意,在 withFlags () 方法中使用某些标记可以缓解其中的一些问题,请检查每个方法的文档以了解更多详情。
Mortal 和 Immortal Data
除了仅存储条目外,Data Grid 的缓存 API 允许您向数据附加 mortality 信息。例如,只是使用 put (键,值) 会创建一个 immortal 条目,例如,一个存在于缓存中的条目(永久存在),直到它被删除(或逐出内存以防止耗尽内存)。但是,如果您使用 put (key, value, lifespan, timeunit) 将数据放入缓存中,这会创建一个 mortal 条目,即具有固定生命周期的条目,并在该生命周期后过期。
除了 Lifespan 外,Data Grid 还支持 maxIdle 作为额外的指标,以决定到期。可以使用任何 lifespans 或 maxIdles 的组合。
putForExternalRead
操作
Data Grid 的 Cache 类包含不同的"put"操作,称为 putForExternalRead。当 Data Grid 用作在其他位置保留数据的临时缓存时,此操作特别有用。在大量读取场景中,缓存中的竞争不应延迟实时事务,因为缓存应该只是优化,而不是以某种方式获得。
要达到此目的,putForExternalRead ()
充当 put 调用,只有在缓存中不存在密钥时才运行,并在另一个线程尝试同时存储同一密钥时失败。在这个特殊情况下,缓存数据是优化系统的方法,而不需要缓存失败会影响到持续的事务,因此为什么以不同的方式处理失败。putForExternalRead ()
被视为快速操作,因为无论它是否成功,它都不会等待任何锁定,因此会立即返回到调用者。
要了解如何使用此操作,让我们来看基本的示例。试想一下,个人实例的缓存,每个由 PersonId 的密钥,其数据源自于单独的数据存储中。以下代码显示了在此示例上下文中使用 putForExternalRead 的最常见模式:
// Id of the person to look up, provided by the application PersonId id = ...; // Get a reference to the cache where person instances will be stored Cache<PersonId, Person> cache = ...; // First, check whether the cache contains the person instance // associated with with the given id Person cachedPerson = cache.get(id); if (cachedPerson == null) { // The person is not cached yet, so query the data store with the id Person person = dataStore.lookup(id); // Cache the person along with the id so that future requests can // retrieve it from memory rather than going to the data store cache.putForExternalRead(id, person); } else { // The person was found in the cache, so return it to the application return cachedPerson; }
请注意,putForExternalRead 不应用作使用源自应用程序执行的新 Person 实例更新缓存的机制(例如,来自修改人员地址的事务)。更新缓存的值时,请使用标准 放置 操作,否则可能会出现缓存损坏数据的可能性。
3.5.3.1. AdvancedCache API
除了简单缓存接口外,Data Grid 还提供 AdvancedCache 接口,并提供给扩展作者。AdvancedCache 提供了访问某些内部组件,并应用标志来改变某些缓存方法的默认行为。以下代码片段描述了如何获取 AdvancedCache:
AdvancedCache advancedCache = cache.getAdvancedCache();
3.5.3.1.1. 标记
标志应用到常规缓存方法,以更改某些方法的行为。有关所有可用标志及其效果的列表,请查看 标记 枚举。使用 AdvancedCache.withFlags () 应用标志。此 builder 方法可用于将任意数量的标记应用到缓存调用,例如:
advancedCache.withFlags(Flag.CACHE_MODE_LOCAL, Flag.SKIP_LOCKING) .withFlags(Flag.FORCE_SYNCHRONOUS) .put("hello", "world");
3.5.3.2. Asynchronous API
除了同步的 API 方法(如 Cache.put ()、Cache.remove () 等)之外,数据网格也有一个异步的非阻塞 API,您可以在其中实现相同的结果。
这些方法的命名方式与阻塞计数器类似,并附加"Async"。 例如,Cache.putAsync ()、Cache.removeAsync () 等。 这些异步对应部分返回一个包含操作实际结果的 CompletableFuture。
例如,在 cache 参数中,在 Cache<String, String>
, Cache.put (String key, String value)
返回 String
while Cache.putAsync (String key) returns Complet
ableFuture<String>
;。
3.5.3.2.1. 为什么使用这种 API?
非阻塞 API 非常强大,它们提供了所有同步通信保证 - 能够处理通信故障和例外 - 在调用完成后不需要阻止。 这可让您更好地利用系统中的并行性。 例如:
Set<CompletableFuture<?>> futures = new HashSet<>(); futures.add(cache.putAsync(key1, value1)); // does not block futures.add(cache.putAsync(key2, value2)); // does not block futures.add(cache.putAsync(key3, value3)); // does not block // the remote calls for the 3 puts will effectively be executed // in parallel, particularly useful if running in distributed mode // and the 3 keys would typically be pushed to 3 different nodes // in the cluster // check that the puts completed successfully for (CompletableFuture<?> f: futures) f.get();
3.5.3.2.2. 哪些进程实际发生在异步中?
Data Grid 中有 4 个事情,它们被视为典型写入操作的关键路径。这些是成本顺序:
- 网络调用
- Marshalling
- 写入缓存存储(可选)
- 锁定
使用 async 方法将取网络调用和划分出关键路径。 然而,出于各种技术原因,写入缓存存储和获取锁定,但仍然出现在调用者的线程中。