Um guia rápido para o Apache Geode

Um guia rápido para o Apache Geode

1. Visão geral

Apache Geode é uma grade de dados distribuída na memória que suporta armazenamento em cache e computação de dados.

Neste tutorial, cobriremos os principais conceitos do Geode e executaremos alguns exemplos de código usando seu cliente Java.

2. Configuração

Primeiro, precisamos baixar e instalar o Apache Geode e definir o ambientegfsh . Para fazer isso, podemos seguir as instruções emGeode’s official guide.

E segundo, este tutorial criará alguns artefatos do sistema de arquivos. Portanto, podemos isolá-los criando um diretório temporário e iniciando coisas a partir daí.

2.1. Instalação e configuração

Em nosso diretório temporário, precisamos iniciar uma instânciaLocator:

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

Locators are responsible for the coordination between different members of a Geode Cluster, que podemos administrar posteriormente no JMX.

A seguir, vamos iniciar uma instânciaServer para hospedar um ou mais dadosRegions:

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

Definimos a opção–server-port como 0 para que o Geode escolha qualquer porta disponível. Embora, se a deixarmos de fora, o servidor usará a porta padrão 40404. A server is a configurable member of the Cluster that runs as a long-lived process and is responsible for managing data Regions.

E, finalmente, precisamos de umRegion:

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

ORegion é basicamente onde armazenaremos nossos dados.

2.2. Verificação

Vamos ter certeza de que tudo está funcionando antes de prosseguirmos.

Primeiro, vamos verificar se temos nossoServer lixar nossoLocator:

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

E a seguir, temos nossoRegion:

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

Além disso, devemos ter alguns diretórios no sistema de arquivos em nosso diretório temporário chamado "localizador" e "servidor1".

Com essa saída, sabemos que estamos prontos para seguir em frente.

3. Dependência do Maven

Agora que temos um Geode em execução, vamos começar a olhar para o código do cliente.

Para trabalhar com Geode em nosso código Java, vamos precisar adicionar a bibliotecaApache Geode Java client ao nossopom:


     org.apache.geode
     geode-core
     1.6.0

Vamos começar simplesmente armazenando e recuperando alguns dados em algumas regiões.

4. Armazenamento e recuperação simples

Vamos demonstrar como armazenar valores únicos, lotes de valores, bem como objetos personalizados.

Para começar a armazenar dados em nossa região de "exemplo", vamos conectar a ela usando o localizador:

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

4.1. Salvando Valores Únicos

Agora, podemos simplesmente armazenar e recuperar dados em nossa região:

@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. Salvando vários valores de uma vez

Também podemos salvar vários valores de uma vez, digamos, ao tentar reduzir a latência da rede:

@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. Salvando objetos personalizados

Strings são úteis, mas mais cedo ou mais tarde precisaremos armazenar objetos personalizados.

Vamos imaginar que temos um registro de cliente que queremos armazenar usando o seguinte tipo de chave:

public class CustomerKey implements Serializable {
    private long id;
    private String country;

    // getters and setters
    // equals and hashcode
}

E o seguinte tipo de valor:

public class Customer implements Serializable {
    private CustomerKey key;
    private String firstName;
    private String lastName;
    private Integer age;

    // getters and setters
}

Existem algumas etapas extras para poder armazenar estas:

Primeiro,they should implement SerializableEmbora este não seja um requisito estrito, tornando-osSerializable,Geode can store them more robustly.

Em segundo lugar,they need to be on our application’s classpath as well as the classpath of our Geode Server.

Para levá-los ao classpath do servidor, vamos empacotá-los, digamos usandomvn clean package.

E então podemos fazer referência ao jar resultante em um novo comandostart server:

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

Novamente, temos que executar esses comandos no diretório temporário.

Finalmente, vamos criar um novoRegion chamado “clientes-exemplo” emServer usando o mesmo comando que usamos para criar a região “exemplo”:

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

No código, entraremos em contato com o localizador como antes, especificando o tipo personalizado:

@Before
public void connect() {
    // ... connect through the locator
    this.customerRegion = this.cache.
      createClientRegionFactory(ClientRegionShortcut.CACHING_PROXY)
        .create("example-customers");
}

E, então, podemos armazenar nosso cliente como antes:

@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. Tipos de região

