1. 概述

Apache Geode 是一个分布式内存数据网格,支持缓存和数据计算功能。

本教程将带你了解 Geode 的核心概念,并通过一些 Java 客户端代码示例来快速上手。

2. 环境准备

首先,我们需要下载并安装 Apache Geode,并配置好 gfsh 环境。可以参考 Geode 官方文档 进行操作。

其次,本教程会生成一些文件系统内容,建议创建一个临时目录用于隔离这些操作。

2.1. 安装与配置

在临时目录下,我们先启动一个 Locator 实例:

gfsh> start locator --name=locator --bind-address=localhost

Locator 负责 Geode 集群成员之间的协调,同时也支持通过 JMX 进行管理。

接着,启动一个 Server 实例,用于承载一个或多个数据 Region

gfsh> start server --name=server1 --server-port=0

⚠️ 这里 --server-port=0 表示让 Geode 自动选择一个可用端口。如果省略该参数,默认使用 40404 端口。

Server 是集群中的一个可配置成员,以长时间运行的进程方式存在,负责管理数据 Region

最后,创建一个 Region

gfsh> create region --name=baeldung --type=REPLICATE

Region 是我们实际存储数据的地方。

2.2. 验证环境

我们可以通过以下命令验证环境是否配置正确:

查看集群成员:

gfsh> list members
 Name   | Id
------- | ----------------------------------------------------------
server1 | 192.168.0.105(server1:6119)<v1>:1024
locator | 127.0.0.1(locator:5996:locator)<ec><v0>:1024 [Coordinator]

查看 Region 信息:

gfsh> describe region --name=baeldung
..........................................................
Name            : baeldung
Data Policy     : replicate
Hosting Members : server1

Non-Default Attributes Shared By Hosting Members  

 Type  |    Name     | Value
------ | ----------- | ---------------
Region | data-policy | REPLICATE
       | size        | 0
       | scope       | distributed-ack

此外,临时目录下应该生成了 locatorserver1 两个子目录。

看到这些输出,说明环境已经准备就绪 ✅

3. Maven 依赖

接下来,我们开始编写客户端代码。

要在 Java 项目中使用 Geode,需要添加 Apache Geode Java 客户端 依赖:

<dependency>
     <groupId>org.apache.geode</groupId>
     <artifactId>geode-core</artifactId>
     <version>1.6.0</version>
</dependency>

我们从最基础的数据存取开始。

4. 简单的数据存取

我们演示如何存储单个值、批量值,以及自定义对象。

首先,连接到 Geode 的 baeldung Region:

@Before
public void connect() {
    this.cache = new ClientCacheFactory()
      .addPoolLocator("localhost", 10334)
        .create();
    this.region = cache.<String, String> 
      createClientRegionFactory(ClientRegionShortcut.CACHING_PROXY)
        .create("baeldung");
}

4.1. 存储单个值

@Test
public void whenSendMessageToRegion_thenMessageSavedSuccessfully() {

    this.region.put("A", "Hello");
    this.region.put("B", "Baeldung");

    assertEquals("Hello", region.get("A"));
    assertEquals("Baeldung", region.get("B"));
}

4.2. 批量存储

@Test
public void whenPutMultipleValuesAtOnce_thenValuesSavedSuccessfully() {

    Supplier<Stream<String>> keys = () -> Stream.of("A", "B", "C", "D", "E");
    Map<String, String> values = keys.get()
        .collect(Collectors.toMap(Function.identity(), String::toLowerCase));

    this.region.putAll(values);

    keys.get()
        .forEach(k -> assertEquals(k.toLowerCase(), this.region.get(k)));
}

4.3. 存储自定义对象

先定义两个类:

public class CustomerKey implements Serializable {
    private long id;
    private String country;
    
    // getters and setters
    // equals and hashcode
}
public class Customer implements Serializable {
    private CustomerKey key;
    private String firstName;
    private String lastName;
    private Integer age;
    
    // getters and setters 
}

✅ 注意事项:

  • 实现 Serializable 接口
  • 类必须同时存在于应用和 Geode Server 的 classpath 中

打包应用后,重启 Server 并指定 classpath:

gfsh> stop server --name=server1
gfsh> start server --name=server1 --classpath=../lib/apache-geode-1.0-SNAPSHOT.jar --server-port=0

创建新的 Region:

gfsh> create region --name=baeldung-customers --type=REPLICATE

客户端代码中连接该 Region:

@Before
public void connect() {
    this.customerRegion = this.cache.<CustomerKey, Customer> 
      createClientRegionFactory(ClientRegionShortcut.CACHING_PROXY)
        .create("baeldung-customers");
}

存储自定义对象:

