definição e implementação de uma solução distribuída de ... · resumo hoje em dia, cada vez...

93
Setembro de 2007 Definição e implementação de uma solução distribuída de cache de dados com estrutura hierárquica Bruno Miguel Ferreira Veigas Dissertação para obtenção do Grau de Mestre em Engenharia Electrotécnica e de Computadores Júri Presidente: Prof. Cjgldadlkh Njgjkjala Bklalsjdla Ylkhjçasdj (12pt normal) Orientador: Prof. Doutor João Paulo Baptista de Carvalho Vogais: Doutora Bjlçsajasdlçl Mjlajçlaffljsd Khsaçlsahfd (12pt normal) Prof. Mçlassjfçldjsal Nçlaçlafjl Uçlçjljçfçjld (12pt normal) Prof. Mçlassjfçldjsal Nçlaçlafjl Uçlçjljçfçjld (12pt normal)

Upload: others

Post on 03-Oct-2020

0 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Setembro de 2007

Definição e implementação de uma solução distribuíd a de

cache de dados com estrutura hierárquica

Bruno Miguel Ferreira Veigas

Dissertação para obtenção do Grau de Mestre em

Engenharia Electrotécnica e de Computadores

Júri

Presidente: Prof. Cjgldadlkh Njgjkjala Bklalsjdla Ylkhjçasdj (12pt normal)

Orientador: Prof. Doutor João Paulo Baptista de Carvalho

Vogais: Doutora Bjlçsajasdlçl Mjlajçlaffljsd Khsaçlsahfd (12pt normal)

Prof. Mçlassjfçldjsal Nçlaçlafjl Uçlçjljçfçjld (12pt normal)

Prof. Mçlassjfçldjsal Nçlaçlafjl Uçlçjljçfçjld (12pt normal)

Page 2: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção
Page 3: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Agradecimentos

A realização deste projecto não teria sido possível sem a ajuda e o apoio de algumas

pessoas, às quais eu gostaria de agradecer. Em primeiro lugar, ao meu orientador, professor

Doutor João Carvalho, por ter proposto este trabalho e pela orientação que me deu.

À empresa WeDo Consulting, que financiou este projecto e reuniu as condições necessá-

rias para a sua realização. Um agradecimento especial ao Nuno Homem e ao João Lopes,

pelo acompanhamento do desenrolar do projecto e pelas sugestões.

Aos meus colegas de trabalho ao longo do curso, sem os quais estes anos de trabalho

árduo teriam sido ainda mais complicados. Um agradecimento especial ao Roberto Cabrita,

pelas dezenas de centenas de horas de trabalho conjunto.

Gostaria de agradecer aos meus amigos e à minha família, pelo seu total apoio para a

realização deste grande projecto de 5 anos, que termina com esta dissertação de mestrado.

Um agradecimento muito especial à minha namorada, pelo apoio incondicional que me deu,

para eu puder atingir este meu grande objectivo.

Por fim, relembrar algumas das pessoas que já partiram e que eu tenho a certeza que me

teriam dado todo o seu apoio: António Cozinheiro, Gonçalo Azoia e João Veigas.

i

Page 4: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção
Page 5: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Resumo

Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A

compra e manutenção de grandes servidores e super computadores é muito dispendiosa.

A criação de grids de computadores, com hardware com boa relação preço/desempenho

e facilmente substituível, parece ser a solução a adoptar. Para permitir que a computação

seja feita de forma distribuída, são necessários mecanismos que garantam a consistência da

informação partilhada. É neste contexto que surge o conceito de cache distribuída.

Pretende-se com este trabalho definir e implementar uma solução distribuída de cache de

dados com estrutura hierárquica. A cache é um dos componentes de um servidor aplicacional.

Pretende-se escalar o sistema para vários servidores aplicacionais. O sistema deverá ser

capaz de permitir a vários servidores aplicacionais partilharem um conjunto de dados em

modo de leitura e de escrita, assegurando a consistência da informação partilhada. Algumas

das operações deverão ser efectuadas de forma síncrona para garantir a robustez da solução

a falhas.

Começa-se por apresentar uma solução simples para o problema, que admite que um dos

servidores aplicacionais não pode falhar. Progressivamente, vão sendo introduzidas melho-

rias na solução, com vista à correcção da restrição anterior e obtenção de um melhor de-

sempenho do sistema distribuído. Faz-se a integração com a cache centralizada previamente

existente e efectuam-se testes para verificar o correcto funcionamento da solução proposta.

Palavras Chave

Cache distribuída

Dados com estrutura hierárquica

Tolerância a falhas

Sistema distribuído

Comunicação por grupos

Eleição estável

iii

Page 6: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção
Page 7: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Abstract

Now a days, more and more, distributed systems are an option to consider, as the purchase

and maintenance of big servers and super computers is very expensive. The creation of

computer grids, using hardware with a good relation price/performance and easily replaceable,

seems to be the best option available. To allow the computation to be made in a distributed

manner, mechanisms to guarantee the consistency of the shared information are needed. In

this context appears the concept of distributed cache.

This work aims the definition and the implementation of a distributed solution of cache with

hierarchical data structure. The cache is one of the application server components. It is inten-

ded to scale the system to some application servers. The system must allow the share of data

between several application servers, in both read and write modes, assuring the consistency

of the shared information. Some of the operations need to be performed in a synchronous way

to guarantee the robustness of the solution to failures.

This work starts with the presentation of a simple solution of the problem, admitting that one

of the application servers cannot fail. Gradually, improvements are introduced toward the final

solution, in order to overcome the previous restriction and to achieve a better performance of

the distributed system. The solution is then integrated with the previously existing centralized

cache and tests are performed to verify the correct functioning of the solution proposed.

Keywords

Distributed cache

Hierarchical data structure

Fault tolerance

Distributed system

Group communication

Stable election

v

Page 8: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção
Page 9: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Conteúdo

Agradecimentos i

Resumo iii

Abstract v

Conteúdo vii

Lista de Figuras xi

Lista de Tabelas xiii

Lista de Siglas xv

I Contextualização e tecnologia utilizada

1 Introdução 1

1.1 Descrição do problema e objectivos . . . . . . . . . . . . . . . . . . . . . . . . . 1

1.2 Discrição genérica de uma cache . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1.3 Sistemas distribuídos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1.3.1 O que é um sistema distribuído? . . . . . . . . . . . . . . . . . . . . . . . 2

1.3.2 Características de um sistema distribuído . . . . . . . . . . . . . . . . . . 2

1.4 Contribuições deste trabalho . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

1.5 Convenções de redação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

1.6 Estrutura do texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2 Enquadramento 5

2.1 Soluções de cache distribuída . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2.1.1 JBoss Cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2.1.2 EHCache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.1.3 Tangosol Coherence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.1.4 SwarmCache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.1.5 OSCache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.1.6 FKache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.1.7 The Bamboo DHT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.1.8 ShiftOne Java Object Cache . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.2 Publicações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

vii

Page 10: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

3 Objectos distribuídos 113.1 Objectos distribuídos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3.2 Ligação de um cliente a um objecto (bind) . . . . . . . . . . . . . . . . . . . . . . 12

3.3 Passagem de parâmetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3.4 Objectos distribuídos em Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3.4.1 O modelo de objectos distribuídos em Java . . . . . . . . . . . . . . . . . 13

3.4.2 Invocação de objectos remotos em Java . . . . . . . . . . . . . . . . . . . 14

3.4.3 O registo RMI (rmiregistry ) . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.4.4 Implementação de objectos remotos . . . . . . . . . . . . . . . . . . . . . 15

3.4.5 Limpeza de objectos distribuídos (Garbage Collecting) . . . . . . . . . . . 16

4 Comunicação fiável em multicast 174.1 Comunicação fiável em multicast básica . . . . . . . . . . . . . . . . . . . . . . . 17

4.2 Escalabilidade em comunicação fiável em multicast . . . . . . . . . . . . . . . . 18

4.2.1 Controlo de feedback não hierárquico . . . . . . . . . . . . . . . . . . . . 19

4.2.2 Controlo de feedback hierárquico . . . . . . . . . . . . . . . . . . . . . . . 20

4.3 Multicast Atómico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

4.3.1 Sincronismo Virtual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

4.4 The Jgroup Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

4.4.1 Composição do Jgroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

4.4.2 O serviço de registo do Jgroup . . . . . . . . . . . . . . . . . . . . . . . . 27

4.4.3 Configuração do sistema distribuído . . . . . . . . . . . . . . . . . . . . . 28

II Desenvolvimento de uma cache de dados distribuída

5 Serviço de Cache centralizado 315.1 Estrutura hierárquica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

5.2 Arquitectura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

5.2.1 Camada de apresentação . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

5.2.2 Camada de partilha de dados . . . . . . . . . . . . . . . . . . . . . . . . . 35

5.2.3 Camada de leitura e escrita . . . . . . . . . . . . . . . . . . . . . . . . . . 36

5.3 Políticas de substituição . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

6 Solução inicial 396.1 Arquitectura da solução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

6.2 Obtenção e registo de referências . . . . . . . . . . . . . . . . . . . . . . . . . . 40

6.3 Contagem de clientes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

6.3.1 Incremento do número de clientes . . . . . . . . . . . . . . . . . . . . . . 42

6.3.2 Decremento do número de clientes . . . . . . . . . . . . . . . . . . . . . 43

6.4 Estrutura hierárquica dos dados em cache . . . . . . . . . . . . . . . . . . . . . 45

6.5 Tolerância a falhas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

6.5.1 Java RMI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

6.5.2 Pontos de falha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

6.5.3 Recuperação da ocorrência de falhas . . . . . . . . . . . . . . . . . . . . 47

6.6 Sincronização e performance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

6.7 Integração com a cache local . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

viii

Page 11: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

6.8 Estatísticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

6.9 Testes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

6.10 Escalabilidade da solução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

7 Melhoramentos na solução inicial 537.1 Arquitectura da solução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

7.2 Eleição . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

7.3 Replicação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

7.4 Tolerância a falhas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

7.4.1 Falha do coordenador após a invocação de um método por parte de uma

cache cliente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

7.4.2 Falhas relacionadas com o algoritmo de eleição . . . . . . . . . . . . . . 58

7.4.3 Falha durante a sincronização com o coordenador . . . . . . . . . . . . . 58

7.5 Testes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

8 Conclusões e trabalhos futuros 618.1 Conclusões . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

8.2 Trabalhos futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

Apêndice

Bibliografia 65

A Interacções entre o serviço de cache e os restantes serviços do servidor aplica-cional 69

B Diagramas UML da solução melhorada 71

ix

Page 12: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção
Page 13: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Lista de Figuras

3.1 Invocação de um método num objecto remoto. . . . . . . . . . . . . . . . . . . . 11

3.2 Passagem de objectos por referência e por valor. . . . . . . . . . . . . . . . . . . 13

4.1 Uma solução simples para comunicação fiável em multicast, onde são conhe-

cidos todos os receptores e se assume que não há falhas em nenhum dos

processos. (a) Transmissão da mensagem. (b) Feedback. . . . . . . . . . . . . . 18

4.2 Vários receptores agendaram um pedido de retransmissão, mas o primeiro pe-

dido de retransmissão leva à supressão dos outros. . . . . . . . . . . . . . . . . 19

4.3 Vista geral de uma solução hierárquica de comunicação fiável em multicast.

Cada coordenador reencaminha a mensagem para os seus filhos e atende pe-

didos de retransmissão. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

4.4 Organização lógica de um sistema distribuído, para distinguir a recepção da

entrega de mensagens. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

4.5 O princípio de funcionamento do sincronismo virtual em multicast. . . . . . . . . 23

4.6 Exemplo do preenchimento do ficheiro config.xml. . . . . . . . . . . . . . . . . . 28

5.1 Contexto de execução básico. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

5.2 Relações entre entidades. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

5.3 Arquitectura do serviço de cache. . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

5.4 Organização do serviço de cache em camadas. . . . . . . . . . . . . . . . . . . 34

5.5 Exemplo de contexto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

5.6 Interacção entre as camadas de apresentação e partilha de dados. . . . . . . . 36

6.1 Arquitectura em camadas do serviço de cache distribuído. . . . . . . . . . . . . . 40

6.2 Arquitectura da camada de cache distribuída. . . . . . . . . . . . . . . . . . . . . 40

6.3 Obtenção e registo de referências. . . . . . . . . . . . . . . . . . . . . . . . . . . 41

6.4 Contagem de clientes - incremento. . . . . . . . . . . . . . . . . . . . . . . . . . 43

6.5 Contagem de clientes - decremento. . . . . . . . . . . . . . . . . . . . . . . . . . 44

6.6 Falha na invocação de um método remoto e respectiva recuperação. . . . . . . . 48

6.7 Crash de um cliente e respectiva recuperação. . . . . . . . . . . . . . . . . . . . 49

6.8 Falha antes do registo de um objecto. . . . . . . . . . . . . . . . . . . . . . . . . 50

7.1 Arquitectura da camada de cache distribuída melhorada. . . . . . . . . . . . . . 53

7.2 Algoritmo de eleição. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

A.1 Arquitectura do serviço de cache incluindo interacções com restantes serviços. . 70

xi

Page 14: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

B.1 Diagrama de classes - Parte 1/2. . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

B.2 Diagrama de classes - Parte 2/2. . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

xii

Page 15: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Lista de Tabelas

5.1 Esquema da tabela de relações. . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

5.2 Preenchimento da tabela de relações para o exemplo da Figura 5.1. . . . . . . . 33

xiii

Page 16: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção
Page 17: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Lista de Siglas

ACK - ACKnowledgement (feedback positivo)

AOP - Aspect-Oriented Programming

API - Application Programming Interface

BSD - Berkeley Software Distribution

DHT - Distributed Hash Table

DRS - Dependable Registry Service

EGMI - External GMI

GMI - Group Method Invocation

GMIS - GMI Service

GMS - Group Membership Service

IGMI - Internal GMI

JDK - Java Development Kit

JMS - Java Message Service

JSR - Java Specification Request

JSP - JavaServer Pages

JVM - Java Virtual Machine

NACK - Negative ACK (feedback negativo)

RMI - Remote Method Invocation

POJO - Plain Old Java Object

SMS - State Merging Service

SRM - Scalable Reliable Multicast

UML - Unified Modeling Language

xv

Page 18: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção
Page 19: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Parte I

Contextualização e tecnologiautilizada

Page 20: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção
Page 21: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Capítulo 1

Introdução

Este trabalho é de carácter multidisciplinar. Visto que existem limitações em relação ao

número máximo de páginas, não é possível descrever detalhadamente todos os conceitos

utilizados. Assim, admite-se que o leitor tem conhecimentos de programação de sistemas,

linguagem de programação Java e bases de dados. Como referências pode consultar-se [36],

[2] e [32], respectivamente.

1.1 Descrição do problema e objectivos

Pretende-se com este trabalho definir e implementar uma solução distribuída de cache de

dados com estrutura hierárquica, partindo de uma solução centralizada já existente.

A cache é um dos componentes de um servidor aplicacional. Pretende-se escalar o sis-

tema para vários servidores aplicacionais. O sistema deverá ser capaz de permitir a vários

servidores aplicacionais partilharem um conjunto de dados de execução de processos de

negócio, em modo de leitura e de escrita, e assegurar a consistência desta informação. Al-

gumas das operações deverão ser efectuadas de forma síncrona para garantir a persistência

dos dados e a robustez da solução a falhas.

A solução para o problema deve alterar a cache existente apenas nos locais estritamente

necessários. Pretende-se que as alterações sejam mínimas para manter a compatibilidade

com os diversos serviços com os quais a cache interage (Anexo A), de forma a diminuir o risco

de ocorrência de problemas, e os testes necessários para verificar o correcto funcionamento

da solução.

A solução deve ser totalmente implementada em Java.

1.2 Discrição genérica de uma cache

Uma cache é memória de acesso rápido, usada para guardar um subconjunto de um

conjunto maior de dados, cuja obtenção é lenta ou "cara"do ponto de vista computacional.

Geralmente a capacidade da cache é reduzida, não permitindo armazenar todos os da-

dos existentes. Assim é necessária a existência de políticas de substituição, que possam

colocar em cache novos dados quando esta estiver totalmente preenchida. As políticas de

substituição são uma tentativa de optimização da utilização da cache [28].

1

Page 22: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Uma vez que a eficiência da cache depende da hit rate (número de pedidos que são satis-

feitos recorrendo apenas aos dados existentes em cache, face a pedidos que necessitam de

ser computados ou se encontram armazenados em tipos de memória mais lentos), a política

de substituição tenta prever o padrão de pedidos futuros. Desta forma, a escolha de uma

política de substituição adequada é uma das principais preocupações no desenho de uma

cache.

Outra decisão que tem de ser tomada no desenho de uma cache é a estratégia de escrita

a adoptar. As alterações efectuadas nos dados em cache têm de ser propagadas para os

dados originais. Numa estratégia write-through cada operação de escrita em cache desen-

cadeia uma escrita síncrona nos dados originais. Alternativamente, pode ser utilizada uma

estratégia write-back, na qual as alterações têm efeito imediato apenas nos dados em cache.

Na estratégia write-back, as alterações só têm efeito nos dados originais quando os dados

são removidos da cache, ou quando tal é especificado directamente pela aplicação.

Uma entrada da cache é um elemento guardado na cache. Este elemento tem de conter

a informação a guardar, assim como atributos necessários para a execução da política de

substituição, tais como a idade e o número de referências.

1.3 Sistemas distribuídos

Esta secção apresenta noções básicas de sistemas distribuídos. Estas noções permitirão

ajudar na compreensão da tecnologia utilizada e das soluções apresentadas.

1.3.1 O que é um sistema distribuído?

Existem diversas definições de sistemas distribuídos. Segundo [37], "‘um sistema distri-

buído é um conjunto de computadores independentes que para os utilizadores aparentam ser

um único sistema coerente"’. Esta definição refere quer o hardware (máquinas autónomas)

quer o software (interacção de utilizadores com o sistema).

1.3.2 Características de um sistema distribuído

As principais características de um sistema distribuído são: transparência, consistência,

escalabilidade e abertura.

Transparência As diferenças entre os vários computadores e a forma como eles comuni-

cam entre si deve estar escondida dos utilizadores, assim como o facto dos processos e os

recursos se encontrarem fisicamente distribuídos por múltiplos computadores. Existem dife-

rentes formas de transparência, tais como: acesso, localização, falha, etc.

Consistência É importante que utilizadores e aplicações possam interagir com o sistema

distribuído de forma uniforme e consistente, independentemente de quando e onde é que a

interacção ocorre.

2

Page 23: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Escalabilidade Deve ser relativamente fácil expandir ou escalar um sistema distribuído.

Esta característica está directamente relacionada com a existência de computadores inde-

pendentes. Deve ser possível a expansão do sistema quer em número de utilizadores, quer

em termos de recursos disponíveis, e aumentar a distância entre utilizadores e recursos.

Abertura Um sistema distribuído aberto é um sistema que oferece serviços segundo regras

padrão que descrevem a sintaxe e a semântica desses serviços. Para permitir flexibilidade um

sistema distribuído aberto deve estar organizado em pequenos componentes, com interfaces

bem definidas, e facilmente substituíveis.

1.4 Contribuições deste trabalho

Apesar de existirem diversas bibliotecas e produtos que permitem a implementação de

caches distribuídas (Secção 2.1), a informação relativa aos detalhes de funcionamento dessas

soluções é praticamente inexistente.

Poderia ter-se optado pela utilização de uma dessas soluções. No entanto, visto que este

trabalho foi desenvolvido em colaboração com uma empresa, optou-se pelo desenvolvimento

de uma solução à medida. Este tipo de solução permite maior facilidade de integração com

a cache centralizada já existente e um total controlo do seu funcionamento. Além disso,

as soluções já existentes geralmente têm associadas funcionalidades adicionais, que são

desnecessárias para a resolução deste problema específico.

Uma vez que os detalhes de funcionamento e implementação das diversas soluções en-

contradas são escassos, a publicação deste trabalho é uma contribuição para a definição e

implementação de uma cache distribuída.

1.5 Convenções de redação

Ao longo do texto serão efectuados diversos tipos de referência. Referências bibliográ-

ficas serão indicadas entre parêntesis rectos, por exemplo [17]. Referências a capítulos ou

secções serão indicadas na forma captítulo.secção.subsecção, prefixadas pelo identificador

correspondente, por exemplo Secção 4.2, corresponde à secção 2 do capítulo 4. Referências

a figuras e tabelas serão indicadas na forma capítulo.figura/tabela, prefixadas pelo identifica-

dor correspondente, por exemplo Tabela 6.3, corresponde à terceira tabela do capítulo 6.

Termos em inglês serão indicados em itálico, assim como referências a objectos apresen-

tados nos exemplos.

Nomenclatura relativa a código fonte é indicada na forma codigoFonte.

1.6 Estrutura do texto

Esta dissertação está dividida essencialmente em duas grandes partes. A primeira parte

apresenta o problema, contextualiza-o e descreve a tecnologia utilizada. A segunda parte

apresenta o ponto de partida, bem como o trabalho desenvolvido no âmbito da dissertação.

3

Page 24: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

O Capítulo 2 aborda algumas publicações relevantes na área de interesse deste trabalho.

Apresenta ainda algumas bibliotecas e produtos existentes, com funcionalidades de cache

distribuída.

Os Capítulos 3 e 4 explicam o funcionamento da tecnologia utilizada no desenvolvimento

deste trabalho. O Capítulo 3 explica de forma geral o conceito de objecto distribuído, ilustra

o seu funcionamento e particulariza para o caso do modelo de objectos distribuídos em Java.

O Capítulo 4 introduz os mecanismos de comunicação fiável em multicast. Explicitam-se os

problemas associados a este tipo de comunicação e explica-se de forma geral a comunicação

por grupos. Particulariza-se a comunicação por grupos para o caso da biblioteca Jgroup.

A segunda parte da dissertação inicia-se com o Capítulo 5. Neste capítulo apresenta-se a

solução centralizada previamente existente, que serviu de ponto de partida para o desenvol-

vimento do trabalho.

O Capítulo 6 descreve a arquitectura e o funcionamento de uma solução distribuída de

cache de dados com estrutura hierárquica. Descreve-se ainda a integração com a cache

centralizada inicial e os testes efectuados.

O Capítulo 7 introduz melhorias na solução inicial, permitindo corrigir os problemas exis-

tentes.

O Capítulo 8 apresenta as principais conclusões que se podem tirar desta dissertação e

menciona possíveis melhoramentos a efectuar na solução apresentada.

Por fim, apresentam-se alguns apêndices que complementam o texto principal do traba-

lho.

4

Page 25: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Capítulo 2

Enquadramento

Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A

compra e manutenção de grandes servidores e super computadores é muito dispendiosa.

A criação de grids de computadores, com hardware com boa relação preço/desempenho

e facilmente substituível, parece ser a solução a adoptar. Esta é a política adoptada pela

Google, que segundo um artigo do The New York Times de Junho de 2006 [20], possuía mais

de 450 000 computadores, dispersos por 25 locais do globo.

Para permitir que a computação seja feita de forma distribuída, são necessários mecanis-

mos que garantam a consistência da informação partilhada. É neste contexto que surgem os

conceitos de cache distribuída e memória distribuída partilhada.

Existe uma tentativa de uniformização de uma API para soluções de caching escritas

em Java, JSR (Java Specification Request) 107 [16]. Caso várias soluções optem por imple-

mentar esta API, será possível proceder à sua substituição facilmente. Algumas das soluções

que implementam a API: Tangosol, EHCache, FKache, etc.

Nas próximas secções apresentam-se diversas soluções de cache distribuída encontra-

das e algumas publicações referentes à temática desta dissertação.

2.1 Soluções de cache distribuída

2.1.1 JBoss Cache

A biblioteca JBoss Cache [17] permite colocar em cache os objectos Java utilizados mais

frequentemente, para melhorar o desempenho das aplicações. Eliminando acessos desne-

cessários à base de dados é possível diminuir o tráfego da rede e aumentar a escalabilidade

das aplicações.

Fornece funcionalidades transaccionais, assim como um grande conjunto de configura-

ções para lidar com acessos concorrentes aos dados, da forma mais eficiente possível para

a aplicação em questão. Adicionalmente, replica o conteúdo para outras instâncias de ca-

che, em execução em máquinas virtuais Java (JVMs) ou servidores distintos, permitindo a

implementação de funcionalidades de clustering.

5

Page 26: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Existem duas APIs distintas, permitindo a utilização daquela que melhor se adequar às

necessidades. Uma das APIs oferece uma estrutura hierárquica em árvore, baseada em

nós, enquanto a outra (POJO Cache) oferece mecanismos de replicação mais granulares,

permitindo obter um melhor desempenho.

A POJO (Plain Old Java Object) Cache [41] permite preservar as relações entre objectos

durante a replicação ou persistência (com recurso a uma base de dados). A replicação é feita

de forma transparente com recurso ao paradigma AOP (Aspect-Oriented Programming). Este

paradigma [43] tenta ajudar na definição da arquitectura da aplicação, permitindo uma melhor

divisão das funcionalidades, obtendo uma arquitectura mais modular. Todas as actualizações

de um POJO são interceptadas, sendo propagadas para as diversas réplicas de forma auto-

mática. Devido à utilização do paradigma AOP, é necessário efectuar uma “pré-compilação”

da aplicação com o aopc (compilador AOP), disponibilizado juntamente com a biblioteca.

A biblioteca está disponível sobre licença LGPL [13].

2.1.2 EHCache

EHCache [39] é uma biblioteca em Java com funcionalidades de cache distribuída, que

implementa a API JSR107.

As suas principais características são a rapidez, leveza, escalabilidade, extensibilidade,

possibilidade de persistência dos dados, possibilidade de utilização com Java EE, alta quali-

dade de implementação e a possibilidade de utilização de forma distribuída.

Relativamente à sua característica distribuída as funcionalidades oferecidas são: desco-

berta de pares, entrega fiável, replicação síncrona ou assíncrona, cópia ou invalidação de

réplicas, replicação transparente e extensibilidade.

Esta biblioteca está disponível sobre licença Apache [11].

2.1.3 Tangosol Coherence

Tangosol Coherence [33] é uma solução comercial que permite a gestão de dados em

memória para aplicações J2EE distribuídas e servidores aplicacionais.

Coherence torna a partilha e gestão de dados, entre vários servidores, tão simples como

se apenas de um servidor se tratasse. Isto é possível devido à utilização de protocolos de

replicação e propagação de alterações.

Coherence implementa a API JSR107 e fornece gestão de dados replicados e distribuídos,

sobre um protocolo fiável e escalável de comunicação entre pares (peer-to-peer ). Não intro-

duz nenhum ponto de falha. Efectua a recuperação de forma totalmente transparente no caso

de ocorrência falhas, redistribuindo os dados quando um servidor deixa de estar operacional.

Quando um novo servidor se junta, ou quando um servidor que falhou recupera, a carga é

redistribuída de forma transparente.

2.1.4 SwarmCache

SwarmCache [42] é uma solução simples mas eficaz de cache distribuída, em Java. Utiliza

comunicação IP multicast para comunicar com um qualquer número de anfitriões numa rede

local.

6

Page 27: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Foi especialmente desenhado para uso em aplicações clustered, em que o número de

operações de leitura é muito superior ao número de operações de escrita, permitindo assim

um melhor desempenho.

Internamente a SwarmCache utiliza uma biblioteca de comunicação por grupos (JGroups).

Foram desenvolvidos wrappers que permitem a utilização da SwarmCache com motores per-

sistentes, nomeadamente Hibernate e JPOX.

Aparentemente o desenvolvimento da SwarmCache foi suspenso, tendo sido a última ver-

são (1.0 RC2) lançada em Outubro de 2003. A biblioteca está disponível sobre licença LGPL

[13].

2.1.5 OSCache

OSCache [24] é uma solução de cache para conteúdo de JSP (JavaServer Pages), res-

postas de servlets Java, ou objectos Java arbitrários, totalmente implementada em Java.

Fornece soluções de cache em memória e persistência em disco, tendo sido desenhada

com vista a atingir um bom desempenho. Suporta aplicações distribuídas, fornecendo esca-

labilidade e tolerância a falhas, sem ser necessário fazer alterações no código que interage

com a cache.

A solução está disponível sobre uma licença derivada da licença Apache [11].

2.1.6 FKache

FKache [18] é uma solução de cache distribuída, totalmente implementada em Java, que

implementa parcialmente a API JSR 107.

Relativamente às funcionalidades distribuídas, tudo é feito de forma transparente. Quando

uma cache inicia junta-se automaticamente a outras caches existentes na rede. Todas as

actualizações/invalidações são automaticamente propagadas para todas as caches da rede.

Caso um objecto não resida na cache local é feito um pedido a outras caches.

Esta implementação pretendia ter melhor desempenho do que todas as outras soluções

de código aberto e comercial, mas aparentemente o seu desenvolvimento foi suspenso, tendo

sido a última versão (1.0 beta6) lançada em Fevereiro de 2005. A biblioteca está disponível

sobre licença LGPL [13].

2.1.7 The Bamboo DHT

Uma forma de criar uma cache distribuída é recorrer a uma tabela de dispersão distri-

buída (DHT - Distributed Hash Table). Este tipo de tabelas faz um mapeamento chave-valor e

disponibiliza operações de put, get e remove.

The Bamboo DHT [27] é uma das várias implementações de DHT existentes. Nesta solu-

ção os vários pares chave-valor encontram-se dispersos pelas diversas caches e são replica-

dos à medida que as caches necessitam de aceder a outros objectos já existentes em caches

distintas.

Visto que existem alguns problemas de segurança, os autores aconselham a que este

software seja utilizado apenas para investigação e não para ambientes de produção. Este

software encontra-se licenciado sobre licença BSD [25].

7

Page 28: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

2.1.8 ShiftOne Java Object Cache

ShiftOne Java Object Cache [31] é uma biblioteca Java que implementa várias políticas

estritas de caching de objectos, tais como tamanho máximo e idade máxima, apresentando

uma framework leve de configuração.

As funcionalidades distribuídas são obtidas com recurso a bibliotecas externas (JGroups

ou JMS).

A biblioteca está disponível sobre licença LGPL [13].

2.2 Publicações

Apesar de existirem diversas implementações de caches distribuídas, as publicações re-

lativas ao seu desenvolvimento são praticamente inexistentes. Uma das bibliotecas encontra-

das que tem publicações associadas é a Cali [47].

Cali é uma framework desenvolvida em C++ para sistemas de cache local e sistemas

distribuídos. O artigo em questão, descreve as estruturas de dados e as políticas de ges-

tão. Relativamente às funcionalidades distribuídas, esta biblioteca apenas fornece primitivas

send/receive, necessitando de recorrer a pacotes externos para a implementação da troca de

mensagens. Também não implementa mecanismos de localização de objectos.

A inserção de um objecto na cache distribuída é feita com recurso a uma função de dis-

persão, ou seja, em função do hash code obtido, o objecto é atribuído a um determinado nó

do sistema distribuído. Os acessos aos objectos são efectuados remotamente, não existindo

migrações de objectos.

O artigo não refere a existência de mecanismos de controlo de clientes de objectos remo-

tos, nem de tolerância a falhas. O facto de um objecto ser atribuído a um nó em função do

hash code também não parece ser a melhor opção a tomar, pois esse nó pode nem sequer

utilizar o objecto em questão, enquanto que o nó que fez a inserção no sistema distribuído

terá de efectuar todos os acessos de forma remota.

Colocando de lado as implementações, [6] descreve um serviço de cache genérico para

grids de computadores. A ideia é ter um serviço de cache que possa ser utilizado simultânea-

mente por várias aplicações, de forma a melhorar a eficiência do sistema. As descrições são

feitas apenas ao nível da arquitectura.

Cache distribuída e replicação são técnicas que podem ser utilizadas de forma inde-

pendente, no entanto grande parte das vezes é conveniente combiná-las [40]. A informação

relativa a esta temática encontra-se dispersa por várias áreas. Por exemplo, os modelos

de consistência são estudados no contexto de memória distribuída partilhada, enquanto que

questões relacionadas com como, onde e quando guardar dados remotamente, é investigada

com maior detalhe em sistemas de informação distribuídos, como é o caso da World Wide

Web [26, 29, 38].

Em [34] comparam-se quatro algoritmos básicos para a implementação de memória

distribuída partilhada, concluindo-se que o desempenho dos algoritmos é sensível ao com-

portamento das aplicações.

8

Page 29: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

O algoritmo “servidor central” é o mais simples de todos, no qual existe um servidor res-

ponsável pelo acesso a todos os dados partilhados, que mantém a única cópia dos dados.

Operações de leitura e de escrita desencadeiam um pedido ao servidor central, que responde

com os dados ou com um ACK, quer se trate uma operação de leitura ou de escrita, respecti-

vamente.

No algoritmo de “migração” os dados são sempre migrados para o sítio onde são acedidos,

ou seja, trata-se de um protocolo “único leitor/único escritor”.

No algoritmo de “replicação para leitura” é passada uma réplica do objecto que se pretende

aceder. Operações de leitura passam a ser feitas localmente, enquanto que operações de

escrita têm de invalidar ou actualizar as restantes réplicas, para manter a consistência da

informação. Este algoritmo já permite “múltiplos leitores/único escritor”.

Por fim, o algoritmo de “replicação total” que permite “múltiplos leitores/múltiplos escrito-

res”. Neste caso é um pouco mais complicado garantir a consistência da informação parti-

lhada, pois a operação de escrita tem de ser efectuada de forma atómica em todas as répli-

cas.

9

Page 30: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção
Page 31: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Capítulo 3

Objectos distribuídos

O paradigma orientado a objectos é actualmente bastante utilizado. Uma dos seus princi-

pais aspectos prende-se com o facto de ser possível definir uma interface pública e esconder

a implementação atrás dessa interface. Desta forma, os objectos podem ser facilmente subs-

tituíveis ou adaptáveis, desde que se respeite a interface previamente definida.

3.1 Objectos distribuídos

Um objecto encapsula dados (estado) e as operações para manipular esses mesmos da-

dos (métodos). Os métodos são disponibilizados através de interfaces. Não há forma de

manipular o estado de um objecto sem ser através da invocação de métodos definidos nas

interfaces. Um objecto pode implementar várias interfaces e uma interface pode ser dis-

ponibilizada por diversos objectos. A separação entre as interfaces e os objectos que as

implementam é fundamental para os sistemas distribuídos.

A Figura 3.1 ilustra a invocação de um método remoto.

Figura 3.1: Invocação de um método num objecto remoto.

11

Page 32: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Na altura em que o cliente pretende ligar-se (bind) ao objecto remoto, é carregada para o

espaço de memória do cliente uma implementação da interface do objecto, chamada proxy.

O trabalho da proxy é empacotar a invocação de métodos em mensagens e desempacotar as

respostas, devolvendo o resultado ao cliente.

O objecto propriamente dito, reside numa máquina remota (servidor) e oferece uma in-

terface igual àquela a que o cliente tem acesso. As mensagens recebidas são passadas

para o skeleton, que as desempacota e invoca o método correspondente na interface do ob-

jecto, alojado no servidor. O skeleton é também responsável por empacotar a resposta e

reencaminhá-la para a proxy.

Um objecto cujo estado reside apenas numa máquina, designa-se por objecto remoto.

3.2 Ligação de um cliente a um objecto (bind)

Geralmente um sistema que suporta objectos distribuídos tem mecanismos de suporte

a referências globais de objectos, válidas em todo o sistema distribuído. Essas referências

podem ser passadas entre processos a executar em diferentes máquinas.

Após a obtenção de uma referência para um objecto é necessário efectuar uma ligação ao

objecto referenciado, antes de puder invocar métodos. Essa ligação traduz-se na colocação

de uma proxy no espaço de memória do cliente, tal como referido na Secção 3.1.

A ligação pode ser feita de forma explícita ou implícita. Na ligação explícita o cliente tem de

invocar uma função especial para fazer a ligação ao objecto, podendo depois disso invocar os

métodos pretendidos. Na ligação implícita podem invocar-se os métodos pretendidos, sendo

a ligação estabelecida de forma transparente.

3.3 Passagem de parâmetros

As referências para objectos podem ser passadas como parâmetros na invocação de mé-

todos. As referências são passadas por valor, o que neste caso corresponde à sua cópia de

de uma máquina para a outra. Após receber uma referência, um processo apenas neces-

sita de fazer a ligação ao objecto no momento em que precisar de invocar um método nesse

mesmo objecto.

Caso todos as operações fossem efectuadas sobre referências, o desempenho do sistema

poderia ser muito baixo, principalmente no caso de objectos de pequena dimensão, como in-

teiros ou booleanos. Cada invocação por parte de um cliente que não estivesse no mesmo

espaço de endereçamento do servidor, iria gerar um pedido entre diferentes espaços de en-

dereçamento, ou pior ainda, entre diferentes máquinas. Por esse motivo, por vezes é feita

distinção no tratamento de objectos locais e objectos remotos.

Ao invocar um método em que é passada a referência de um objecto como parâmetro,

essa referência é copiada e passada por valor, caso se trate de uma referência para um

objecto remoto. Neste caso, o objecto é passado literalmente por referência. Caso a referência

seja para um objecto local, é passada uma cópia do objecto, ou seja, o objecto é passado por

valor.

Estas situações estão ilustradas na Figura 3.2. Existe um servidor em execução na má-

quina C e um cliente na máquina A. O cliente tem referências para um objecto local O1 e

12

Page 33: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

um objecto remoto O2, que passa como parâmetros na invocação de um método remoto. Ao

invocar o método remoto o cliente passa uma cópia de O1 e uma cópia da referência para O2.

Copiar um objecto por completo pode não ser desejável. Assim sendo é forçoso fazer

uma distinção entre objectos locais e objectos remotos. Esta distinção viola o princípio da

transparência e torna mais complicada a implementação de aplicações distribuídas.

Figura 3.2: Passagem de objectos por referência e por valor.

3.4 Objectos distribuídos em Java

A invocação de métodos remotos, Java Remote Method Invocation (RMI), faz parte da pla-

taforma Java e é um modelo para objectos distribuídos. Este modelo encontra-se integrado

na linguagem e pretende manter a semântica da invocação a métodos locais, facilitando a

implementação e utilização de objectos remotos. Desta forma é possível manter um grau ele-

vado de transparência. Porém, no desenvolvimento do Java resolveu-se tornar a distribuição

aparente nos casos em que a transparência era ineficiente, difícil, ou impossível de realizar.

3.4.1 O modelo de objectos distribuídos em Java

O Java adopta os objectos remotos como a única forma de objectos distribuídos. As

interfaces são implementadas por intermédio de uma proxy, tal como descrito na Secção 3.1.

Uma proxy aparenta ser um objecto local no espaço de endereçamento do cliente.

Existem algumas diferenças entre objectos locais e objectos remotos em Java, mas uma

das mais importantes diz respeito ao acesso em exclusivo a determinada região (monitor).

Em Java é possível declarar um método associado a um monitor, com recurso à directiva

synchronized. No caso de objectos locais, recorrendo a esta directiva, é possível seriali-

zar por completo o acesso. No caso de objectos remotos torna-se mais complicado, pois é

necessário bloquear os clientes do lado dos clientes (proxy ) ou do lado dos servidores.

13

Page 34: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

A abordagem adoptada pela equipa de desenvolvimento do Java RMI foi restringir o blo-

queio de objectos remotos apenas para os proxies. Isto significa que na prática os objectos

remotos não podem ser protegidos de acessos simultâneos, por parte de processos que es-

tão a utilizar proxies diferentes, recorrendo apenas à directiva synchronized. Desta forma é

necessário recorrer a técnicas explícitas de bloqueio, como por exemplo declarar os métodos

remotos como synchronized.

3.4.2 Invocação de objectos remotos em Java

Uma vez que as diferenças entre um objecto local e um remoto são dificilmente visíveis

ao nível da linguagem, a invocação de métodos remotos também é feita de modo quase

transparente.

Qualquer tipo primitivo ou objecto pode ser passado como argumento de um método re-

moto, desde que possa ser empacotado. Em Java empacotável é sinónimo de serializável.

Para o objecto ser serializável tem de implementar a interface serializable. Teoricamente

todos os objectos podem ser serializáveis, com excepção de objectos dependentes do sis-

tema operativo, tais como descritores de ficheiros e sockets.

Durante a invocação de um método remoto os objectos locais são passados por valor, ao

contrário das referências para objectos remotos que são passadas por referência, tal como

descrito na Secção 3.3.

Uma referência para um objecto remoto consiste num endereço de rede e um porto, para

além de um identificador dentro do espaço de endereçamento do servidor. Uma referência

para um objecto remoto tem ainda de conter a pilha de protocolos, usada para comunicação

entre o servidor e o cliente.

Um objecto remoto é constituído com base em duas classes, a classe servidor e a classe

cliente. A classe servidor contém a implementação dos métodos disponibilizados para invo-

cação remota, assim como o estado do objecto. O skeleton é gerado a partir da especificação

da interface.

A classe cliente contém a implementação do código que é necessário executar no lado

de cliente e uma implementação de uma proxy. A proxy é gerada a partir da especificação

da interface, tal como o skeleton. O funcionamento da proxy está descrito na Secção 3.1.

Para cada invocação remota a proxy estabelece uma ligação com o servidor, sendo a ligação

terminada após o retorno da invocação. É por esse motivo que a proxy necessita do endereço

de rede e do porto, mencionados anteriormente.

Uma proxy tem toda a informação necessária que um cliente necessita para invocar méto-

dos num objecto remoto. Em Java as proxies são serializáveis por isso podem ser passadas

como parâmetro na invocação de métodos remotos e usadas como referências para os ob-

jectos remotos respectivos.

3.4.3 O registo RMI (rmiregistry)

Uma aplicação típica RMI, consiste em dois programas separados: cliente e servidor.

O servidor disponibiliza objectos e o cliente invoca métodos nesses objectos. Antes de

puder invocar métodos o cliente tem de adquirir referências para os objectos. As referências

podem ser obtidas durante a execução normal do programa, como retorno da invocação de

um método, ou com recurso ao registo RMI (rmigregistry).

14

Page 35: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Para um cliente puder obter uma referência a partir do rmiregistry é necessário que o

servidor tenha efectuado o registo previamente.

O registo é um objecto remoto que faz o mapeamento entre nomes (Strings) e referências

para objectos remotos. Um processo servidor pode ter o seu próprio registo, ou pode ser

utilizado um registo por cada anfitrião. A classe java.rmi.registry.LocateRegistry pode

ser usada para obter uma referência para o registo no anfitrião especificado, ou para criar um

objecto registo.

3.4.4 Implementação de objectos remotos

Uma interface remota é uma interface que declara métodos que podem ser invocados de

uma máquina virtual Java remota. Uma interface remota tem de estender directa ou indirec-

tamente a interface java.rmi.Remote. Esta interface funciona como marcador, não definindo

qualquer método.

Para além das excepções específicas lançadas pela aplicação, a declaração de um mé-

todo remoto tem de incluir o lançamento da excepção java.rmi.RemoteException ou de uma

das suas superclasses.

A excepção java.rmi.RemoteException é lançada quando a invocação do método re-

moto falha por qualquer motivo. Algumas das razões para o lançamento da excepção são:

problemas de comunicações, erro no empacotamento ou desempacotamento de parâmetros

ou valores de retorno, erros de protocolo, etc.

Para a criação e exportação de objectos remotos utilizam-se as classes

java.rmi.server.UnicastRemoteObject e java.rmi.activation.Activatable.

Enquanto que a classe java.rmi.server.UnicastRemoteObject define um objecto

cuja referência é válida apenas enquanto o processo servidor estiver vivo, a classe

java.rmi.activation.Activatable é uma classe abstracta que define um objecto que ini-

cia a sua execução quando os seus métodos são invocados, parando a execução quando

necessário.

Visto que não foram utilizados objectos “activos” no desenvolvimento do trabalho, o funci-

onamento da classe java.rmi.activation.Activatable não será aprofundado.

A classe java.rmi.server.UnicastRemoteObject ou uma classe que a estenda, ex-

porta os objectos aquando a sua construção. Esta exportação consiste na escuta de um

porto TCP. Caso a classe não herde de java.rmi.server.UnicastRemoteObject, é pos-

sível exportar um objecto recorrendo explicitamente ao método exportObject da classe

java.rmi.server.UnicastRemoteObject.

Geralmente, uma classe que implementa uma interface remota estende a classe

java.rmi.server.UnicastRemoteObject, herdando os comportamentos remotos das super-

classes de java.rmi.server.UnicastRemoteObject (equals, hashCode e toString) ou es-

tende uma outra classe remota. A classe pode implementar qualquer número de interfaces e

definir métodos que não estão declarados na interface remota, ficando esses métodos dispo-

níveis apenas localmente.

Para parar de exportar o objecto existe o método unexportObject da classe

java.rmi.server.UnicastRemoteObject. A paragem torna o objecto indisponível para in-

vocações remotas.

15

Page 36: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

3.4.5 Limpeza de objectos distribuídos (Garbage Collecting)

Tal como num sistema local, é desejável que num sistema distribuído os objectos que já

não estão referenciados sejam removidos automaticamente.

O sistema RMI tem um algoritmo de contagem de referências baseado em [5]. A conta-

gem de clientes que referenciam um objecto é feita para cada máquina virtual Java. Cada

vez que uma nova referência chega a uma máquina virtual Java, é enviada uma mensagem

referenced para o servidor do objecto. Se a referência já existir apenas é incrementado um

contador na máquina virtual do cliente. À medida que as referências vão deixando de ser

utilizadas, o contador é decrementado e quando a última referência local for descartada é

enviada uma mensagem unreferenced para o servidor.

As mensagens de referenced e unreferenced são utilizadas pelo servidor para actualizar

a contagem de clientes para o objecto em questão.

Quando não existirem mais clientes a referenciar o objecto, passa a existir uma referência

fraca. A referência fraca permite que o objecto seja removido do sistema, caso não haja

nenhuma referência local para esse objecto no servidor. Enquanto existir uma referência local

para o objecto é possível passar uma referência para o cliente, e o objecto não pode ser

removido do sistema.

Um objecto que necessite de notificação para quando não existirem mais referências, tem

de implementar a interface java.rmi.server.Unreferenced.

Para além do mecanismo de contagem de referências, uma referência está associada a

um tempo de aluguer (lease). Se um cliente não renovar o aluguer considera-se que houve

uma falha do cliente, e o número de clientes é decrementado. É possível controlar o tempo

de aluguer recorrendo à propriedade de sistema java.rmi.dgc.leaseValue. Por omissão

o tempo de aluguer é de 10 minutos. De referir que um período de aluguer curto permite

ter um melhor controlo da falha de clientes e consequentemente uma limpeza mais rápida

dos objectos não referenciados, mas leva a um aumento das comunicações associadas à

renovação do respectivo aluguer.

É possível que um objecto seja removido prematuramente caso ocorram problemas de co-

municação, como por exemplo uma partição da rede. Neste caso a invocação de um método

remoto por parte do cliente irá gerar uma excepção java.rmi.RemoteException, que tem de

ser tratada pela aplicação cliente.

16

Page 37: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Capítulo 4

Comunicação fiável em multicast

Pretende-se com este trabalho desenvolver uma cache de dados distribuída. A solução

tem de ser tolerante a falhas, de forma a maximizar a disponibilidade da cache. Uma forma

de aumentar a tolerância a falhas de um sistema é recorrer a técnicas de replicação. Para

fazer a replicação de forma eficiente é necessário o envio de actualizações em multicast.

Existem diversos protocolos para comunicação em multicast. O artigo [23] faz um sumário

de diversos protocolos existentes e classifica-os segundo as suas características (fiabilidade,

retransmissão, gestão de grupos, ...). As características com especial interesse são a fiabili-

dade e a existência de grupos de comunicação.

É neste contexto que surgem os grupos de comunicação fiável. Este tipo de serviço ga-

rante que uma mensagem é entregue a todos os membros do grupo. No entanto, garantir

a entrega das mensagens pode ser uma tarefa nada trivial, especialmente na presença de

falhas.

Sendo a fiabilidade o principal requisito, serão referidos de seguida alguns dos protocolos

de comunicação em multicast que cumprem este requisito. Começa-se por apresentar uma

solução básica.

4.1 Comunicação fiável em multicast básica

Grande parte das camadas de transporte oferecem mecanismos de comunicação fiável

ponto-a-ponto, mas raramente oferecem mecanismos com garantia de entrega para um con-

junto de processos.

Quando estes mecanismos não estão disponíveis pode sempre recorrer-se a comunicação

fiável ponto-a-ponto, estabelecendo uma ligação com cada um dos processos com os quais

se quer comunicar. Esta abordagem é de fácil implementação e pode ser uma boa solução

para grupos pequenos, mas não é muito eficiente.

Na comunicação fiável em multicast, uma mensagem que é enviada para um grupo de

processos deve ser entregue a cada membro do grupo. As dificuldades surgem quando um

processo se junta ao grupo, ou quando um dos processos do grupo falha.

Torna-se necessário fazer a distinção entre comunicação fiável na presença de processos

que podem falhar e comunicação fiável quando se assume que todos os processos funcionam

de forma correcta. No primeiro caso, considera-se que a comunicação é fiável quando se pode

garantir que todos os membros do grupo que não falharam recebem a mensagem.

17

Page 38: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Admitindo que existe acordo de quem são os elementos do grupo, assumindo que os

processos não falham, e ainda, que os processos não se juntam nem saem do grupo durante

a comunicação, apenas é necessário garantir que a mensagem é entregue a cada membro

do grupo. Desta forma é relativamente fácil implementar uma solução.

Considere-se o caso em quem existe apenas um processo que quer enviar uma mensa-

gem para vários processos. Assumindo que estão disponíveis mecanismos de multicast não

fiável, podem ocorrer perdas de pacotes, sendo a mensagem entregue apenas a alguns dos

processos. A Figura 4.11 ilustra uma possível solução para o problema.

O processo emissor atribuí um número de sequência a cada mensagem enviada em multi-

cast. Admite-se que as mensagens são recebidas pela ordem em que foram enviadas. Cada

mensagem enviada em multicast fica guardada no histórico do processo emissor enquanto

não for recebida uma confirmação de entrega (ACK) por parte de cada um dos receptores.

Se um receptor detectar que uma mensagem está em falta, envia um NACK, indicando ao

emissor que é necessário efectuar uma retransmissão.

Figura 4.1: Uma solução simples para comunicação fiável em multicast, onde são conhecidos todos os

receptores e se assume que não há falhas em nenhum dos processos. (a) Transmissão da mensagem.

(b) Feedback.

4.2 Escalabilidade em comunicação fiável em multicast

Existem diversos problemas na solução apresentada na Secção 4.1. A solução não é

escalável, pois admitindo que existem N receptores, o emissor tem de estar preparado para

receber N ACKs.

Uma possível solução para este problema é enviar apenas NACKs. No entanto o problema

não fica resolvido, pois pode ocorrer uma situação em que uma mensagem não é entregue a1Figura 7-8 de [37]

18

Page 39: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

nenhum dos receptores, originando N NACKs. Além disso esta possível solução levantaria um

problema relacionado com a dimensão do histórico do emissor, pois caso um dos receptores

recebesse correctamente todas as mensagens, o emissor não receberia feedback e as men-

sagens teriam de ser guardadas eternamente. Logicamente isso não seria implementável,

sendo necessário definir um tamanho máximo para o histórico que ao ser atingido levaria ao

descarte de mensagens, podendo mais tarde um pedido de retransmissão não ser satisfeito.

Diversas soluções foram propostas com o intuito de atingir escalabilidade em comunica-

ções fiáveis em multicast. Seguidamente apresentam-se duas dessas soluções.

4.2.1 Controlo de feedback não hierárquico

Como referido na Secção 4.2 um dos principais problemas é devido ao grande número

de mensagens de feedback. Assim sendo, torna-se necessário arranjar formas de diminuir o

número de mensagens de retorno. Um dos mecanismo adoptado foi a supressão de feedback

e é o esquema que está na origem do protocolo Scalable Reliable Multicast (SRM) descrito

em [10].

No protocolo SRM os receptores apenas enviam NACKs. Os NACKs são enviados em

multicast, pelos receptores que necessitam da retransmissão de um pacote perdido.

O pedido de retransmissão não tem de ser necessariamente satisfeito pelo emissor, po-

dendo ser satisfeito por qualquer um dos processos que tenha a informação. Para evitar

a existência de múltiplas cópias dos dados retransmitidos, as retransmissões são feitas em

multicast para todo o grupo.

O mecanismo de supressão de feedback entra em acção antes do envio de um NACK ou

antes da retransmissão de dados, onde um processo espera um tempo aleatório e no qual

suprime a sua própria transmissão, caso a ouça por parte de outro elemento do grupo.

A Figura 4.22 ilustra o funcionamento do mecanismo de supressão de feedback.

Figura 4.2: Vários receptores agendaram um pedido de retransmissão, mas o primeiro pedido de

retransmissão leva à supressão dos outros.

Os mecanismos de supressão de feedback conduzem a soluções que escalam bem, con-

tudo também sofrem de problemas.

2Figura 7-9 de [37]

19

Page 40: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

O principal problema está em conseguir que apenas um NACK seja enviado, ou que a

retransmissão seja feita apenas por um processo. É preciso que o tempo de propagação das

mensagens seja baixo, para que as mensagens em multicast sejam rapidamente recebidas

pelos diversos processos. Este requisito verifica-se nas redes locais, mas não para o caso de

redes largas em que é espectável que o número de mensagens seja superior ao desejado, ou

seja, superior a uma mensagem por cada NACK ou retransmissão global necessária. Poderia

optar-se pela segmentação do tempo em slots, mas essa solução levaria à necessidade de

sincronização das estações, o que levanta outros tantos problemas.

Outro problema prende-se com a necessidade de processamento de mensagens, por

parte de processos que já receberam a mensagem correctamente, ou seja, mensagens que

são inúteis para eles.

4.2.2 Controlo de feedback hierárquico

A supressão de feedback tal como descrita na Secção 4.2.1 é uma solução não hierár-

quica. Para puder obter uma solução realmente escalável, para grupos muito grandes, é

preciso recorrer a abordagens hierárquicas.

A Figura 4.33 apresenta a visão geral de uma solução hierárquica. Para simplificar, assume-

se que apenas um processo quer transmitir uma mensagem em multicast para um grande

grupo de processos. O grupo de processos está dividido em partições, cada uma formando

um subgrupo, que estão organizadas em forma de árvore. O subgrupo que contem o emis-

sor forma a raiz da árvore. Dentro de cada um dos subgrupos pode ser utilizado qualquer

esquema de multicast que funcione para pequenos grupos.

Figura 4.3: Vista geral de uma solução hierárquica de comunicação fiável em multicast. Cada

coordenador reencaminha a mensagem para os seus filhos e atende pedidos de retransmissão.

Cada subgrupo nomeia um coordenador local, que é responsável por atender pedidos de

retransmissão por parte dos receptores do seu subgrupo. Desta forma, cada coordenador

3Figura 7-10 de [37]

20

Page 41: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

local terá o seu próprio histórico. Se o próprio coordenador tiver perdido uma mensagem, ele

pede ao coordenador do subgrupo pai para retransmitir a mensagem. Num esquema baseado

em ACK, um coordenador local envia um ACK para o seu pai se tiver recebido a mensagem.

Se o coordenador tiver recebido ACKs para a mensagem por parte de todos os membros do

subgrupo, bem como por parte dos seus filhos, a mensagem pode ser removida do histórico.

O problema principal desta solução tem a ver com a construção da árvore, pois por vezes

há necessidade de construir a árvore de forma dinâmica.

Posto isto, a construção de mecanismos fiáveis de comunicação em multicast que escalem

para um grande número de receptores, dispersos por uma rede wide, é um problema de difícil

resolução. Não existe apenas uma solução e cada nova solução apresentada introduz novos

problemas.

Alguns dos protocolos que adoptam soluções de controlo de feedback hierárquico: Tree-

Based Multicast Transport Protocol [46], Log-Based Receiver-Reliable Multicast [15], Reliable

Multicast Transport Protocol [19], Structure-Oriented Resilient Multicast [45].

4.3 Multicast Atómico

Nas Secções 4.1 e 4.2 admitia-se que os processos não falhavam. Esta secção aborda a

comunicação fiável em multicast, na presença de falhas.

Em sistemas distribuidos, geralmente o necessário é garantir que a mensagem é entregue

a todos os processos, ou a nenhum. Por vezes é ainda necessário garantir que todos os pro-

cessos recebem as mensagens pela mesma ordem. Este problema é conhecido por multicast

atómico.

Para perceber porque é que a propriedade atómica é tão importante considere-se o exem-

plo de uma base de dados replicada, construída como uma aplicação sobre um sistema dis-

tribuído. O sistema distribuído oferece mecanismos de envio de mensagens em multicast e,

em particular, permite a criação de grupos de processos para os quais as mensagens podem

ser enviadas de forma fiável. A base de dados replicada é então construída como um grupo

de processos, com um processo para cada uma das réplicas. Operações de actualização

são sempre enviadas em multicast para todas as réplicas e efectuadas localmente, ou seja, é

utilizado um protocolo de replicação activa.

Supondo que uma série de actualizações está para ser executada, mas que durante a

execução de uma das actualizações uma das réplicas falha. A réplica que falhou perdeu a

actualização, mas todas as outras réplicas efectuam a actualização correctamente.

Quando a réplica recupera, pode ter perdido diversas actualizações. Torna-se necessário

sincronizar o estado com as outras réplicas, efectuando as operações pela ordem correcta.

Supondo agora que o sistema distribuído suporta comunicação em multicast atómico: a

actualização enviada para todas as réplicas antes de uma delas falhar é efectuada em todas

a réplicas que não falharam, ou em nenhuma. Na comunicação em multicast atómico, uma

operação só é efectuada se todos os processos estiverem de acordo em relação aos mem-

bros do grupo. Neste caso, a operação só teria sucesso se os restantes membros do grupo

chegassem a acordo em relação à falha da réplica.

Ao recuperar, a réplica tem de se juntar novamente ao grupo. Não receberá actualizações

enquanto não estiver registada como membro. Para se juntar ao grupo é necessário que o

seu estado esteja coerente com o dos restantes membros do grupo.

21

Page 42: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Desta forma a comunicação em multicast atómico garante que os processos que não

falharam mantêm a base de dados consistente, e obriga à reconciliação dos processos após

a sua recuperação e nova junção ao grupo.

4.3.1 Sincronismo Virtual

Na presença de falhas, os mecanismos de comunicação fiável em multicast podem ser

univocamente definidos com base em alterações nos membros do grupo.

Antes de mais é necessário fazer uma distinção entre a recepção de uma mensagem e

a entrega de uma mensagem. Adopta-se um modelo em que o sistema distribuído consiste

numa camada de comunicação. A Figura 4.44 ilustra este modelo e a distinção entre recepção

e entrega de mensagens.

Figura 4.4: Organização lógica de um sistema distribuído, para distinguir a recepção da entrega de

mensagens.

Uma mensagem recebida é guardada na camada de comunicação, até puder ser entrega

à aplicação, que se encontra num nível superior.

A ideia base do multicast atómico consiste na entrega de uma mensagem em multicast,

univocamente associada a uma lista de processos à qual tem de ser entregue. Esta lista

constituí uma vista do grupo, correspondente aos processos existentes no grupo, vistos pelo

emissor, na altura em que a mensagem foi enviada. Um ponto importante é o facto de cada

processo da lista ter a mesma vista, ou seja, todos os processos têm de acordar que a men-

sagem tem de ser entregue a cada um deles.

Supondo que uma mensagem m é enviada no momento é que o emissor tem a vista G.

Enquanto a mensagem está a ser enviada, um processo entra ou sai do grupo. A mudança

é anunciada para todos os processos em G, ou seja, ocorre uma mudança de vista, envi-

ando uma mensagem mv anunciando a entrada ou saída de um processo. Passam a existir

4Figura 7-11 de [37]

22

Page 43: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

duas mensagens em trânsito: m e mv. O que é necessário garantir é que a mensagem m é

entregue a todos os elementos de G ou que m não é entregue a nenhum.

Existe apenas uma situação na qual o envio da mensagem m pode falhar, que corresponde

à falha do emissor, caso contrário a mensagem deve ser entregue a todos os processos da

vista G, que continuam a executar correctamente. Caso o emissor falhe, a mensagem pode

ser entregue a todos os outros processos na vista G, ou ignorada por todos. Um sistema

de comunicação fiável em multicast com estas características designa-se por virtualmente

síncrono [4].

Como exemplo de funcionamento, considere-se a Figura 4.55. Num dado momento o

processo P1 junta-se ao grupo, o qual passa a ser constituído pelos processos P1 a P4. Após

o envio de algumas mensagens para o grupo o processo P3 falha. Contudo, antes de falhar

a mensagem é enviada para P2 e P4, mas não para P1. O sincronismo virtual garante que a

mensagem não é entregue a nenhum dos processos, ou seja, é como se o processo P3 não

tivesse enviado a mensagem antes de falhar.

Figura 4.5: O princípio de funcionamento do sincronismo virtual em multicast.

Após o processo P3 ter sido removido do grupo, a comunicação continua entre os restan-

tes membros. Mais tarde o processo P3 recupera e junta-se ao grupo, mas apenas após ter

actualizado o seu estado com os restantes processos.

O princípio do sincronismo virtual admite que todas as comunicações em multicast ocor-

rem entre alterações de vistas, ou seja, uma alteração de vista funciona como uma barreira

através da qual nenhuma comunicação em multicast pode passar.

4.4 The Jgroup Project

O projecto Jgroup é uma integração da tecnologia de comunicação de grupos com objec-

tos distribuídos. Jgroup suporta um paradigma de programação de grupos de objectos, que

permite o desenvolvimento de serviços fiáveis e de alta disponibilidade, baseado em replica-

ção.

5Figura 7-12 de [37]

23

Page 44: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

O Jgroup estende as funcionalidades do Java RMI apresentado na Secção 3.4, permi-

tindo esconder dos clientes o facto de um objecto puder ser implementado por um grupo de

objectos, em vez de um único. Por outro lado, recorrendo à comunicação por grupos os servi-

dores podem coordenar as suas acções por forma a manter a consistência global do sistema

distribuído.

Na implementação da comunicação por grupos o Jgroup segue de perto a semântica do

sincronismo virtual, apresentada na Secção 4.3.1.

O Jgroup permite que os servidores formem um grupo de comunicação interna, com co-

municação em multicast, para manter a consistência do sistema distribuído, não sendo ne-

cessário os clientes juntarem-se ao grupo. Por omissão, os cliente interagem com o grupo de

servidores através de uma interface externa, com uma semântica anycast, em que a operação

é efectuada em qualquer um dos elementos do grupo servidor. No entanto é também possí-

vel invocar métodos nos servidores em multicast. Esta característica é bastante importante,

pois permite que os grupos tenham apenas a dimensão necessária para atingir a disponibi-

lidade e fiabilidade pretendida, permitindo a existência de um grande número de clientes e

consequentemente que o sistema seja escalável.

Jgroup é baseado no modelo de objectos distribuídos em Java e encontra-se implemen-

tado totalmente em Java. O código fonte do Jgroup encontra-se disponível livremente na

Internet [3] sobre licença Lesser General Public License (LGPL) [13].

4.4.1 Composição do Jgroup

O Jgroup pode ser decomposto em 3 serviços: Group Membership Service (GMS), Group

Method Invocation Service (GMIS) e State Merging Service (SMS).

O SMS é um serviço que permite que o sistema distribuído esteja particionado, podendo

posteriormente voltar a ser formado apenas por uma única partição. Este serviço existe para

fazer a junção de partições. Uma vez que neste trabalho não se pretende ter um sistema

particionado, este serviço não será utilizado e consequentemente o seu funcionamento não

será detalhado.

Seguidamente descreve-se o GMS e GMIS.

4.4.1.1 Group Membership Service - GMS

Um grupo é um conjunto de objectos servidores que cooperam para disponibilizar um ser-

viço distribuído. Para aumentar a flexibilidade, a composição do grupo pode variar à medida

que novos servidores são adicionados e os existentes são removidos.

Servidores que pretendam contribuir para o serviço distribuído juntam-se ao grupo,

tornando-se membros. Mais tarde, um membro pode decidir deixar de contribuir saindo do

grupo. A qualquer momento, a lista de membros de um grupo inclui os servidores que estão

operacionais e que se juntaram ao grupo, mas ainda não sairam do grupo.

A tarefa do GMS é registar variações voluntárias na lista de membros, assim como vari-

ações involuntárias devidas a falhas e a problemas de comunicação entre servidores. Todas

as variações na lista de membros são reportadas a cada um deles, através da instalação de

vistas, tal como descrito na Secção 4.3.1. Uma vista consiste numa lista dos membros e de

um identificador único.

24

Page 45: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

A especificação do GMS teve em conta diversos aspectos. O serviço tem de registar as

alterações de forma correcta e atempada6, para que as vistas instaladas possam reflectir

informação recente acerca da composição do grupo. Uma vista só pode ser instalada depois

de todos os elementos contidos nessa vista chegarem a acordo da composição da mesma.

Por fim o GMS tem de garantir que duas vistas instaladas por dois servidores diferentes são

instaladas pela mesma ordem.

As últimas duas propriedades são necessárias para que localmente um servidor possa

saber o que se está a passar no sistema distribuído.

4.4.1.2 Group Method Invocation Service - GMIS

O Jgroup difere de outros sistemas de grupos de objectos devido à adopção de apenas

um paradigma de comunicação, totalmente baseada em invocação de métodos em grupos.

Clientes e servidores interagem com os grupos, invocando remotamente métodos neles.

Ainda que clientes e servidores partilhem o mesmo paradigma de comunicação, distingue-

se entre invocação de métodos internos ao grupo (Internal Group Method Invocation - IGMI),

utilizados pelos servidores e invocação de métodos de forma externa ao grupo (External

Group Method Invocation - EGMI), utilizados pelos clientes.

Existem diversas razões para fazer esta distinção:

• Visibilidade: Os métodos necessários para implementar o serviço de replicação não

devem ser visíveis para os clientes. Os clientes devem apenas ter acesso às interfaces

que definem o serviço, enquanto os métodos para comunicação entre servidores devem

ser privados aos membros do grupo.

• Transparência: O Jgroup fornece um mecanismo de invocação para os clientes que

pretende ser transparente em relação à implementação do Java RMI, ou seja, os clientes

não têm de saber se estão a invocar um método num grupo de servidores, ou apenas

num único.

• Eficiência: Caso as especificações da EGMI e da IGMI fossem iguais, seria necessário

que os clientes se tornassem membros do grupo, o que levaria a que a escalabilidade

do sistema fosse mais reduzida. A EGMI tem uma semântica mais fraca do que a IGMI.

Ao tornar esta diferença aparente para o programador é possível tornar o sistema mais

escalável, limitando os custos mais elevados da comunicação entre membros do grupo,

a um número tipicamente inferior ao que seria necessário, caso os clientes fizessem

parte do grupo.

Ao desenvolver sistemas distribuídos utilizando o Jgroup os métodos para comunicação

interna são agrupados para formar a interface remota interna do objecto servidor, enquanto

que os métodos externos são agrupados para formar a sua interface remota externa.

Em tempo de execução, é gerada uma proxy capaz de atender a invocação de métodos

em grupos, baseada nas interfaces remotas do objecto servidor. Esta proxy permite a um

objecto cliente, ou servidor, comunicar com o grupo de objectos servidores, como se duma

invocação Java RMI habitual se tratasse.

Para puder invocar métodos internos ao grupo, os servidores têm de obter um proxy de

grupo por parte do Jgroup, em execução na máquina virtual Java local. Por outro lado, clientes

6Não existem garantias temporais. Apenas é garantido que a instalação de novas vistas não será adiada indefini-damente.

25

Page 46: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

que precisem de interagir com um grupo necessitam de obter uma referência a partir de um

serviço de registo.

O Jgroup tem um serviço de registo próprio, que será descrito na Secção 4.4.2. Este

serviço é uma extensão do registo incluído no Java RMI e assim sendo o funcionamento

básico é o descrito na Secção 3.4.3.

À semelhança das definições feitas na Secção 4.3.1, é necessário distinguir entre fazere completar a invocação de um método. Fazer a invocação de um método corresponde ao

momento em que a invocação é feita. Completar a invocação do método corresponde ao

momento em que a execução do método termina.

De seguida explica-se o funcionamento da EGMI e da IGMI.

Internal Group Method Invocation - IGMIAo contrário do Java RMI tradicional, a IGMI retorna um vector de resultados, em vez de

um único valor.

A IGMI está disponível de forma síncrona e assíncrona. Na forma síncrona, o processo

que invocou o método fica bloqueado até que um vector com os resultados de cada um dos

membros do grupo lhe possa ser entregue. Podem haver situações em que este compor-

tamento não é desejável, por causa do tempo que o processo pode ficar bloqueado. Além

disso é necessário ter consciência que podem ocorrer deadlocks caso existam invocações

circulares.

Na forma assíncrona, o processo que invocou o método não fica bloqueado mas especifica

um objecto de callback, que será notificado quando o resultado estiver disponível.

Se o método invocado não tiver valor de retorno (void) um processo ao fazer uma invo-

cação tem duas possibilidades: especificar o objecto de callback para ser notificado que a

invocação está completa, ou especificar null indicando que não pretende ser notificado.

Uma IGMI é completa de acordo com o sincronismo virtual, descrito na Secção 4.3.1.

É garantido que uma IGMI termina com um vector de resultados (contendo, pelo menos, o

valor de retorno computado pelo próprio processo que fez a invocação), ou com uma das

excepções definidas no método invocado. Além disso, se um servidor S completar uma IGMI

numa vista, todos os servidores com a mesma vista também completam a mesma invocação,

ou S irá instalar uma nova vista. Desta forma é garantido que a invocação será completa por

todos os servidores do grupo que não falharam.

A IGMI satisfaz a propriedade de integridade na qual uma invocação só é completa por

cada um dos servidores no máximo uma vez, e apenas se algum dos servidores tiver feito a

invocação previamente.

O Jgroup garante que cada IGMI é completa no máximo numa vista, ou seja, se servi-

dores completaram a mesma IGMI, não o podem ter feito em vistas diferentes. Desta forma,

garante-se que todos os resultados contidos na vector de resposta foram computados durante

a mesma vista.

External Group Method Invocation - EGMIA EGMI que caracteriza a interacção entre cliente e servidor é completamente transparente

para o cliente, que a utiliza como se de uma RMI padrão se tratasse.

A EGMI permite dois tipos de invocação: anycast e multicast. Uma invocação em anycast

é completa pelo menos por um servidor do grupo, a não ser que não exista nenhum servidor

26

Page 47: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

operacional. Uma invocação em multicast é completa por todos os servidores do grupo.

A escolha do tipo de invocação pode ser especificada para cada um dos métodos da

interface remota. Por defeito a semântica é anycast. Para usar multicast é necessário indicar

que o método lança a excepção McastRemoteException. Durante a geração do código da

proxy e do skeleton a interface é inspeccionada para verificar qual a semântica especificada.

É garantido que se existir pelo menos um servidor operacional a EGMI retornará um valor,

ou uma das excepções especificadas na declaração do método será lançada. Além disso,

uma EGMI é efectuada no máximo uma vez por cada servidor e apenas se um cliente tiver

invocado o método anteriormente. Estas duas propriedades aplicam-se quer para invocação

de métodos em multicast, quer para unicast. No caso do multicast é também garantido o

sincronismo virtual.

IGMI e EGMI diferem num aspecto importante. Se uma IGMI completar, é garantido que

o fará com a mesma vista para todos os servidores, no entanto uma EGMI pode concluir em

diferentes vistas concorrentes. Esta situação é possível caso um servidor tenha completo a

invocação, mas tenha sido particionado antes de entregar o resultado ao cliente. O proxy do

cliente detecta uma falha do servidor e invoca novamente o método. A única solução para o

problema seria o cliente juntar-se ao grupo, antes de invocar o método. Desta forma o cliente

participaria no protocolo de acordo de vistas e veria a invocação do método atrasada, até

uma nova vista ser instalada. No entanto, essa solução traria problemas de escalabilidade, tal

como referido na Seccção 4.4.1.2.

Os problemas anteriores têm origem na divisão do sistema em partições, contudo tal como

referido na Secção 4.4.1, não se pretende ter um sistema particionado.

4.4.2 O serviço de registo do Jgroup

Ao introduzir o paradigma de grupos de objectos, referiu-se que a transparência era um

ponto importante, não pudendo os clientes distinguir a invocação de um método num grupo

de servidores replicados, de uma simples invocação Java RMI.

A Secção 3.4.3 explica como é que se pode obter uma referência a partir do registo RMI

padrão. Este registo introduz um ponto de falha único. Caso falhe não é possível obter referên-

cias para objectos remotos. Além disso, o registo RMI padrão não permite obter referências

para grupos de objectos.

Para colmatar estas falhas o Jgroup incluí o Dependable Registry Service (DRS), onde os

grupos de servidores podem registar as suas referências e os clientes as podem obter.

O DRS é constituído por um grupo de servidores que mantêm um registo replicado dos

pares nome/grupo de servidores, resolvendo o problema do ponto de falha único do registo

RMI. De notar que a implementação do DRS recorre ao próprio Jgroup, ou seja, existe um

conjunto de servidores organizados em grupo, para fornecer o serviço, ao qual os clientes

acedem através de EGMI.

O registo do Jgroup apresenta vantagens em relação ao registo RMI. Como já referido

uma delas é a oferta de um registo altamente disponível. Outra é o facto de os clientes não

precisarem de saber a localização do registo, como acontecia na implementação incluída no

JDK. Os clientes interagem com as replicas como se de apenas um servidor se tratasse.

27

Page 48: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

4.4.3 Configuração do sistema distribuído

De forma a puder fazer alterações no sistema distribuído, sem ter de recompilar as apli-

cações, a configuração das máquinas que estão a utilizar o Jgroup é feita com recurso a um

ficheiro de configuração. Além da composição do sistema distribuído, o ficheiro permite ainda

a configuração de portos de acesso e a definição de parâmetros do sistema de transporte.

A Figura 4.6 ilustra o preenchimento do ficheiro.

<?xml version="1.0" encoding="us-ascii"?>

<!DOCTYPE Configuration SYSTEM "config.dtd">

<Configuration version="1.0">

<BootstrapRegistry port="44002"/>

<Transport payload="1024" maxTTL="10" TTLWarning="5"/>

<DistributedSystem>

<Domain name="ist.utl.pt" address="226.1.1.1" port="44001">

<Host name="sigma01" port="44000"/>

<Host name="sigma03" port="44000"/>

<Host name="sigma04" port="44000"/>

<Host name="sigma05" port="44000"/>

<Host name="sigma06" port="44000"/>

</Domain>

</DistributedSystem>

</Configuration>

Figura 4.6: Exemplo do preenchimento do ficheiro config.xml.

O atributo port do elemento BootstrapRegistry permite definir o porto de comunicação

no qual serviço de registo do Jgroup ficará à escuta. O elemento Transport permite definir

alguns parâmetros da camada de transporte, tais como: tamanho do conteúdo do pacote,

número máximo de saltos entre a origem e o destino, aviso quando um determinado número

de saltos é ultrapassado, etc.

Relativamente ao sistema distribuído, é necessário configurar o domínio, endereço para

comunicação em multicast e o respectivo porto. É ainda necessário configurar o nome das

máquinas e o endereço para comunicação em unicast.

De notar que é necessária a existência deste ficheiro em todas as máquinas que estão a

executar a aplicação.

28

Page 49: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Parte II

Desenvolvimento de uma cache dedados distribuída

Page 50: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção
Page 51: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Capítulo 5

Serviço de Cache centralizado

A elaboração deste trabalho foi efectuada com base num serviço de cache centralizado já

existente, que será descrito ao longo deste capítulo.

A aplicação em funcionamento sobre a cache é responsável pela execução de tarefas de

fluxos de processo. Cada fluxo de processo tem associado um identificador, a partir do qual

é possível identificar univocamente o seu contexto de execução. Um contexto de execução

representa relações entre entidades existentes no sistema, para um fluxo específico.

Uma entidade pode ser referenciada por diversos contextos simultaneamente.

A estrutura de cache existente é uma estrutura de dois níveis, em que o nível superior

apenas tem conhecimento de contextos, enquanto o nível inferior opera com entidades.

As relações entre entidades podem ser de um para um, ou de um para muitos. São as

relações entre entidades que dão origem a uma estrutura hierárquica.

5.1 Estrutura hierárquica

Em termos de representação, um contexto de execução é um objecto da classe

CachedParameterMap e uma entidade é um objecto da classe CachedObject.

Um CachedParameterMap representa relações entre CachedObjects.

Um CachedObject é constituído por um conjunto de tipos elementares (boolean, int,

string, ...), com representação física em entidades da base de dados. Este objecto ar-

mazena em memória os valores lidos da base de dados, bem como bandeiras (flags), que

permitem saber se o objecto sofreu alterações em memória que ainda não foram reflectidas

na base de dados.

A Figura 5.1 representa um CachedParameterMap. Este CachedParameterMap contém re-

lações entre dois CachedObjects: ORDER e CLIENT.

O CachedObject ORDER é constituído por OrderID, DataLancamento e ReferenciaDeOr-

dem, correspondendo os seus valores a uma entrada da tabela ENT_T_ORDER, na base de

dados.

O CachedObject CLIENT é constituído por ClientID, Nome, Morada e TelefoneDeCon-

tacto, correspondendo os seus valores a uma entrada da tabela ENT_T_CLIENT, na base de

dados.

31

Page 52: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Figura 5.1: Contexto de execução básico.

Existe uma relação de um para um, entre os CachedObjects da Figura 5.1, mas tal como

se ilustra na Figura 5.2 também podem existir relações de um para N.

Figura 5.2: Relações entre entidades.

Um CacheParameterMap é definido univocamente pelo CachedObject ORDER. As associ-

ações entre CachedObjects são válidas para um contexto específico e dão origem a entradas

na tabela PRV_T_RELATION, da base de dados, com o esquema indicado na Tabela 5.1.

Tabela 5.1: Esquema da tabela de relações.

ORDER_ID ORIG_ENTITY ORIG_KEY DEST_ENTITY DEST_KEY RELATION_INDEX

A coluna ORDER_ID corresponde ao identificador do CachedObject ORDER e identifica

univocamente o contexto de execução. As colunas ORIG_ENTITY e DEST_ENTITY corres-

pondem à definição da relação hierárquica de pai para filho, tendo como identificadores as

entradas das colunas ORIG_KEY e DEST_KEY. A coluna RELATION_INDEX indica o índice

32

Page 53: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

da relação (0 a N-1). A uma relação de um para um está sempre associado o índice 0.

Para o exemplo da Figura 5.1, as entradas da tabela PRV_T_RELATION são as indicadas na

Tabela 5.2.

Tabela 5.2: Preenchimento da tabela de relações para o exemplo da Figura 5.1.

ORDER_ID ORIG_ENTITY ORIG_KEY DEST_ENTITY DEST_KEY RELATION_INDEX

99 ENT_ORDER 99 ENT_CLIENT 56 0

5.2 Arquitectura

A Figura 5.3 ilustra a arquitectura do serviço de cache.

Figura 5.3: Arquitectura do serviço de cache.

Existem três camadas distintas no serviço de cache: apresentação, partilha de dados e

leitura e escrita. As três camadas encontram-se organizadas de acordo com a Figura 5.4.

A camada de apresentação disponibiliza aos diversos processos os dados organizados de

acordo com uma determinada estrutura. A camada de partilha de dados mantém a coerên-

cia da informação, garantindo que múltiplas estruturas de visualização lêem e modificam os

mesmos dados. Por fim, a camada de leitura e escrita garante as funcionalidades básicas

de gestão de uma cache: ler dados para memória e sincronizar com a base de dados as

alterações efectuadas em memória.

5.2.1 Camada de apresentação

A classe de suporte da camada de apresentação é a CachedParameterMap. É nesta ca-

mada que os objectos estão organizados hierarquicamente, tal como descrito na Secção 5.1.

Os principais métodos disponibilizados por esta camada são: getParameter, setParameter,

synch, assign e unassign.

33

Page 54: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Figura 5.4: Organização do serviço de cache em camadas.

Os métodos getParameter e setParameter permitem aceder e alterar, respectivamente,

o valor de um parâmetro específico de um CachedObject. As invocações dos métodos são

feitas tendo em conta a estrutura hierárquica do CachedParameterMap.

Considere-se o contexto da Figura 5.5. O acesso ao campo Morada é feito usando o

seguinte caminho: Ordem.Cliente.Morada. Este caminho representa um nome lógico do

parâmetro.

Figura 5.5: Exemplo de contexto.

Os métodos assign e unassign permitem inserir e remover um elemento da estrutura

hierárquica, respectivamente. Estas alterações têm efeito imediato na base de dados, ou

seja, é utilizada uma estratégia write-through para alterações na estrutura hierárquica.

34

Page 55: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

O método sync permite guardar (na base de dados) as alterações efectuadas (em memó-

ria), em todos os CachedObjects referenciados pelo CachedParameterMap. Desta forma, as

alterações de CachedOjects são feitas com uma estratégia write-back.

5.2.2 Camada de partilha de dados

A camada de partilha de dados é implementada pelas classes CachedObjectsManager e

ResourceCacheServiceBase, que assentam na utilização dos CachedObjects.

O serviço de cache propriamente dito é fornecido pela classe CachedObjectManager,

enquanto que a classe ResourceCacheServiceBase é responsável pelo acesso aos

CachedObjects.

Os principais métodos disponibilizados pela classe CachedObjectsManager à camada de

apresentação são: assign, unassign, synch, getParameter e setParameter.

Os nomes dos métodos são iguais aos da camada de apresentação, Secção 5.2.1, assim

como as funções por eles desempenhadas. De notar a diferença no acesso aos parâmetros

do CachedObject. Na passagem da camada de apresentação para a camada de partilha de

dados ocorre uma transformação dos nomes dos parâmetros. Passam de nomes lógicos para

nomes físicos, correspondentes aos existentes na base de dados.

A transformação referida é feita com recurso a serviços externos à cache (serviço de de-

finição de contextos e serviço de definição de entidades). O serviço de cache trabalha em

conjunto com estes e outros serviços. As funcionalidades dos serviços não serão descritas

pois estão fora do âmbito deste trabalho, sendo apenas referidas quando se considere ne-

cessário. A arquitectura do serviço de cache, contemplando as interacções com os restantes

serviços pode ser consultada no Anexo A.

Os principais métodos disponibilizados pela classe ResourceCacheServiceBase são

exclusiveUseWait, release e remove. O método exclusiveUseWait permite aceder em ex-

clusividade a um objecto da cache. Caso um objecto esteja em uso, a invocação é suspensa

até que o objecto esteja livre. Se o objecto não estiver em cache, é carregado a partir da base

de dados e colocado na cache. O método release liberta um objecto de utilização, permitindo

que seja utilizado por outro processo. Por fim, o método remove remove um objecto da cache,

mas não o remove da base de dados.

A classe ResourceCacheServiceBase permite o armazenamento de objectos de qualquer

tipo. O acesso aos objectos é feito com recurso a um objecto chave.

As relações entre os diversos CachedObjects de um contexto, não se encontram nos

CachedParameterMaps, mas sim todas juntas no Map _referenceN, do CachedObjectsManager.

As relações 1 para N estão expressas em memória sob a forma de vectores de referên-

cias, que o serviço de cache identifica através da chave composta: <ORDER_ID>:<Entidade

1>:<Entidade N>:<Campo da Entidade N que aponta para a Entidade 1>:<Identificador da

Entidade 1>.

A identificação das relações 1 para 1 é feita através da chave composta: <OR-

DER_ID>:<Entidade 1>:<Identificador da Entidade 1>:<Entidade N>.

Sempre que se actualiza uma relação 1 para 1 é necessário actualizar a relação 1 para

N correspondente. Relembrar o exemplo ilustrado na Figura 5.2, em que cada ordem tem

associado apenas um cliente, mas um cliente pode estar associado a várias ordens.

35

Page 56: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Interacção entre as camadas de apresentação e partilha de dadosConsiderando novamente o contexto da Figura 5.5 e relembrando que o acesso à morada

é feito através do caminho: Ordem.Cliente.Morada. Para se chegar ao valor da morada, o

serviço de cache através da referência Ordem, obtém o CachedObject Ordem com identifica-

dor 100. Seguidamente consulta a referência Cli_id através da qual obtém o CachedObject

Cliente, com identificador 66. A partir deste momento já é possível consultar/alterar o campo

Morada. A Figura 5.6 ilustra este acesso, assim como a interacção entre as camadas de

apresentação e partilha de dados.

Figura 5.6: Interacção entre as camadas de apresentação e partilha de dados.

5.2.3 Camada de leitura e escrita

A camada de leitura e escrita é suportada pela classe CachedObjectsDbStore. Esta classe

tem como principais funções a leitura e escrita dos dados das entidades, ou seja, os dados

contidos num CachedObject, na base de dados.

A camada de leitura e escrita interage com as duas classes da camada de partilha de

dados.

Os principais métodos da classe CachedObjectsDbStore invocados pela classe

ResourceCacheServiceBase são: add, get e delete. O método add escreve um

CachedObject na base de dados, executando uma actualização ou inserção, consoante o

objecto exista ou não na base de dados. O método get obtém um objecto a partir da base de

dados.

Em relação à classe CachedObjectsManager, os principais métodos invocados, da ca-

mada inferior, são: getRelationsList, addNewRowRelation, deleteRowRelation, createNew

e delete. Os três primeiros métodos utilizam a tabela PRV_T_RELATION da base de dados,

cujo esquema se encontra na Tabela 5.1. O método getRelationsList procura relações

entre duas entidades, tendo em conta uma ordem específica e devolve um Map com as rela-

ções encontradas. O método addNewRowRelation adiciona uma linha à tabela de relações e

o método removeRowRelation remove uma linha. O método createNew cria uma entrada na

tabela, correspondente à entidade especificada na chave, e devolve um CachedObject pre-

enchido com os valores por omissão. Finalmente o método delete, invocado por ambas as

36

Page 57: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

classes, apaga um objecto da base de dados.

5.3 Políticas de substituição

Esta cache não define uma política de substituição, no verdadeiro sentido da palavra, mas

sim uma política de remoção de objectos.

Numa política de substituição existe um algoritmo, que é executado de cada vez que a

inserção de um objecto em cache, leva a que o número máximo de objectos permitidos seja

ultrapassado. Há forçosamente uma substituição de um objecto em cache.

Na política de remoção de objectos, se um objecto que deveria ser removido estiver a ser

utilizado, a remoção não é feita, ou seja, o número de objectos em cache pode ultrapassar o

máximo definido. Visto que a inserção na cache é feita de forma dinâmica, o verdadeiro limite

ao tamanho da cache passa a ser a memória livre disponível no servidor. Entenda-se por

objecto em utilização, um objecto que foi bloqueado e que ainda não foi libertado. Para evitar

a existência eterna de objectos em memória, existe uma thread que executa periodicamente,

na tentativa de remover os objectos que já o deveriam ter sido.

Uma política de remoção de objectos tem vantagens e inconvenientes face a uma política

de substituição. A principal vantagem é que um objecto que se tem a certeza que ainda é

necessário não é removido da cache, evitando que as suas alterações sejam escritas na base

de dados, seja descartado de memória e carregado novamente da base de dados. A principal

desvantagem é o facto de deixar de existir efectivamente um valor máximo, ou seja, o valor

máximo existe mas não que tem de ser cumprido de forma rígida.

A cache permite definir a política de remoção de objectos. É possível escolher uma de

três políticas distintas: criação, modificação e frequência de acesso. Na política de criação, o

objecto que se encontra há mais tempo em cache é o primeiro a ser descartado. Na política

de modificação, o primeiro objecto a ser removido é o que foi modificado/acedido há mais

tempo. Por fim, na política de frequência de acesso, o objecto acedido/modificado menos

vezes, num determinado intervalo de tempo, é o primeiro a ser descartado.

37

Page 58: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção
Page 59: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Capítulo 6

Solução inicial

Com este trabalho pretende-se desenvolver uma cache de dados distribuída, partindo da

cache local descrita no Capítulo 5.

A abordagem inicial do problema foi baseada essencialmente em Remote Method Invoca-

tion do Java.

Esta abordagem apresenta a vantagem de não ter de lidar com o problema da consistên-

cia. Existe apenas um objecto (sem cópias ou réplicas associadas) e uma ou mais referências

remotas para esse mesmo objecto. Independentemente de se tratar de uma operação de lei-

tura ou escrita, existe sempre uma invocação remota do objecto sobre o qual se pretende

efectuar a operação, tal como descrito no Capítulo 3.

6.1 Arquitectura da solução

Aproveitando a arquitectura em camadas apresentada na Figura 5.4, uma solução óbvia

passa pela introdução de uma camada distribuída, entre a camada de partilha de dados e a

camada de leitura e escrita. A Figura 6.1 ilustra esta arquitectura, com duas caches a partilhar

dados.

Esta parece ser a solução mais simples, porque a gestão distribuída dos objectos passaria

a ser feita em termos de objectos elementares, que têm representação em entidades da base

de dados (CachedObjects). Estes objectos apresentam como principal vantagem a constitui-

ção por tipos elementares, logo serializáveis, e que podem ser facilmente transmitidos entre

máquinas distintas. Além disso, pretende-se manter a concorrência ao mais alto nível e desta

forma é possível que um CachedObject seja partilhado por caches distintas.

A nova camada terá uma função de cache de nível superior à cache local, na qual um

objecto que não existe localmente pode existir numa cache remota. Esta arquitectura em

níveis é análoga à existente em hardware nos processadores, nos quais podem existir vários

níveis de cache (cache de nível 1, cache de nível 2, ...). À medida que se sobe nos níveis, ou

que se desce na arquitectura em camadas, a obtenção do objecto torna-se mais custosa, do

ponto de vista temporal.

A Figura 6.2 representa a arquitectura da camada de cache distribuída. Esta camada é

constituída por um gestor de objectos distribuídos e pelas diversas caches (não é necessário

que sejam duas, podem ser mais, ou apenas uma). Existe comunicação entre o gestor e as

39

Page 60: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Figura 6.1: Arquitectura em camadas do serviço de cache distribuído.

caches. As caches também comunicam entre si.

Figura 6.2: Arquitectura da camada de cache distribuída.

O gestor pode estar alojado no mesmo servidor que uma das caches, ou num servidor

distinto.

6.2 Obtenção e registo de referências

A Figura 6.3 ilustra os passos necessários para obtenção e registo de referências. Admita-

se que a cache X necessita de um objecto para o qual não tem referência, ou seja, ocorreu

um miss na cache local.

A cache X faz um pedido ao gestor, especificando a chave do objecto pretendido. Admi-

tindo que o objecto não está a ser utilizado por nenhuma outra cache, o gestor cria um lock

40

Page 61: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

(a)

(b)

Figura 6.3: Obtenção e registo de referências.

associado à chave especificada. O gestor dá indicação à cache X que o objecto não está em

uso e que tem permissão para fazer o carregamento do objecto a partir da base de dados.

Neste momento a cache Y faz um pedido ao gestor, com a mesma chave especificada

pela cache X. O gestor verifica que o objecto está a ser carregado por uma outra cache, e a

thread responsável pelo processamento do pedido da cache Y fica bloqueada.

Em simultâneo, a cache X procede ao carregamento do objecto da base de dados. A leitura

da base de dados é feita com recurso às funcionalidades da camada de leitura e escrita. A

cache X faz a exportação do objecto, tornando-o num objecto remoto.

41

Page 62: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

A cache X acede ao gestor novamente e faz o registo do objecto. Durante o registo o

lock é substituído pela referência para o objecto remoto registado, efectuando a libertação de

todas as threads que possam estar bloqueadas nesse lock. É inserida uma referência para o

objecto na cache local de X.

Devido à libertação do lock, a thread responsável pelo processamento do pedido da cache

Y é desbloqueada. A cache Y é informada que o objecto está em uso e recebe uma referência

para o objecto remoto. É inserida uma referência para o objecto remoto na cache local de Y.

De notar que com esta solução, o gestor tem de ter capacidade para alojar referências

para todos os objectos existentes nas diversas caches.

6.3 Contagem de clientes

A contagem de clientes é feita por objecto. Cada objecto monitoriza os clientes que o estão

a utilizar.

As próximas secções explicitam de que forma é feito o registo e a remoção de um cliente,

assim como detalhes do registo e da remoção de referências do gestor.

6.3.1 Incremento do número de clientes

A Figura 6.4 ilustra o incremento do número de clientes. Existem duas situações em que

o incremento é efectuado: registo do objecto e aquisição de uma referência para o objecto.

A Figura 6.4a apresenta em detalhe o registo de um objecto, no que respeita à contagem

de clientes. Após o carregamento do objecto o1 da base de dados, por parte da cache X,

o1 encontra-se com 0 clientes. A cache X faz a exportação de o1, tornando-o num objecto

remoto.

A cache X faz o registo de o1 no gestor. Durante o registo o gestor invoca remotamente o

método registerClient em o1, registando um novo cliente.

Durante o registo do cliente, o número de clientes passa a 1 e é criado o objecto r1, no

espaço de endereçamento da cache X. O objecto r1 contém uma referência para o1. A cache

X faz a exportação de r1, tornando-o num objecto remoto.

Uma referência para r1 é passada para o gestor, como valor de retorno do método

registerClient. Por sua vez, esta referência é passada do gestor para a cache X, como

retorno do método register. A cache X coloca a referência para r1 no seu refMap.

A Figura 6.4b apresenta de forma detalhada a obtenção de uma referência para um

objecto remoto, com incidência na contagem de clientes.

A cache Y efectua um pedido ao gestor, especificando a chave do objecto o1. O ges-

tor verifica que o objecto pretendido já está em utilização e invoca remotamente o método

registerClient em o1 para efectuar o registo do novo cliente.

Durante o registo do cliente o número de clientes de o1 passa a 2 e é criado um novo

objecto r2, que referencia o objecto o1. O objecto r2 é exportado pela cache X, tornando-se

num objecto remoto.

Uma referência para r2 é passada como valor de retorno do método registerClient, para

o gestor. A cache Y é informada de que o objecto pretendido está em uso e recebe referências

42

Page 63: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

(a) Detalhe do registo de um objecto.

(b) Detalhe da obtenção de uma referência.

Figura 6.4: Contagem de clientes - incremento.

para os objectos remotos o1 e r2. A referência para o1 é colocada na cache local, enquanto

que a referência para r2 é colocada no refMap da cache Y.

6.3.2 Decremento do número de clientes

O decremento dos clientes de um objecto é efectuado sempre que a referência para o

objecto remoto é removida da cache local. Quando não existem mais clientes o objecto é

removido do gestor. A Figura 6.5 ilustra os processos de remoção de clientes e de remoção

43

Page 64: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

do gestor. O estado inicial do sistema distribuído é o apresentado na Figura 6.4b.

(a) Remoção do cliente local.

(b) Remoção de cliente remoto e remoção do gestor.

Figura 6.5: Contagem de clientes - decremento.

A Figura 6.5a ilustra a remoção de um objecto da sua cache local.

A cache X começa por remover do refMap a referência para o objecto r1. Invoca o método

unregisterClient do objecto o1, que decrementa o número de clientes para um. O objecto

o1 ainda não pode ser removido do sistema distribuído, pois ainda tem um cliente. Entretanto,

o objecto r1 é limpo pelo garbage collector, pois deixou de ser referenciado.

Deixa de existir uma referência na cache local para o1, mas apesar disso ele não é limpo

pelo garbage collector, pois ainda existem referências para o objecto no sistema distribuído.

44

Page 65: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Os detalhes sobre a limpeza de objectos distribuídos encontram-se na Secção 3.4.5.

A Figura 6.5b ilustra a remoção da referência para um objecto remoto, assim como a

remoção de um objecto do sistema distribuído. O estado inicial do sistema é o apresentado

na Figura 6.5a.

A cache Y remove a referência local que tem para o objecto remoto r2. Invoca o método

remoto unregisterClient, sobre o1, que decrementa o número de clientes, ficando este a

zero. Entretanto, o objecto r2 é limpo pelo garbage collector, pois não é referenciado local-

mente pela cache X e deixou de ser referenciado remotamente pela cache Y.

Como não existem clientes para o objecto o1, este invoca o método unregister, no gestor.

O gestor invoca o método remoto getClientNumber, no objecto o1, para verificar se número

de clientes é efectivamente nulo. Esta verificação é necessária para garantir que entretanto

não houve um pedido ao gestor para a utilização do objecto o1.

Uma vez que não existem mais clientes de o1, a referência é removida do gestor. A

invocação do método unregisterClient, por parte da cache Y, retorna e a referência para o1

é removida do espaço de endereçamento da cache Y.

Uma vez que deixam de existir referências para o1 no sistema distribuído, o objecto é

limpo pelo garbage collector, tal como descrito na Secção 3.4.5.

6.4 Estrutura hierárquica dos dados em cache

É necessário garantir que alterações efectuadas na estrutura hierárquica são visíveis por

todas as caches, que estão a utilizar um determinado contexto.

As relações entre entidades têm significado ao nível do CachedParameterMap, mas estão

todas juntas num mesmo Map, tal como descrito na Secção 5.2.2.

Uma possível solução para a propagação das alterações seria criar um objecto idêntico

ao CachedObject, mas para as relações entre entidades. Este novo objecto seria tratado de

forma idêntica ao CachedObject, tal como descrito na Secção 6.2.

Admitindo que existem C CachedObjects num contexto, seriam necessárias pelo menos C-

1 relações, ou seja, seria necessário lidar com C-1 objectos adicionais, no sistema distribuído.

Além do aumento da complexidade, uma solução deste género necessitaria de grandes

alterações no serviço de cache, e traria complicações adicionais devido às interacções com o

serviço de definição de contextos.

Outra solução possível seria a criação de um objecto idêntico ao CachedObject, mas que

em vez conter apenas uma relação, teria todas as relações de um determinado contexto.

Desta forma existiria apenas um objecto adicional, aos C objectos já existentes. Apesar do

aumento da complexidade ser reduzido, esta solução provocaria ainda bastantes alterações

na cache inicial.

De forma a minimizar as alterações ao serviço existente, é necessário recorrer a uma

nova abordagem. Uma nova abordagem passa pela introdução da comunicação por grupos,

descrita na Secção 4.3. A implementação foi elaborada com recurso à biblioteca Jgroup,

descrita na Secção 4.4.

45

Page 66: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

A ideia seria criar um grupo de comunicação, entre as diversas caches, para cada um

dos contextos. Ou seja, duas caches que estivessem a utilizar o mesmo contexto, seriam

membros do mesmo grupo. As operações de assign e unassign seriam efectuadas de forma

síncrona em todas as caches que estivessem a usar o contexto em questão.

Originalmente seria utilizada uma chave que identificasse univocamente o contexto, para a

identificação dos grupos de comunicação. No entanto verificou-se que o identificador do grupo

é um número inteiro. Mantendo a ideia anterior, criou-se apenas um grupo de comunicação

com todas as caches. Cada cache mantem uma lista dos contextos que está a utilizar. De

cada vez que é efectuada uma operação no grupo, a cache antes de fazer a actualização

verifica se está a utilizar o contexto em questão. Em caso negativo não faz nada.

Esta solução apresenta a desvantagem de ter de processar a operação, mesmo quando

o contexto em questão não está a ser utilizado pela cache, mas foi a única encontrada para

resolver o problema de forma simples.

Pode acontecer que exista uma associação ou des-associação que influencie a execução

de fluxos de processo, a correr em servidores distintos. No entanto este comportamento

também podia ocorrer para fluxos de processo em execução no mesmo servidor, na cache

inicial. O que se pretende é manter o mesmo comportamento da cache inicial.

As caches apenas têm em memória as relações necessárias para os contextos que estão

em utilização, ou seja, não existe nenhuma cache que tenha conhecimento de todas as rela-

ções existentes em base de dados. Assim, quando uma cache pretende utilizar um contexto,

faz o carregamento das relações a partir da base de dados.

Se existir uma actualização no contexto antes do carregamento da base de dados ter

terminado, é efectuado um novo carregamento da base de dados. De notar que as alterações

são efectuadas primeiro em base de dados e só depois em memória.

6.5 Tolerância a falhas

Devido à sua natureza, um sistema distribuído encontra-se sujeito a mais tipos de falhas

do que um sistema centralizado. É necessário minimizar a ocorrência dessas falhas através

de mecanismos de recuperação, pois pretende-se alta disponibilidade por parte do sistema.

As falhas podem ter diversas origens. Os principais tipos de falhas estão relacionados

com: crash de servidor, omissão e timing.

Uma falha devida a um crash ocorre quando um servidor termina a sua execução de forma

prematura, estando a trabalhar de forma correcta até esse momento.

Uma falha por omissão ocorre quando o servidor falha na resposta a um pedido. Esta falha

pode dever-se ao facto de o servidor nunca ter recebido o pedido, ou de ter ocorrido uma falha

no envio da resposta. Este é o tipo de falha considerado no caso da existência de problemas

de comunicação.

Uma falha de timing ocorre quando um servidor dá uma resposta, antes do cliente ter

alocado o buffer necessário para fazer o alojamento, ou devido a uma resposta tardia por

parte do servidor, sendo esta a situação mais frequente.

Existem outros tipos de falhas, relacionados com respostas arbitrárias por parte do servi-

dor (falhas "‘Bizantinas"’) e incorrecções nos valores de resposta.

46

Page 67: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

6.5.1 Java RMI

Uma vez que a comunicação é feita através do Java RMI é necessário perceber como é

que os erros são tratados por este modelo para objectos distribuídos. Em Java RMI, a ocor-

rência de falhas na invocação de métodos remotos é aparente para o programador através do

lançamento da excepção java.rmi.RemoteException, tal como descrito na Secção 3.4.4.

O principal problema está em saber qual o tipo de falha que ocorreu. Utilizando o Java

RMI não é possível saber qual a causa da falha, o que leva a que o tratamento dos diversos

tipos seja feito da mesma maneira para todos eles.

O Java RMI utiliza o protocolo TCP para comunicação, o que implica que há garantias

de entrega das mensagens desde que exista um caminho entre a origem e o destino, e que

que esteja um processo à escuta no porto especificado. Contudo não é possível estar inde-

finidamente à espera de uma resposta, existindo mecanismos de timeout para resolver essa

situação. Assim sendo não é possível saber se o servidor teve um crash ou se simplesmente

está ocupado a processar um pedido.

Caso não seja possível estabelecer uma ligação entre a origem e o destino, não existe

forma de saber se o servidor se encontra em baixo, ou se é um problema de comunicação.

Devido à impossibilidade de identificar correctamente o tipo de falha ocorrida, na imple-

mentação efectuada optou-se por assumir que todas as falhas são devidas a crash de servi-

dores.

6.5.2 Pontos de falha

A arquitectura desta solução é constituída pelas várias caches e um único gestor, tal como

descrito na Secção 6.1. É possível recuperar das falhas das caches, mas não da falha do

gestor. Desta forma o gestor apresenta-se como um ponto de falha do sistema. Caso ocorram

falhas no gestor todo o sistema falha.

Relembrar que o próprio Java RMI introduz um ponto de falha adicional, o registo RMI, tal

como descrito na Secção 4.4.2. A instância do registo RMI é criada pelo gestor, de forma a

concentrar os diversos pontos de falha num só.

O sistema possui uma única base de dados. Visto que não existe replicação, a base de

dados é também um ponto de falha, sem a qual o sistema distribuído não funciona. Assim

sendo é conveniente colocar o gestor no mesmo servidor onde se encontra a base de dados.

Desta forma obtém-se um único ponto de falha, caso os problemas estejam relacionados com

ligações de rede.

6.5.3 Recuperação da ocorrência de falhas

Estão previstas a ocorrência de falhas na invocação de métodos em objectos de cache

remotos, assim como falhas na execução das caches. Existem três situações distintas onde

podem ocorrer falhas: invocação de um método remoto, crash de clientes e antes do registo

de uma referência.

As próximas secções descrevem os passos efectuados durante a recuperação, para as

três situações referidas.

47

Page 68: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

6.5.3.1 Falha na invocação de um método remoto

Caso a falha seja no acesso a um dos objectos de cache remotos, a cache que invocou

o método acede ao gestor e remove a referência para o objecto que falhou. Reinicia-se o

pedido e haverá uma cache que obterá permissão para carregar o objecto da base de dados.

A Figura 6.6 ilustra a ocorrência deste tipo de falha. Para este exemplo, assume-se que o

estado do sistema distribuído é o representado na Figura 6.4b.

Figura 6.6: Falha na invocação de um método remoto e respectiva recuperação.

A cache X falha, ou seja, a sua execução termina de forma abrupta. A cache Y tenta invo-

car um método no objecto o1, que se encontrava no espaço de endereçamento da cache X. É

lançada uma excepção RemoteException que é apanhada pela cache Y. A referência para o

objecto remoto o1 é removida da cache Y. A cache Y invoca o método remoto unregister do

gestor, especificando a chave e a referência a remover. O gestor remove do seu espaço de

endereçamento a referência para o objecto o1. A invocação do método unregister retorna e

a cache Y remove do seu refMap a referência para o objecto remoto r2. A cache Y reinicia o

processo de obtenção/registo de referência descrito na Secção 6.2.

Quando caches distintas se apercebem da falha do mesmo objecto, ambas tentam fazer a

sua remoção do gestor. No entanto a operação de remoção só é efectuada uma vez, mesmo

que já tenha sido efectuado o registo de uma nova referência, com a chave do objecto que se

pretende remover, ou seja, é uma operação idempotente. Consegue-se esta propriedade com

recurso a um método de remoção, em que além da chave do objecto que se pretende remover

também se especifica o objecto em si (neste caso a referência para o objecto remoto). Desta

forma a remoção só é efectuada se o objecto especificado for igual ao objecto mapeado pela

chave.

Podia ter-se optado por carregar o objecto da base de dados e efectuar novo registo, após

a remoção do objecto que falhou. Não foi essa a solução adoptada, pois podiam existir vários

clientes a detectar a falha. Nesse caso existiriam mais acessos à base de dados do que os

necessários e seria necessário recorrer na mesma ao gestor, para decidir qual das cópias do

48

Page 69: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

objecto seria a utilizada.

6.5.3.2 Crash de clientes

Geralmente as caches são clientes de objectos locais e também de objectos remotos.

É necessário fazer a monitorização destes clientes. Durante o funcionamento normal do sis-

tema, a monitorização é feita como descrito na Secção 6.3. Contudo, os clientes estão sujeitos

a falhas. As falhas têm de ser detectadas, para que o número de clientes possa ser decre-

mentado. Se o decremento de clientes não fosse efectuado, um objecto que tivesse como

cliente, um cliente que falhou, nunca seria removido do sistema distribuído, a não ser que a

cache onde o objecto está alojado também falhasse.

Para fazer a detecção, recorreu-se explicitamente ao mecanismo de contagem de referên-

cias do Java RMI, utilizando a interface Unreferenced, tal como descrito na Secção 3.4.5.

Foram criados objectos que funcionam como identificadores de clientes. A título de exemplo,

considerem-se os objectos r1 e r2 da Figura 6.4b. Existe apenas uma única referência para

estes objectos, detida por cada um dos clientes de o1. O objecto é notificado quando deixa

de ser referenciado pelo cliente, sendo o número de clientes decrementado, caso não o tenha

sido da forma descrita na Secção 6.3.2.

O decremento por ocorrência de falhas é um complemento e não um substituto dos meca-

nismos descritos na Secção 6.3.2. O decremento por falha é mais lento, pois baseia-se nas

leases do Java RMI.

Não era possível utilizar uma técnica semelhante recorrendo somente às referências para

os objectos de cache, pois para além das referências dos clientes, existe também a referência

no gestor.

A Figura 6.7 ilustra a ocorrência da falha de um cliente. Inicialmente existem dois clientes

do objecto o1, um local (cache X) e outro remoto (cache Y).

Figura 6.7: Crash de um cliente e respectiva recuperação.

49

Page 70: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

A cache Y termina a sua execução de forma abrupta. O objecto r2 deixa de ser refe-

renciado. r2 é notificado pelo sistema RMI que já não é referenciado. r2 utiliza a referência

que tem para o1 para invocar o método unregisterClient. O número de clientes de o1 é

decrementado e o objecto r2 é removido do sistema distribuído pelo garbage collector.

6.5.3.3 Falha antes do registo de um objecto

É possível que uma cache falhe após ter recebido permissão para carregar o objecto

pretendido da base de dados, mas antes de o ter conseguido registar. A Figura 6.8 ilustra

esta situação, bem como a respectiva recuperação.

Figura 6.8: Falha antes do registo de um objecto.

A cache X faz o pedido de um objecto ao gestor. O objecto pretendido não está disponível,

sendo criado um lock no gestor. A cache X recebe informação que o objecto não está em uso

e que obtém permissão para o carregar da base de dados. Antes de efectuar o carregamento

do objecto, a cache X falha.

Entretanto, a cache Y faz um pedido ao gestor, para o mesmo objecto especificado pela

cache X. Devido à existência do lock, o gestor sabe que o objecto está a ser carregado, e por

isso bloqueia a thread responsável por processar o pedido da cache Y.

Como o lock não foi removido atempadamente numa operação de registo, ocorre timeout

e o lock é removido pelo gestor. O tratamento é feito pelo gestor, sem que a cache Y tenha

conhecimento da ocorrência da falha. É criado um novo lock e a cache Y recebe permissão

para carregar o objecto da base de dados.

50

Page 71: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

6.6 Sincronização e performance

A sincronização foi elaborada de forma a tentar manter a concorrência ao mais alto nível

possível. Este requisito é fundamental pois a cache faz parte de um sistema que corre em

máquinas multiprocessador. Neste caso podem não ser apenas processos que se encontram

à espera de ser escalonados, mas sim processadores que estão parados, a desperdiçar o seu

tempo de processamento.

Tendo em conta a performance, foram criados locks de leitura e locks de escrita. Estes

locks são utilizados no acesso à estrutura do gestor, onde estão as referências para os objec-

tos remotos. Desta forma apenas as operações de registo e remoção necessitam de fazer lock

de toda a estrutura, enquanto que pedidos de obtenção podem ser satisfeitos em simultâneo.

6.7 Integração com a cache local

A integração com a cache local foi efectuada da forma descrita nas secções anteriores

deste capítulo. Neste secção abordam-se apenas os aspectos mais relevantes, que não foram

considerados nas secções anteriores. Inicialmente as novas funcionalidades foram testadas

de forma isolada, para verificar o correcto funcionamento da solução.

Foi uma fase demorada, pois alguns detalhes passaram despercebidos na análise inicial

do código existente, tais como interacções entre outros serviços do servidor aplicacional.

Essas interacções tornaram necessária a serialização de alguns objectos.

A classe CachedObject foi estendida (DistributedCachedObject), passando a implemen-

tar uma interface remota, disponibilizando os métodos que inicialmente eram locais. Todos os

acessos directos a atributos (essencialmente bandeiras) passaram a ser efectuados através

de métodos, e disponibilizados pela interface remota.

Foram efectuadas alterações no método de sincronização do CachedObject com a base de

dados. Inicialmente acedia-se a cada um dos parâmetros do CachedObject para obter o seu

valor e de seguida procedia-se à actualização na base de dados. Se se mantivesse este com-

portamento haveria uma invocação remota para cada parâmetro existente no CachedObject.

A alteração efectuada consiste em invocar remotamente apenas o método de sincronização.

A sincronização propriamente dita passa a ser feita pela cache que detem o objecto.

6.8 Estatísticas

Foram introduzidos mecanismos que permitem a monitorização da utilização do sistema

distribuído de cache. A monitorização é feita pelo gestor. Existem contadores do número de

operações de get, register e unregister, assim como contadores de hit e miss.

Já existiam mecanismos idênticos para a utilização da cache centralizada. Manteve-se o

que já existia, considerando-se que apenas ocorre um miss no caso em que a cache obtém

permissão para carregar o objecto da base de dados, ou seja, caso um pedido seja satisfeito

por uma outra cache incrementa-se na mesma o contador de hit.

51

Page 72: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

6.9 Testes

Durante a execução de testes aos novos componentes introduzidos, tentou-se utilizar uma

abordagem estrutural. Neste tipo de abordagem são escolhidos casos de teste que exercitem,

idealmente, todo o código [8]. Apesar de se ter tentado seguir esta metodologia, não foi

elaborada uma bateria de testes.

Houve duas fazes de teste distintas: uma fase de testes aos novos componentes antes

da integração e outra após a integração. Na primeira fase de testes recorreu-se ao debugger

e a output que permitisse identificar a execução de determinados blocos do programa. O

recurso ao debugger foi essencial, pois permitiu através de execução passo-a-passo verificar

o correcto funcionamento dos mecanismo de lock, assim como os mecanismos desenvolvidos

para a tolerância a falhas.

Seguidamente passou-se à integração da solução com o sistema previamente existente

e iniciou-se a segunda fase de testes. Nesta fase os testes foram feitos essencialmente

com recurso à execução de fluxos de processo. Adicionalmente recorreu-se a output para

identificação da execução de determinados blocos, aos logs do servidor aplicacional e tabelas

da base de dados.

Foram testadas a leitura e escrita de parâmetros de CachedObjects, a operação de atribui-

ção (assign) e a operação de sincronização. Foram efectuados testes simples e em execução

concorrente sobre os mesmos dados. Verificou-se que os fluxos terminaram sem erro, que

os valores na base de dados estavam de acordo com o esperado numa execução correcta do

fluxo, e que acessos concorrentes aos mesmos dados utilizavam o valor da última modifica-

ção.

6.10 Escalabilidade da solução

A complexidade do sistema está dispersa pelas diversas caches. Estas caches actuam

como servidores e clientes de objectos remotos.

Admite-se que num ambiente de execução real a probabilidade de um objecto estar a

ser partilhado por caches distintas é baixa. No entanto não existem valores, pois o sistema

desenvolvido não chegou a ser testado nesta situação.

O bootleneck da solução apresentada é o gestor. O gestor tem de ser consultado cada

vez que existe um miss na cache local, e no momento da remoção do objecto do sistema

distribuído.

Apesar de não terem sido efectuados testes de escalabilidade, preve-se que o sistema

tenha um bom desempenho para um número pequeno de caches clientes, sendo a perfor-

mance degradada de forma aproximadamente linear à medida que o número de caches vai

aumentando.

É espectável que com o aumento de caches clientes, o número de objectos em utilização

pelas diversas caches aumente. Este aumento do número de clientes levará a um aumento

das comunicações entre as diversas caches, degradando a performance do sistema.

52

Page 73: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Capítulo 7

Melhoramentos na solução inicial

O principal problema da solução apresentada no Capítulo 6 é a possibilidade de ocorrência

de falhas no gestor. Admite-se que o gestor não pode falhar e sem ele o sistema distribuído é

encerrado.

Neste capítulo apresenta-se uma solução melhorada, que corrige o problema da falha do

gestor, assim como o problema do registo RMI. A possibilidade de ocorrência de uma falha

do sistema de gestão de base de dados não está directamente relacionada com o serviço de

cache, e pode ser colmatada com recurso a soluções de bases de dados replicadas, disponi-

bilizadas pela própria Oracle.

Utiliza-se a tecnologia de comunicação por grupos, para replicar a informação contida no

gestor e passa a utilizar-se o registo disponibilizado pelo Jgroup, em vez do registo RMI.

Relembrar que o registo RMI introduz um ponto de falha, e que o registo do Jgroup corrige

esse problema, tal como descrito na Secção 4.4.2.

7.1 Arquitectura da solução

A arquitectura em camadas da solução é a mesma que foi apresentada na Figura 6.1, no

entanto foram efectuadas alterações na camada distribuída. A Figura 7.1 representa a nova

arquitectura da camada de cache distribuída. A situação ilustrada é para o caso de existirem

dois servidores aplicacionais, mas podem existir mais, ou apenas um.

Figura 7.1: Arquitectura da camada de cache distribuída melhorada.

Cada servidor além de possuir uma cache, possuí também um gestor. Dos vários gestores

53

Page 74: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

que existem no sistema, apenas um (coordenador) atende pedidos das diversas caches. Os

outros gestores funcionam como backups.

Caso ocorra uma falha na máquina que aloja o coordenador, há uma eleição e é nomeado

um novo coordenador.

7.2 Eleição

Num sistema distribuído, um algoritmo utilizado para seleccionar um nó distinto, ou um

líder para coordenar alguma actividade é conhecido como algoritmo de eleição. Geralmente

não interessa qual o processo que desempenha esse papel, mas algum tem de o fazer.

A primeira ideia que surgiu, para a resolução do problema da falha do gestor, consistia na

implementação de um algoritmo de eleição. As diversas caches executariam o algoritmo e

aquela que vencesse a eleição criaria uma instância do gestor, que seria utilizada por todas

as caches. O algoritmo de eleição permitiria recuperar da falha do gestor.

Existem diversos algoritmos de eleição, sendo os mais conhecidos o Bully, proposto por

Garcia-Molina [14], e o Ring [12]. Relativamente a desenvolvimentos recentes, o artigo [9]

propõe novas abordagens, com recurso a métodos tolerantes a falhas, apresentando soluções

mais eficientes do que as inicialmente propostas.

Nos algoritmos referidos, cada vez que um novo processo inicia a execução ocorre uma

nova eleição. São utilizados identificadores para cada um dos participantes da eleição. Estes

identificadores definem prioridades. O participante com maior prioridade ganha a eleição.

O facto de poder ser eleito um novo coordenador, mesmo sem o anterior ter falhado, é

indesejável para este sistema. As referências já registadas teriam de ser transferidas para o

novo coordenador, ou invalidadas e obtidas novamente.

Tentou-se alterar o algoritmo Ring para que este comportamento não se verificasse, mas

sem sucesso. A tentativa foi feita com o Ring e não com o Bully, pois no primeiro uma eleição

ocorre com recurso a 2(n− 1) mensagens (caso a falha não seja detectada concorrentemente

por diversos processos), enquanto que o segundo necessita de n − 2 mensagens, no melhor

caso, mas no pior caso tem complexidade O(n2) (situação em que é o processo com menor

prioridade que inicia a eleição).

Com o objectivo de encontrar um algoritmo de eleição que não apresentasse esta ca-

racterística, iniciou-se uma longa pesquisa. Foram analisados dezenas de algoritmos, até

encontrar um que satisfaz a propriedade de estabilidade. Segundo [1], um algoritmo de elei-

ção é estável se garantir que uma vez eleito o coordenador, este permanece coordenador

enquanto não falhar e as suas ligações estiverem operacionais.

O documento [1] apresenta dois algoritmos distintos: um onde pode ocorrer perda de

mensagens e outro onde as perdas não podem ocorrer. Visto que se optou pelo RMI para

a troca de mensagens, a comunicação é feita com recurso ao protocolo TCP. Uma vez que

a camada de transporte já oferece garantias de entrega, optou-se pela implementação do

protocolo que admite que a comunicação é fiável.

O pseudo-código encontra-se representado na Figura 7.21. O símbolo ⊥ identifica uma

situação de ausência de coordenador, p é o identificador do processo, n é o número de pro-

cessos que podem executar o algoritmo, r é o número da ronda, s e k são números de novas

1Figura 2 de [1]

54

Page 75: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

rondas, δ é um parâmetro que permite especificar o tempo entre o envio de mensagens por

parte do coordenador/candidato.

Figura 7.2: Algoritmo de eleição.

A execução é feita por rondas r = 0, 1, 2, .... Para iniciar a ronda s, um processo envia

(START, s) para um processo específico, designado "coordenador da ronda s", correspon-

dendo ao processo s mod n. Coloca a variável que identifica a ronda (r ) com o valor s, des-

promove o coordenador anterior e reinicia o temporizador.

De δ em δ tempo, o processo verifica se é o coordenador da ronda e em caso afirmativo

envia (OK, r) para todos os processos (tarefa 0).

Quando um processo recebe (OK, k) para a ronda actual (k = r), reinicia o temporizador e,

caso não haja coordenador e esta seja a segunda mensagem de (OK, k) que recebe, elege

como coordenador o processo k mod n.

Se o valor do temporizador for superior a 2δ, ocorreu um timeout por parte do coorde-

nador/candidato actual. Envia uma mensagens de (STOP, r) para o coordenador/candidato

actual e inicia a ronda seguinte. Este timeout é o mecanismo que permite identificar a ocor-

rência de uma falha no coordenador/candidato.

Se um processo receber uma mensagem de (STOP, k), com um valor da ronda igual ou

superior ao actual, abdica da candidatura/coordenação e inicia a ronda k + 1.

Ao receber uma mensagem (OK, k) ou (START, k), com um valor da ronda superior ao

actual, inicia a ronda k.

É importante uma escolha adequada do parâmetro δ.

Um valor muito pequeno de δ pode levar à tomada de decisões prematuras, e faz com que

o número de mensagens enviadas pelo coordenador seja grande. Um valor grande para δ

55

Page 76: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

leva a um tempo da eleição grande, o que pode fazer com que algumas tarefas do servidor

se encontrem suspensas por um longo período de tempo. É necessário ter em consideração

este compromisso.

Admitindo que raramente ocorre uma falha de um servidor, o valor de δ pode ser elevado.

Foram efectuados testes para um valor de 5 segundos, com resultados satisfatórios. Para

valores próximos de 2 segundos verificou-se que em situações de elevada carga do servidor

eram tomadas decisões precipitadas.

Relativamente à implementação, como referido a comunicação é feita por RMI, via

Jgroup, ou seja, utiliza-se o Jgroup sem recurso à comunicação por grupos.

Não foram criados grupos de comunicação, pois na execução do algoritmo, parte das

mensagens tem de ser entregue apenas a um dos processos. Ao optar pela comunicação

por grupos seria necessário recorrer ainda a comunicação unicast. Além disso, as garantias

que o sincronismo virtual oferece (entrega a todos os processos do grupo, ou a nenhum) não

são um requisito para o algoritmo de eleição, e assim não se sobrecarrega o sistema com

funcionalidades desnecessárias.

7.3 Replicação

A existência de um algoritmo de eleição, permite ao sistema recuperar das falhas do ges-

tor, tal como descrito na Secção 7.2. Contudo, caso ocorresse uma falha no gestor seria

necessário voltar a registar todos os objectos em uso pelas caches, ou descartar as referên-

cias actuais e re-obter as referências.

Este podia ser um processo demorado, que teria de interromper momentaneamente a

execução de todos os servidores, para garantir a consistência do sistema.

A solução adoptada foi a replicação da informação do gestor. Cada cache possui uma

instância do gestor. Existe apenas um gestor que é utilizado (coordenador), funcionando os

outros como backups. Quando o coordenador falha, existe uma eleição e o novo coordena-

dor fica imediatamente disponível para desempenhar as suas funções. Desta forma, não é

necessário invalidar as referências para objectos em utilização pelas caches, nem registar as

referências já existentes, pois o novo coordenador já tem uma visão consistente do sistema

distribuído.

A única operação efectuada no gestor que não implica a invocação de métodos no grupo,

do qual os backups fazem parte, é a operação de get, para o caso em que já existe uma

referência remota no gestor. Em todas as outras operações a modificação (inserção/remoção)

de referências foi isolada, sendo executada de forma síncrona, em todos os elementos do

grupo.

Poderia pensar-se que seria possível a cada uma das caches efectuar a operação sobre o

gestor alojado no seu espaço de endereçamento, sendo a operação propagada pelas diversas

caches, com recurso à comunicação por grupos. Contudo, aproveitando o que estava feito da

solução inicial, isso não é possível. O seguinte exemplo ilustra essa impossibilidade.

Duas caches estão a tentar obter uma referência para o mesmo objecto. O objecto preten-

dido não se encontra em cache. Em ambos os gestores é adquirido o lock de escrita. Ambos

os gestores instalam um objecto de lock no grupo, perdendo-se o primeiro que foi instalado.

56

Page 77: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Ambas as caches adquirem o objecto da base de dados, e ambas fazem o registo, que é pro-

pagado pelo grupo de servidores. A partir deste momento o sistema encontra-se num estado

inconsistente, pois as caches possuem objectos distintos, enquanto uma deveria possuir o

objecto e outra uma referência. Neste caso, seria necessário implementar um mecanismo de

lock distribuído. Uma vez que já existia um algoritmo de eleição implementado, optou-se por

contornar esses problemas utilizando o coordenador.

7.4 Tolerância a falhas

Visto que apenas houve alterações na interacção com o gestor, apenas esses aspectos

serão referidos nesta secção, além da introdução do algoritmo de eleição. Os restantes cená-

rios de falha já foram abordados na Secção 6.5.

7.4.1 Falha do coordenador após a invocação de um método por partede uma cache cliente

O coordenador pode falhar após a realização da operação no grupo e antes de efectuar o

retorno para o cliente. O cliente não tem forma de saber se a operação foi realmente efectuada

ou não. Após a falha do coordenador há uma nova eleição. É necessário garantir que a

operação é efectuada, para que o sistema distribuído se mantenha num estado consistente.

Uma operação de get pode ser efectuada mais do que uma vez, desde que não sejam

guardadas as referências que identificam a cache como cliente do objecto. Visto que a opera-

ção apenas é repetida caso haja uma falha do gestor, antes do retorno para o cliente, o cliente

não tem forma de guardar a referência de cliente, logo este problema não se põe. Caso a co-

ordenador falhe e a operação tenha sido efectuada, o que sucede é que vão ocorrer dois

ou mais registos de novos clientes do objecto (dependendo do número de vezes consecuti-

vas que o coordenador falhou). Momentaneamente o número de clientes do objecto estará

incorrecto, mas será reposto após a expiração das leases do RMI.

Caso o objecto ainda não estivesse em utilização, a repetição da operação de get levaria

a um bloqueio no lock, significando que o objecto estaria a ser carregado. Visto que a cache

não chegou a obter permissão para carregar o objecto e está a repetir a operação, ocorrerá

um timeout e reinicia-se o processo de obtenção de referência.

A operação de register também pode ser efectuada várias vezes. O problema que po-

deria existir com um novo registo de cliente é exactamente o mesmo que para a operação

get, logo não há problema. Caso o objecto tenha sido registado anteriormente, o registo não

volta a ser efectuado e a cache que invocou o método recebe uma referência para o objecto

previamente registado.

Por fim, a operação unregister também pode ser efectuada várias vezes, pois a refe-

rência só é removida do gestor, caso a chave e a referência especificada coincidam com as

armazenadas no gestor.

Resumindo, qualquer uma das operações sobre o gestor pode ser efectuada mais do que

uma vez, desde que não se guardem as referências relativas ao registo de clientes.

Quando uma cache se apercebe que ocorreu uma falha no coordenador, todas as opera-

ções que necessitem da sua intervenção são suspensas. As operações são retomadas após

a notificação do fim da eleição.

57

Page 78: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

7.4.2 Falhas relacionadas com o algoritmo de eleição

Um dos aspectos mais importantes é a manutenção da consistência do sistema distribuído.

É necessário garantir que os processos que participam na eleição estão em condições de ser

eleitos.

Um processo antes de iniciar o algoritmo de eleição tem de tentar sincronizar o seu estado

com o coordenador, caso este exista. Esta verificação aparentemente simples, revelou-se

bastante complexa devido à possibilidade de ocorrência de algumas situações. Antes de mais,

relembrar que um processo apenas é eleito coordenador após o envio de duas mensagens

de OK, sendo ele o coordenador da ronda.

A título de exemplo considere-se a seguinte situação. Dois processos (com identificadores

1 e 2) estão a executar o algoritmo de eleição, não existindo ainda coordenador. O coorde-

nador da ronda é o processo 2, tendo já enviado a primeira mensagem de OK para todos os

processos. Entretanto, um novo processo (com identificador 0) inicia a sua execução, verifi-

cando se existe coordenador. Como não existe, começa a executar o algoritmo de eleição. O

processo 2 envia para todos a segunda mensagem de OK, tornando-se no coordenador para

os processos 1 e 2. No entanto ainda não é visto como coordenador pelo processo 0, pois

este recebeu apenas uma mensagem de OK. O processo 1 sincroniza-se com o coordenador

e passa a receber todas as actualizações. São efectuadas operações sobre o coordenador,

recebendo o processo 1 as respectivas actualizações. O coordenador falha antes de enviar

uma nova mensagem de OK, ou seja, o processo 0 nunca chega a ver o processo 2 como

coordenador. O processo 1 detecta a falha do coordenador e o processo 0 detecta a falha

do coordenador da ronda. Inicia-se uma nova ronda, sendo o processo 0 o coordenador da

ronda. O processo 0 ganha a eleição, mesmo existindo uma réplica do coordenador que

falhou. O sistema distribuído fica num estado inconsistente.

Para resolver o problema recorreu-se ao Jgroup com uma interface externa de comuni-

cação em anycast. Um processo após a sincronização com o gestor e antes de se juntar

ao grupo de actualizações, coloca uma referência no registo. Visto que todos os processos

executam esta operação, no registo fica uma referência para um grupo. Um processo que se

encontra na situação descrita no parágrafo anterior, antes de enviar a segunda mensagem de

OK invoca um método nesta nova interface, que terminará com sucesso caso exista uma ré-

plica em condições de ser eleita. A invocação do método é uma espécie de operação de ping.

Caso termine com sucesso o coordenador da ronda suprime o envio da segunda mensagem

de OK, abdicando da sua oportunidade de se tornar coordenador, e mantendo a consistência

do sistema distribuído.

7.4.3 Falha durante a sincronização com o coordenador

Para minimizar os efeitos da ocorrência de falhas e garantir que a junção ao grupo é

efectuada no momento correcto, a sincronização com o coordenador é feita através de um

método de callback.

Um gestor que se queira sincronizar com o coordenador invoca o método

syncWithCoordinator do coordenador, especificando a sua própria referência remota. O

coordenador faz um lock da estrutura de referências em modo de leitura, e cria um array com

todas as referências e as respectivas chaves. Recorrendo à referência previamente especifi-

cada, o coordenador invoca o método de callback syncReplica, passando por valor o array

58

Page 79: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

das chaves e as estatísticas actuais da utilização da cache.

O gestor que pretende tornar-se uma réplica guarda os valores que lhe foram passados

pelo coordenador, coloca no registo a sua referência do "‘grupo de ping"’ e junta-se ao grupo

de actualizações.

O coordenador liberta o lock de leitura e a invocação do método syncWithCoordinator

retorna.

Assim garante-se que o coordenador apenas altera o seu estado depois da réplica estar

sincronizada e pronta para receber novas actualizações.

Se ocorrer uma falha na invocação do método syncWithCoordinator, a réplica (ou aspi-

rante a réplica) tem informação local suficiente para saber se a sincronização já foi feita, ou

se é necessário invocar novamente o método quando for eleito um novo coordenador.

Pode parecer que existe um caso de falha que pode colocar o sistema distribuído num

estado inconsistente, que está a ser deixado de fora.

O que é que acontece se uma nova cache inicia a sua execução e o coordenador falha

antes de ocorrer sincronização? Como já foi referido na Secção 7.4.2, se existirem réplicas

uma delas será eleita coordenadora, mas o que acontece se não existirem réplicas ou todas

elas falharem?

A resposta é simples. Uma cache só pode obter/registar referências após conhecer o

coordenador. Isto implica que se o coordenador falhou e nenhuma das réplicas tomou o seu

lugar é porque todas as caches que poderiam deter referências falharam. Assim sendo, não

existem referências para invalidar e o sistema encontra-se num estado consistente.

7.5 Testes

Relativamente a testes foram efectuados os referidos na Secção 6.9, tendo novamente

dividido os testes em duas fases distintas.

59

Page 80: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção
Page 81: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Capítulo 8

Conclusões e trabalhos futuros

8.1 Conclusões

Esta dissertação explana os passos e as decisões tomadas durante o desenvolvimento e

implementação de uma solução distribuída de cache de dados com estrutura hierárquica.

Começa-se por introduzir os conceitos básicos para a compreensão do funcionamento de

um sistema distribuído, bem como os problemas associados a este tipo de sistemas.

Referem-se alguns artigos relacionados com caches distribuídas. Apresentam-se vários

produtos que oferecem funcionalidades de cache distribuída. A maioria dos produtos apre-

sentados são de código aberto, e estão sujeitos a licenças que permitem a sua integração em

produtos comerciais. Explicitam-se as suas principais características, com especial ênfase

nas vantagens e inconvenientes de cada um.

Verifica-se que a documentação relativa ao funcionamento e detalhes de implementação

dos diversos produtos encontrados é insuficiente para perceber de forma exacta o seu fun-

cionamento. Além disso, os produtos geralmente estão acompanhados de funcionalidades

adicionais, desnecessárias para este caso específico. Assim, optou-se por desenvolver uma

solução à medida, que interferisse apenas nos pontos estritamente necessários com a solu-

ção centralizada existente.

Descrevem-se as funcionalidades e a arquitectura do serviço de cache centralizado, que

serviu como ponto de partida para o desenvolvimento deste trabalho.

A fase inicial de desenvolvimento centrou-se no criação de uma solução simples e de

fácil implementação. No entanto, a solução não surgiu de uma forma tão simples quanto

o desejado, pois é necessário garantir que o sistema distribuído se encontra num estado

consistente, e que continua a sua execução mesmo quando alguns dos elementos falham.

Esta solução permite a resolução do problema, assumindo a existência de um processo

que não falha (gestor). O servidor que aloja o gestor tem de ser o primeiro a iniciar, e caso

um dos outros servidores detecte a sua falha termina a execução. Além disso, o registo RMI

introduzia um ponto de falha adicional, mas que podia ser minimizado, caso a sua instância

fosse iniciada pelo servidor que se admitia que não podia falhar.

Visto que a solução inicial assumia que o gestor não podia falhar, foi necessário efectuar

alguns melhoramentos para puder remover esta limitação. Para resolver o problema do ponto

61

Page 82: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

de falha implementou-se um algoritmo de eleição. O algoritmo de eleição permite que no caso

de ocorrência de uma falha no gestor haja eleição de um novo gestor.

Na grande maioria dos algoritmos de eleição existentes, ocorre uma eleição cada vez que

um novo processo inicia o algoritmo. Este comportamento é indesejável, pois poderia ser

nomeado um novo gestor e seria necessário invalidar ou voltar a registar todas as referências

para objectos já existentes. Após uma intensa pesquisa encontrou-se um algoritmo que sa-

tisfaz a propriedade de estabilidade, segundo a qual apenas ocorre uma nova eleição caso o

actual gestor falhe.

Concluiu-se que apesar da existência de um algoritmo de eleição estável, caso ocorresse

uma falha do gestor seria necessário invalidar ou voltar a registar as referências para objectos

já existentes, pois não existiam cópias de segurança. Optou-se por contornar o problema,

pois caso o número de objectos existentes no sistema distribuído fosse elevado a recuperação

seria lenta. Recorreu-se à biblioteca de comunicação por grupos Jgroup. Esta biblioteca foi

utilizada para replicar a informação do gestor. Continua a ser utilizado apenas um gestor

(coordenador), enquanto que as réplicas têm função apenas de backup. Caso o coordenador

falhe, qualquer uma das outras réplicas está em condições de assumir a coordenação.

Esta solução permite que o sistema esteja disponível desde que um qualquer servidor

esteja em execução. Poderá assim dizer-se que os objectivos propostos foram cumpridos.

8.2 Trabalhos futuros

A solução apresentada não é óptima, existindo certamente alguns aspectos que ainda

podem ser melhorados. Um desses aspectos prende-se com a criação de cópias locais.

Estima-se que mais de 80% das operações sobre CachedObjects seja de leitura. Uma

das formas de aumentar o desempenho do sistema seria utilizar uma cópia do objecto, em

vez de uma referência remota. Todos os pedidos de leitura seriam resolvidos localmente. Os

pedidos de escrita seriam redireccionados para a cache que obteve permissão para carregar

o objecto da base de dados, e as alterações seriam propagadas para todas as cópias. Pode

ser aproveitado o mecanismo de registo de clientes para passar uma referência de callback,

permitindo uma posterior propagação das actualizações.

Não existe a necessidade de recorrer à comunicação por grupos para a propagação das

actualizações. Do ponto de vista da consistência é suficiente que alterações sobre um mesmo

objecto sejam vistas pela mesma ordem. Elimina-se assim a necessidade da passagem de

todos os pedidos por um gestor centralizado, para fazer a serialização dos pedidos. Quer a

utilização de comunicação por grupos quer a serialização de todos os pedidos iriam compro-

meter a escalabilidade do sistema.

Este tipo de solução levaria a uma implementação do protocolo primary-backup que for-

nece uma implementação de consistência sequencial, visto que o primário (cache que carre-

gou o objecto da base de dados) pode ordenar todo os pedidos de escrita. Evidentemente

que todos os processos vêem todos as operações de escrita pela mesma ordem, independen-

temente da cópia que utilizam para as operações de leitura. De notar ainda que utilizando um

protocolo bloqueante, os processos vão sempre ver os efeitos da sua escrita mais recente.

62

Page 83: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Apêndice

Page 84: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção
Page 85: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Bibliografia

[1] M. K. Aguilera, C. Delporte-Gallet, H. Fauconnier, and S. Toueg. Stable leader election.

In DISC ’01: Proceedings of the 15th International Conference on Distributed Computing,

pages 108–122, London, UK, 2001. Springer-Verlag.

[2] K. Arnold and J. Gosling. The Java Programming Language. Addison-Wesley, Third

edition, 2000.

[3] O. Babaoglu, B. E. Helvik, H. Meling, A. Montresor, and H. Kolltveit. Página web do The

Jgroup Project - http://jgroup.sourceforge.net.

[4] K. Birman and T. Joseph. Exploiting virtual synchrony in distributed systems. In SOSP ’87:

Proceedings of the eleventh ACM Symposium on Operating systems principles, pages

123–138, New York, NY, USA, 1987. ACM Press.

[5] A. Birrell, D. Evers, G. Nelson, S. Owicki, and E. Wobber. Distributed garbage collection

for network objects. Technical Report 116, Digital, Systems Research Center, 130 Lytton

Avenue, Palo Alto, CA 94301, 1993.

[6] Y. Cardenas, J.-M. Pierson, and L. Brunie. Uniform Distributed Cache Service for Grid

Computing. In IEEE, editor, In 16th DEXA: In 2th International Workshop on Grid and

Peer-to-Peer Computing Impacts on Large Scale Hereogeneous Distributed Database

Systems., pages 351–355. IEEE Computer Society, Aug. 2005.

[7] W. Consulting. Desenho da Solução Activis - WDS03NOV01900, 2004.

[8] R. G. Crespo. Transparências da disciplina de software de telecomunicações - introdução

aos testes, 2007.

[9] M. EffatParvar, M. Effatparvar, A. Bemana, and M. Dehghan. Determining a central con-

trolling processor with fault tolerant method in distributed system. In ITNG ’07: Pro-

ceedings of the International Conference on Information Technology, pages 658–663,

Washington, DC, USA, 2007. IEEE Computer Society.

[10] S. Floyd, V. Jacobson, C.-G. Liu, S. McCanne, and L. Zhang. A reliable multicast fra-

mework for light-weight sessions and application level framing. IEEE/ACM Transactions

on Networking, 5(6):784–803, 1997.

[11] T. A. S. Foundation. The Apache Software License, Version 2.0 -

http://www.apache.org/licenses/LICENSE-2.0, 2004.

[12] G. N. Frederickson and N. A. Lynch. Electing a leader in a synchronous ring. J. ACM,

34(1):98–115, 1987.

65

Page 86: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

[13] I. Free Software Foundation. GNU Lesser General Public License -

http://www.gnu.org/licenses/lgpl-2.1.txt, 1999.

[14] H. Garcia-Molina. Elections in a distributed computing system. IEEE Trans. Comput.,

C-31(1):48–59, Jan. 1982.

[15] H. W. Holbrook, S. K. Singhal, and D. R. Cheriton. Log-based receiver-reliable multicast

for distributed interactive simulation. In SIGCOMM, pages 328–341, 1995.

[16] java.net Expert Group. JSR 107 - Java Caching API Home - https://jsr107.dev.java.net/.

[17] JBoss. JBoss Cache - http://labs.jboss.com/jbosscache/.

[18] F. Karlstrom and P. Fajeau. FKache Open Source - http://jcache.sourceforge.net.

[19] J. C. Lin and S. Paul. RMTP: A reliable multicast transport protocol. In INFOCOM, pages

1414–1424, San Francisco, CA, Mar. 1996.

[20] J. Markoff and S. Hansell. Hiding in plain sight, google seeks more power. New York

Times, June 2006.

[21] A. Montresor. Jgroup tutorial and programmer’s manual, 2000.

[22] A. Montresor. System Support for Programming Object-Oriented Dependable Applicati-

ons in Partitionable Systems. PhD thesis, University of Bologna, Italy, Mar. 2000. Techni-

cal Report UBLCS-2000-10.

[23] K. Obraczka. Multicast transport protocols: a survey and taxonomy, 1998.

[24] Opensymphony. OSCache - http://www.opensymphony.com/oscache/.

[25] O. S. I. OSI. The BSD License - http://www.opensource.org/licenses/bsd-license.php.

[26] P. Prabhakar. Implementation and comparison of distributed caching schemes. In ICON

’00: Proceedings of the 8th IEEE International Conference on Networks, page 491,

Washington, DC, USA, 2000. IEEE Computer Society.

[27] S. C. Rhea. The Bamboo Distributed Hash Table - A Robust, Open-Source DHT -

http://bamboo-dht.org/.

[28] L. Rizzo and L. Vicisano. Replacement policies for a proxy cache. IEEE/ACM Trans.

Netw., 8(2):158–170, 2000.

[29] P. Rodriguez, C. Spanner, and E. Biersack. Analysis of web caching architectures: Hie-

rarchical and distributed caching, 2001.

[30] G. Seshadri. Tutorials & code camps - jguru: Remote method invocation (rmi). web, 2000.

[31] ShiftOne. ShiftOne Java Object Cache - http://jocache.sourceforge.net/.

[32] A. Silberschatz, H. F. Korth, and S. Sudarshan. Database System Concepts. McGraw-

Hill, Inc., New York, NY, USA, Fifth edition, 2005.

[33] T. D. G. Solutions. Tangosol Coherence - http://www.tangosol.com/coherence-

overview.jsp.

66

Page 87: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

[34] M. Stumm and S. Zhou. Algorithms implementing distributed shared memory. Computer,

23(5):54–64, 1990.

[35] I. Sun Microsystems. Java Remote Method Invocation Specification - Java 2 SDK, Stan-

dard Edition, v1.5.0, 2004.

[36] A. S. Tanenbaum. Modern Operating Systems. Prentice Hall PTR, Upper Saddle River,

NJ, USA, Second edition, 2001.

[37] A. S. Tanenbaum and M. van Steen. Distributed Systems, Principles and Paradigms.

Prentice-Hall, 2002.

[38] T. Tay and Y. Zhang. Peer-distributed web caching with incremental update scheme. IEE

proceedings. Communications, 152(3):327–334, 2005.

[39] E. P. Team. EHCache Home - http://ehcache.sourceforge.net/.

[40] O. Theel and M. Pizka. Distributed caching and replication. In HICSS ’99: Proceedings of

the Thirty-second Annual Hawaii International Conference on System Sciences-Volume

8, Washington, DC, USA, 1999. IEEE Computer Society.

[41] B. Wang. Performance we should expect from PojoCache -

http://wiki.jboss.org/wiki/Wiki.jsp?page=WhatShouldWeExpectOfThePojoCachePerformance,

2006.

[42] J. Watkinson. SwarmCache - Cluster-aware Caching for Java -

http://swarmcache.sourceforge.net/.

[43] Wikipedia. Aspect-Oriented Programming - http://en.wikipedia.org/wiki/Aspect-

oriented_programming.

[44] Wikipedia. Cache - http://en.wikipedia.org/wiki/Cache.

[45] X. Xu, A. Myers, H. Zhang, and R. Yavatkar. Resilient multicast support for continuous-

media applications, 1997.

[46] R. Yavatkar, J. Griffoen, and M. Sudan. A reliable dissemination protocol for interactive

collaborative applications. In ACM Multimedia, pages 333–344, 1995.

[47] J. Zola. Cali, efficient library for cache implementation. In ENC ’04: Proceedings of the

Fifth Mexican International Conference in Computer Science (ENC’04), pages 415–420,

Washington, DC, USA, 2004. IEEE Computer Society.

67

Page 88: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção
Page 89: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Apêndice A

Interacções entre o serviço decache e os restantes serviços doservidor aplicacional

A Figura A.1 ilustra a arquitectura do serviço de cache inicial, tendo em consideração as

interacções entre os restantes serviços.

69

Page 90: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Figura A.1: Arquitectura do serviço de cache incluindo interacções com restantes serviços.

70

Page 91: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Apêndice B

Diagramas UML da soluçãomelhorada

As Figuras B.1 e B.2 apresentam os diagramas UML de classes, da camada distribuída da

solução melhorada.

71

Page 92: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Figura B.1: Diagrama de classes - Parte 1/2.

72

Page 93: Definição e implementação de uma solução distribuída de ... · Resumo Hoje em dia, cada vez mais, os sistemas distribuídos são uma opção a considerar. A compra e manutenção

Figura B.2: Diagrama de classes - Parte 2/2.

73