Para a maioria dos ambientes, teremos mais de uma cópia ou mais de uma partição de nossa região, dependendo de nossos requisitos de capacidade de leitura e gravação.

Até agora, usamos regiões replicadas na memória. Vamos olhar mais de perto.

5.1. Região Replicada

Como o nome sugere,a Replicated Region maintains copies of its data on more than one ServerVamos testar isso.

Do consolegfsh no diretório de trabalho, vamos adicionar mais umServer chamadoserver2 ao cluster:

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

Lembre-se que quando fizemos “exemplo”, usamos–type=REPLICATE. Por causa disso,Geode will automatically replicate our data to the new server.

Vamos verificar isso parandoserver1:

gfsh> stop server --name=server1

E, então, vamos executar uma consulta rápida na região de "exemplo".

Se os dados foram replicados com sucesso, obteremos os resultados de volta:

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

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

Portanto, parece que a replicação foi bem-sucedida!

Adicionar uma réplica à nossa região melhora a disponibilidade dos dados. E, como mais de um servidor pode responder às consultas, também obteremos maior taxa de transferência de leitura.

Mas,what if they both crash? Since these are in-memory regions, the data will be lost. Para isso, podemos usar–type=REPLICATE_PERSISTENT, que também armazena os dados no disco durante a replicação.

5.2. Região particionada

Com conjuntos de dados maiores, podemos dimensionar melhor o sistema configurando o Geode para dividir uma região em partições separadas ou buckets.

Vamos criar umRegion particionado denominado “particionado por exemplo”:

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

Adicione alguns dados:

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"

E verifique rapidamente:

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

Então, para validar se os dados foram particionados, vamos pararserver1 novamente e consultar novamente:

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

Desta vez, recuperamos apenas algumas das entradas de dados porque esse servidor só tem uma partição de dados, portanto, quandoserver1 cair, seus dados serão perdidos.

But what if we need both partitioning and redundancy? Geode também suportaa number of other types. Os três seguintes são úteis:

  • PARTITION_REDUNDANT partiçõesand replica nossos dados em diferentes membros do cluster

  • PARTITION_PERSISTENT particiona os dados comoPARTITION, mas para o disco, e

  • PARTITION_REDUNDANT_PERSISTENT nos dá todos os três comportamentos.

6. Linguagem de consulta de objeto

O Geode também oferece suporte à Object Query Language, ou OQL, que pode ser mais poderosa do que uma simples pesquisa de chave. É um pouco como SQL.

Para este exemplo, vamos usar a região “cliente-exemplo” que construímos anteriormente.

Se adicionarmos mais alguns clientes:

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);

Então, podemos usarQueryService para encontrar clientes cujo primeiro nome é “Allan”:

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. Função

Uma das noções mais poderosas de grades de dados na memória é a idéia de "levar os cálculos aos dados".

Simplificando, como Geode é Java puro,it’s easy for us to not only send data but also logic to perform on that data.

Isso pode nos lembrar da idéia de extensões SQL como PL-SQL ou Transact-SQL.

7.1. Definindo uma Função

Para definir uma unidade de trabalho para Geode fazer, we implementa a interfaceFunction do Geode.

Por exemplo, vamos imaginar que precisamos mudar todos os nomes do cliente para maiúsculas.

Em vez de consultar os dados e fazer com que nosso aplicativo faça o trabalho, podemos apenas implementarFunction:

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();
    }
}

Observe quegetId must retorna um valor exclusivo, portanto, o nome da classe é normalmente uma boa escolha.

OFunctionContext contém todos os nossos dados de região e, portanto, podemos fazer uma consulta mais sofisticada a partir dele ou, como fizemos aqui, alterá-lo.

EFunction tem muito mais potência do que isso, então verifiquethe official manual, especialmentethe getResultSender method.

7.2. Função de implantação

Precisamos conscientizar o Geode de nossa função para poder executá-lo. Como fizemos com nossos tipos de dados personalizados, empacotaremos o jar.

Mas desta vez, podemos apenas usar o comandodeploy:

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

7.3. Função de execução

Agora, podemos executar oFunction do aplicativo usando oFunctionService:

@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. Conclusão

Neste artigo, aprendemos os conceitos básicos do ecossistemaApache Geode. Analisamos simples get e puts com tipos padrão e personalizados, regiões replicadas e particionadas e oql e suporte a funções.

E como sempre, todas essas amostras estão disponíveisover on GitHub.