@Test
public void whenPutCustomKey_thenValuesSavedSuccessfully() {
    CustomerKey key = new CustomerKey(123);
    Customer customer = new Customer(key, "William", "Russell", 35);

    this.customerRegion.put(key, customer);

    Customer storedCustomer = this.customerRegion.get(key);
    assertEquals("William", storedCustomer.getFirstName());
    assertEquals("Russell", storedCustomer.getLastName());
}

5. Region 类型

5.1. 复制型 Region(Replicated Region)

✅ 特点:数据在多个 Server 之间完全复制。

添加一个新 Server:

gfsh> start server --name=server2 --classpath=../lib/apache-geode-1.0-SNAPSHOT.jar --server-port=0

由于我们使用的是 --type=REPLICATE,Geode 会自动将数据复制到新 Server。

验证复制是否成功:

gfsh> stop server --name=server1
gfsh> query --query='select e.key from /baeldung.entries e'
Result : true
Limit  : 100
Rows   : 5

Result
------
C
B
A 
E
D

✅ 数据仍然可访问,说明复制成功。

⚠️ 但这种 Region 是纯内存的,Server 崩溃后数据会丢失。如果需要持久化,可使用 --type=REPLICATE_PERSISTENT

5.2. 分区型 Region(Partitioned Region)

✅ 特点:数据被分片存储在不同的 Server 上。

创建分区 Region:

gfsh> create region --name=baeldung-partitioned --type=PARTITION

插入数据:

gfsh> put --region=baeldung-partitioned --key="1" --value="one"
gfsh> put --region=baeldung-partitioned --key="2" --value="two"
gfsh> put --region=baeldung-partitioned --key="3" --value="three"

查询验证:

gfsh> query --query='select e.key, e.value from /baeldung-partitioned.entries e'
Result : true
Limit  : 100
Rows   : 3

key | value
--- | -----
2   | two
1   | one
3   | three

停止 server1 后再次查询:

gfsh> stop server --name=server1
gfsh> query --query='select e.key, e.value from /baeldung-partitioned.entries e'
Result : true
Limit  : 100
Rows   : 1

key | value
--- | -----
2   | two

⚠️ 只返回部分数据,说明数据已丢失。

✅ 如果既要分区又要高可用,可以使用:

  • PARTITION_REDUNDANT:分区 + 冗余
  • PARTITION_PERSISTENT:分区 + 持久化
  • PARTITION_REDUNDANT_PERSISTENT:分区 + 冗余 + 持久化

6. 对象查询语言(OQL)

Geode 支持类似 SQL 的查询语言 OQL。

示例:查找 firstName 为 "Allan" 的客户:

Map<CustomerKey, Customer> data = new HashMap<>();
data.put(new CustomerKey(1), new Customer("Gheorge", "Manuc", 36));
data.put(new CustomerKey(2), new Customer("Allan", "McDowell", 43));
this.customerRegion.putAll(data);

执行查询:

QueryService queryService = this.cache.getQueryService();
String query = 
  "select * from /baeldung-customers c where c.firstName = 'Allan'";
SelectResults<Customer> results =
  (SelectResults<Customer>) queryService.newQuery(query).execute();
assertEquals(1, results.size());

7. 函数执行(Function Execution)

Geode 支持将计算逻辑发送到数据所在节点执行。

7.1. 定义 Function

实现 Geode 的 Function 接口:

public class UpperCaseNames implements Function<Boolean> {
    @Override
    public void execute(FunctionContext<Boolean> context) {
        RegionFunctionContext regionContext = (RegionFunctionContext) context;
        Region<CustomerKey, Customer> region = regionContext.getDataSet();

        for ( Map.Entry<CustomerKey, Customer> entry : region.entrySet() ) {
            Customer customer = entry.getValue();
            customer.setFirstName(customer.getFirstName().toUpperCase());
        }
        context.getResultSender().lastResult(true);   
    }

    @Override
    public String getId() {
        return getClass().getName();
    }
}

7.2. 部署 Function

gfsh> deploy --jar=./lib/apache-geode-1.0-SNAPSHOT.jar

7.3. 执行 Function

@Test
public void whenExecuteUppercaseNames_thenCustomerNamesAreUppercased() {
    Execution execution = FunctionService.onRegion(this.customerRegion);
    execution.execute(UpperCaseNames.class.getName());
    Customer customer = this.customerRegion.get(new CustomerKey(1));
    assertEquals("GHEORGE", customer.getFirstName());
}

8. 总结

本教程带你快速了解了 Apache Geode 的核心功能:

  • 基本的存储与查询
  • 自定义对象的使用
  • 复制与分区 Region 的区别
  • OQL 查询支持
  • Function 执行机制

所有示例代码可在 GitHub 获取。


原始标题:A Quick Guide to Apache Geode