Python para Desenvolvedores

2ª edição, revisada e ampliada

Capítulo 38: Processamento distribuído


Geralmente a solução para problemas que requerem muita potência computacional é a utilização de máquinas mais poderosas, porém esta solução é limitada em termos de escalabilidade. Uma alternativa é dividir os processos da aplicação entre várias máquinas que se comunicam através de uma rede, formando um cluster ou um grid.

A diferença básica entre cluster e grid é que o primeiro tem como premissa de projeto ser um ambiente controlado, homogêneo e previsível, enquanto o segundo é geralmente heterogêneo, não controlado e imprevisível. Um cluster é um ambiente planejado especificamente para processamento distribuído, com máquinas dedicadas em um lugar adequado. Um grid se caracteriza pelo uso de estações de trabalho que podem estar em qualquer lugar.

Processamento distribuido

Os modelos mais comuns de cluster:

  • computacional.
  • de recursos.
  • de aplicação ou híbrido.

O modelo computacional tem como objetivo usar processadores e memória dos equipamentos envolvidos para obter mais potência computacional. A implementação geralmente utiliza um sistema escalonador de filas (metascheduler), que realiza o agendamento das tarefas a serem processadas pelos nós (máquinas que compõem o modelo), com isso a operação tende a ser contínua, com interação reduzida com os usuários. Um exemplo conhecido é o [email protected].

Processamento distribuido

O cluster de recursos é usado para armazenar informações em um grupo de computadores, tanto para obter mais performance de recuperação de dados quanto para expandir a capacidade de armazenamento. Este modelo pode ser usado para prover infra-estrutura para aplicações ou para atender requisições feitas de forma interativa por usuários. Entre os serviços que podem operar desta forma estão os Sistemas Gerenciadores de Banco de Dados (SGBD), como o MySQL Cluster.

O modelo híbrido é uma aplicação projetada especificamente para funcionar em várias máquinas ao mesmo tempo. Ao invés de prover recursos diretamente, a aplicação utiliza os equipamentos para suportar suas próprias funcionalidades. Com isso, a infra-estrutura é utilizada de forma quase transparente pelos usuários que usam a aplicação interativamente. Todos os nós rodam o aplicativo e podem operar como servidores e clientes. O exemplo mais comum de arquitetura híbrida são os sistemas de compartilhamento de arquivos (file sharing) que usam comunicação Peer To Peer (P2P).

Independente do modelo utilizado. sistemas distribuídos devem atender a quatro requisitos básicos: Comunicação: as máquinas envolvidas devem se comunicar de forma a permitir a troca de informações entre elas. Metadados: os dados sobre o processamento precisam ser mantidos de forma adequada. Controle: os processos devem ser gerenciados e monitorados. Segurança: o sigilo, integridade e disponibilidade devem estar protegidos.

Existem diversas tecnologias voltadas para o desenvolvimento de aplicações distribuídas, tais como: XML-RPC, Web Services, objetos distribuídos, MPI e outras.

Objetos distribuídos

A premissa básica da tecnologia de objetos distribuídos é tornar objetos disponíveis para que seus métodos possam ser evocados remotamente a partir de outras máquinas ou mesmo por outros processos na mesma máquina, usando a pilha de protocolos de rede TCP/IP para isso.

Processamento distribuido

Existem diversas soluções para estes casos, porém utilizar objetos distribuídos oferece várias vantagens em relação a outras soluções que implementam funcionalidades semelhantes, tal como o protocolo XML-RPC:

  • Simplicidade para implementação.
  • Oculta as camadas de comunicação.
  • Suporte a estruturas de dados nativas (contanto que sejam serializáveis).
  • Boa performance.
  • Maturidade da solução.

PYthon Remote Objects (PYRO) é um framework para aplicações distribuídas que permite publicar objetos via TCP/IP. Na máquina servidora, o PYRO publica o objeto, cuidando de detalhes como: protocolo, controle de sessão, autenticação, controle de concorrência e outros.

Exemplo de servidor:

In [ ]:
import Pyro.core

# A classe Pyro.core.ObjBase define
# o comportamento dos objetos distribuídos
class Dist(Pyro.core.ObjBase):

    def calc(self, n):

        return n**n

if __name__ == '__main__':

    # Inicia a thread do servidor
    Pyro.core.initServer()

    # Cria o servidor
    daemon = Pyro.core.Daemon()

    # Publica o objeto
    uri = daemon.connect(Dist(),'dist')

    # Coloca o servidor em estado operacional
    daemon.requestLoop()

Na máquina cliente, o programa usa o PYRO para evocar rotinas do servidor e recebe os resultados, da mesma forma que um método de um objeto local.

Exemplo de cliente:

In [ ]:
import Pyro.core

# Cria um objeto local para acessar o objeto remoto
proxy = Pyro.core.getProxyForURI('PYROLOC://127.0.0.1/dist')

# Evoca um método do objeto remoto
print proxy.calc(1000)

Os métodos publicados através do PYRO não podem ser identificados por introspecção pelo cliente.

Embora o PYRO resolva problemas de concorrência de comunicação com os clientes que estão acessando o mesmo servidor (cada conexão roda em uma thread separada), fica por conta do desenvolvedor (ou de outros frameworks que a aplicação utilize) resolver questões de concorrência por outros recursos, como arquivos ou conexões de banco de dados, por exemplo. É possível autenticar as conexões através da criação de objetos da classe Validator, que podem verificar credenciais, endereços IP e outros itens.

In [1]:
 
Out[1]: