Краткое руководство по Apache Geode

Краткое руководство по Apache Geode

1. обзор

Apache Geode - это распределенная сетка данных в памяти, поддерживающая кэширование и вычисление данных.

В этом руководстве мы рассмотрим ключевые концепции Geode и рассмотрим несколько примеров кода с использованием его клиента Java.

2. Настроить

Во-первых, нам нужно загрузить и установить Apache Geode и установить средуgfsh . Для этого мы можем следовать инструкциям вGeode’s official guide.

И второе, этот урок создаст некоторые артефакты файловой системы. Таким образом, мы можем изолировать их, создав временный каталог и запустив все оттуда.

2.1. Установка и настройка

Из нашего временного каталога нам нужно запустить экземплярLocator:

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

Locators are responsible for the coordination between different members of a Geode Cluster,, который мы можем в дальнейшем администрировать через JMX.

Затем давайте запустим экземплярServer для размещения одного или нескольких данныхRegions:

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

Мы устанавливаем для параметра–server-port значение 0, чтобы Geode выбрал любой доступный порт. Хотя, если мы пропустим это, сервер будет использовать порт по умолчанию 40404. A server is a configurable member of the Cluster that runs as a long-lived process and is responsible for managing data Regions.

И наконец, нам понадобитсяRegion:

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

Region - это, в конечном счете, место, где мы будем хранить наши данные.

2.2. верификация

Прежде чем идти дальше, убедитесь, что у нас все работает.

Во-первых, давайте проверим, есть ли у насServer иLocator:

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

А теперь у нас естьRegion:

gfsh> describe region --name=example
..........................................................
Name            : example
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

Кроме того, у нас должны быть некоторые каталоги в файловой системе в нашем временном каталоге, называемые «locator» и «server1».

Получив эти результаты, мы знаем, что готовы двигаться дальше.

3. Maven Dependency

Теперь, когда у нас есть работающий Geode, давайте приступим к рассмотрению клиентского кода.

Для работы с Geode в нашем Java-коде нам нужно добавить библиотекуApache Geode Java client в нашpom:


     org.apache.geode
     geode-core
     1.6.0

Начнем с простого сохранения и извлечения данных в нескольких регионах.

4. Простое хранение и поиск

Давайте продемонстрируем, как хранить отдельные значения, пакеты значений, а также настраиваемые объекты.

Чтобы начать хранить данные в нашем «примерном» регионе, давайте подключимся к нему с помощью локатора:

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

4.1. Сохранение одиночных значений

Теперь мы можем просто хранить и извлекать данные в нашем регионе:

@Test
public void whenSendMessageToRegion_thenMessageSavedSuccessfully() {

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

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

4.2. Одновременное сохранение нескольких значений

Мы также можем сохранить несколько значений одновременно, например, при попытке уменьшить задержку в сети:

@Test
public void whenPutMultipleValuesAtOnce_thenValuesSavedSuccessfully() {

    Supplier> keys = () -> Stream.of("A", "B", "C", "D", "E");
    Map 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
}

Есть несколько дополнительных шагов, чтобы иметь возможность хранить их:

Во-первых,they should implement Serializable, хотя это не является строгим требованием, сделав ихSerializable,Geode can store them more robustly.

Во-вторых,they need to be on our application’s classpath as well as the classpath of our Geode Server.

Чтобы направить их в путь к классам сервера, давайте упакуем их, скажем, с помощьюmvn clean package.

А затем мы можем ссылаться на полученный jar в новой командеstart server:

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

Опять же, мы должны запускать эти команды из временного каталога.

Наконец, давайте создадим новыйRegion с именем «example-customers» наServer, используя ту же команду, которую мы использовали для создания области «example»:

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

В коде мы, как и раньше, обратимся к локатору, указав пользовательский тип:

@Before
public void connect() {
    // ... connect through the locator
    this.customerRegion = this.cache.
      createClientRegionFactory(ClientRegionShortcut.CACHING_PROXY)
        .create("example-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. Типы регионов

Для большинства сред у нас будет более одной копии или более одного раздела нашего региона, в зависимости от наших требований к пропускной способности чтения и записи.

До сих пор мы использовали реплицированные регионы в памяти. Давайте посмотрим поближе.

5.1. Реплицированный регион

Как следует из названия,a Replicated Region maintains copies of its data on more than one ServerДавайте проверим это.

Из консолиgfsh в рабочем каталоге добавим в кластер еще одинServer с именемserver2:

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

Помните, что когда мы делали «пример», мы использовали–type=REPLICATE. Из-за этогоGeode will automatically replicate our data to the new server.

Давайте проверим это, остановивserver1:

gfsh> stop server --name=server1

А затем давайте выполним быстрый запрос в «примерной» области.

Если данные были реплицированы успешно, мы получим результаты:

gfsh> query --query='select e.key from /example.entries e'
Result : true
Limit  : 100
Rows   : 5

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

Итак, похоже, репликация прошла успешно!

Добавление реплики в наш регион улучшает доступность данных. А поскольку на запросы могут отвечать более одного сервера, мы также увеличим скорость чтения.

Ноwhat if they both crash? Since these are in-memory regions, the data will be lost. Для этого мы можем вместо этого использовать–type=REPLICATE_PERSISTENT, который также сохраняет данные на диске во время репликации.

5.2. Разделенная область

С большими наборами данных мы можем лучше масштабировать систему, настроив Geode для разделения региона на отдельные разделы или сегменты.

Давайте создадим один разделенныйRegion с именем "example-partitioned":

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

Добавьте некоторые данные:

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

И быстро проверим:

gfsh> query --query='select e.key, e.value from /example-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 /example-partitioned.entries e'
Result : true
Limit  : 100
Rows   : 1

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

На этот раз мы вернули только некоторые записи данных, потому что на этом сервере есть только один раздел данных, поэтому при удаленииserver1 его данные были потеряны.

But what if we need both partitioning and redundancy? Geode также поддерживаетa number of other types. Следующие три удобны:

  • PARTITION_REDUNDANT разделовand реплицирует наши данные по разным членам кластера

  • PARTITION_PERSISTENT разделяет данные какPARTITION, но на диск, и

  • PARTITION_REDUNDANT_PERSISTENT  дает нам все три варианта поведения.

6. Язык объектных запросов

Geode также поддерживает Object Query Language или OQL, который может быть более мощным, чем простой поиск ключей. Это немного похоже на SQL.

В этом примере давайте воспользуемся регионом «пример-клиент», который мы создали ранее.

Если мы добавим еще пару клиентов:

Map 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 queryService = this.cache.getQueryService();
String query =
  "select * from /example-customers c where c.firstName = 'Allan'";
SelectResults results =
  (SelectResults) queryService.newQuery(query).execute();
assertEquals(1, results.size());

7. функция

Одним из наиболее мощных понятий сеток данных в памяти является идея «переноса вычислений на данные».

Проще говоря, поскольку Geode - это чистая Java,it’s easy for us to not only send data but also logic to perform on that data.

Это может напомнить нам об идее расширений SQL, таких как PL-SQL или Transact-SQL.

7.1. Определение функции

Чтобы определить единицу работы для Geode, we реализует интерфейс GeodeFunction.

Например, представим, что нам нужно изменить все имена клиентов на верхний регистр.

Вместо того, чтобы запрашивать данные и заставлять наше приложение выполнять работу, мы можем просто реализоватьFunction:

public class UpperCaseNames implements Function {
    @Override
    public void execute(FunctionContext context) {
        RegionFunctionContext regionContext = (RegionFunctionContext) context;
        Region region = regionContext.getDataSet();

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

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

Обратите внимание, чтоgetId must возвращает уникальное значение, поэтому имя класса обычно является хорошим выбором.

FunctionContext содержит все данные нашего региона, поэтому мы можем выполнить более сложный запрос или, как мы сделали здесь, изменить его.

ИFunction имеет гораздо большую мощность, чем это, поэтому проверьтеthe official manual, особенноthe getResultSender method.

7.2. Функция развертывания

Нам нужно, чтобы Geode знал о нашей функции, чтобы иметь возможность ее запускать. Как и в случае с нашими пользовательскими типами данных, мы упакуем jar.

Но на этот раз мы можем просто использовать командуdeploy:

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

7.3. Выполнение функции

Теперь мы можем выполнитьFunction из приложения, используяFunctionService:

@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. Мы рассмотрели простые операции получения и размещения со стандартными и пользовательскими типами, реплицированные и разделенные области, а также поддержку oql и функций.

И как всегда все эти образцы доступныover on GitHub.