为原子数据结构配置和使用 Geode 区域和锁

Configuring and Using Geode Regions and Locks for Atomic Data Structures

我目前正在使用 Spring Boot Starter 1.4.2.RELEASE,以及通过 Maven 孵化的 Geode Core 1.0.0,针对由 Geode Locator 组成的本地 Docker 配置,和 2 个缓存节点。

我在这里查阅了文档:

http://geode.apache.org/docs/guide/developing/distributed_regions/locking_in_global_regions.html

我已经配置了一个 cache.xml 文件用于我的应用程序,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<client-cache
        xmlns="http://geode.apache.org/schema/cache"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://geode.apache.org/schema/cache
                        http://geode.apache.org/schema/cache/cache-1.0.xsd"
        version="1.0">
    <pool name="serverPool">
        <locator host="localhost" port="10334"/>
    </pool>
    <region name="testRegion" refid="CACHING_PROXY">
        <region-attributes pool-name="serverPool"
                scope="global"/>
    </region>
</client-cache>

在我的 Application.java 中,我通过以下方式将该区域公开为一个 bean:

@SpringBootApplication
public class Application {

    @Bean
    ClientCache cache() {
        return new ClientCacheFactory()
                .create();
    }

    @Bean
    Region<String, Integer> testRegion(final ClientCache cache) {
        return cache.<String, Integer>getRegion("testRegion");
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

在我的 "service" DistributedCounter.java:

@Service
public class DistributedCounter {

@Autowired
private Region<String, Integer> testRegion;

/**
 * Using fine grain lock on modifier.
 * @param counterKey {@link String} containing the key whose value should be incremented.
 */
public void incrementCounter(String counterKey) {
    if(testRegion.getDistributedLock(counterKey).tryLock()) {
        try {
            Integer old = testRegion.get(counterKey);
            if(old == null) {
                old = 0;
            }
            testRegion.put(counterKey, old + 1);
        } finally {
            testRegion.getDistributedLock(counterKey).unlock();
        }
    }
}

我已经使用 gfsh 配置了一个名为 /testRegion 的区域 - 但是没有选项表明它的类型应该是 "GLOBAL",只有各种其他选项可用 - 理想情况下这应该是一个持久的, 并复制了缓存,因此执行以下命令:

create region --name=/testRegion --type=REPLICATE_PERSISTENT

使用操作指南:http://geode.apache.org/docs/guide/getting_started/15_minute_quickstart_gfsh.html 可以很容易地在我的双节点配置上看到持久性和复制的功能。

但是,上面的 DistributedCounter 中的锁定不会导致任何错误 - 但当两个进程试图获取同一锁时它不起作用 "key" - 第二个进程未被阻止获取锁。 Gemfire 论坛中有一个较早的代码示例,它使用 DistributedLockService - 当前文档警告不要将其用于锁定区域条目。

用于支持 "map" 原子增量 long 的细粒度锁定的用例是受支持的用例吗?如果是,如何适当地配置它?

这仅适用于服务器端代码(例如在函数中)。

在客户端代码中,您可以使用 "region.putIfAbsent" 实现锁定语义。

如果 2 个(或更多)客户端在同一区域和密钥上调用此 API,则只有一个会成功放置,这由 return 值为 null 表示。该客户端被认为持有锁。其他客户将获得获胜者放置的物品。这很方便,因为如果您 "put" 的值包含客户端的唯一标识符,那么输家甚至知道谁持有锁。

让区域条目代表一把锁还有其他好处。锁在失败后仍然存在。可以使用region expiration来设置锁的最长租用时间,而且如前所述,很容易判断谁持有锁。

希望这对您有所帮助。

GFSH 似乎没有提供正确的 scope=GLOBAL 选项。

也许您可以使用 --cache-xml-file 选项启动服务器...这将指向 cache.xml 文件。

cache.xml 文件应如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schema.pivotal.io/gemfire/cache" xsi:schemaLocation="http://schema.pivotal.io/gemfire/cache http://schema.pivotal.io/gemfire/cache/cache-8.1.xsd" version="8.1" lock-lease="120" lock-timeout="60" search-timeout="300" is-server="true" copy-on-read="false">
<cache-server port="0"/>
<region name="testRegion">
    <region-attributes data-policy="persistent-replicate" scope="global"/>
</region>
</cache>

另外客户端配置不需要在region-attributes

中定义范围

DistributedLock 和 RegionDistributedLock 的区域 APIs 仅支持具有全局范围的区域。这些 DistributedLocks 的锁定范围在 DistributedLockService 的名称(这是 Region 的完整路径名)内,仅在集群内。例如,如果全局区域存在于服务器上,则该区域的分布式锁只能在该服务器或该集群中的其他服务器上使用。

缓存客户端最初是一种分层缓存的形式,这意味着一个集群可以作为客户端连接到另一个集群。如果一个客户端创建了一个实际的全局区域,那么客户端中的 DistributedLock 将只在该客户端及其所属的集群中有一个范围。 DistributedLocks 不会以任何方式传播到此类客户端连接到的服务器。

正确的方法是编写利用服务器上存在的全局区域上的 DistributedLock API 的函数。您可以将这些功能部署到服务器,然后从客户端在服务器上调用它们。

一般来说,避免使用全局区域,因为每个单独的 put 都会在服务器集群中获取一个 DistributedLock,这是一个非常昂贵的操作。

您可以通过在服务器上创建自定义 DistributedLockService 来对非全球区域执行类似的操作,然后使用函数 lock/unlock 围绕您需要在该集群内进行全局同步的代码。在这种情况下,区域(对于非全局区域)上的 DistributedLock 和 RegionDistributedLock API 将不可用,并且必须使用 DistributedLockService API 在服务器上的函数内完成所有锁定。