Configuração avançada

Normalmente o Zope roda numa configuração monolítica (chamada instância) que utiliza um só processo do sistema operacional. O desempenho deste processo está determinado por vários fatores e pode ser ajustado utilizando alguns parâmetros de configuração que variam de site em site:

  • Global Interpreter Lock: o Python é uma linguagem interpretada que utiliza um mecanismo conhecido como Global Interpreter Lock (GIL) para sincronizar a execução de threads em código que não se considera seguro para ser executado de forma concorrente; o GIL não é um problema por si mesmo, mas ele tem um comportamento que afeta o desempenho de código Python rodando em sistemas com vários processadores (a configuração típica na atualidade).
  • threads: uma instância pode responder várias requisições de forma simultânea (2, por padrão); se nenhuma thread está disponível, a requisição é colocada em uma fila; se uma thread é utilizada durante muito tempo pode ficar bloqueada deixando o site inacessível.
  • conexões com o banco de dados e seu cache: quando uma thread está atendendo uma requisição ela precisa estabelecer uma conexão com o banco de dados; essa conexão fica bloqueada e ninguém pode usá-la até ser liberada; por padrão o Zope pode abrir até 7 conexões entre a aplicação e o banco de dados.
  • cache em memória: cada conexão tem seu próprio cache em memória e esse cache pode armazenar um número determinado de objetos (30.000, por padrão); os objetos são entregues direitamente desse cache se eles se encontram lá; caso contrário, a conexão solicita ao banco de dados, deserealiza e armazena no cache; este processo é custoso em termos de utilização de CPU.
  • número de instâncias e memória: a execução de várias instâncias em paralelo não tem um impacto considerável na utilização total da memória.

Para ajustar a performance de uma instância Zope deve prestar especial atenção às seguintes diretivas:

  • zodb-cache-size: número de objetos que o cache da ZODB tentará manter na memória; o valor padrão (30.000) pode resultar pequeno em portais com muito conteúdo; o valor recomendado pode ser calculado com base no número de objetos totais armazenados.
  • zserver-threads: número de threads que o servidor ZServer usará para responder requests; o valor padrão (2) é o ótimo na maioria dos casos e se considera uma má prática colocar um valor de 1.

Mais informação na documentação do pacote plone.recipe.zope2instance.

Escalabilidade

A escalabilidade pode ser definida como a capacidade de um sistema de se ajustar a um aumento em sua utilização.

Como descrito anteriormente, uma instância do Zope pode responder várias requisições de forma simultânea. Porém, essa configuração as vezes não é recomendada em ambientes de produção por se resultar mais lenta que uma configuração que utiliza várias instâncias em paralelo.

Para poder compartilhar o banco de dados entre mais de uma instância Zope foi desenvolvido o Zope Enterprise Objects (ZEO). O ZEO substitui o storage normal do banco de dados por uma estrutura que utiliza uma arquitetura cliente/servidor na rede. O ZEO permite escalar com facilidade qualquer instalação de Zope simplesmente adicionando mais instâncias para permitir atender mais clientes de forma concorrente. O ZEO também é útil para rodar processos de manutenção do banco de dados e para debugar problemas em ambientes de produção.

Porém, toda essa flexibilidade vem com um custo: escrever no banco de dados é mais lento quando utilizamos ZEO.

Uma configuração típica do ZEO possui no mínimo as seguintes partes e diretivas:

[buildout]
...
parts +=
    ...
    zeoserver

[zeoserver]
recipe = plone.recipe.zeoserver
zeo-address = 8100

[instance]
...
shared-blob = on
zeo-address = 127.0.0.1:8100
zeo-client = on

A diretiva zeo-address indica a porta que será usada no servidor ZEO, ou uma combinação de endereço IP e porta no caso dos clientes ZEO. O valor da diretiva shared-blob depende do tipo de configuração utilizada e indica se os clientes ZEO devem fazer download dos blobs utilizando as conexões ao banco de dados, ou se eles podem assumir que os blobs estão disponíveis no sistema de arquivos por se encontrarem na mesma máquina, ou disponibilizados por algum serviço de compartilhamento como NFS.

Mais informação na documentação dos pacotes plone.recipe.zeoserver e plone.recipe.zope2instance.

Alta disponibilidade

A alta disponibilidade pode se definir como a caraterística de um sistema que permite garantir um nível de operação e desempenho num período de tempo determinado.

A alta disponibilidade depende da implementação de redundância em todos os níveis do sistema. Neste item só analisaremos a parte referente ao banco de dados.

Como mencionado anteriormente, o Zope utiliza por padrão o banco de dados ZODB para armazenar objetos Python serializados (pickles). O ZODB é um ponto de falha único em um sistema formado por várias instâncias de Zope rodando em modo ZEO client e uma instância de Zope rodando em modo ZEO server. Para resolver este problema existem duas soluções: ZODB Replicated Storage (ZRS) e RelStorage.

ZRS

O ZRS é um mecanismo de replicação entre um banco de dados primário e um ou vários bancos de dados secundários. A replicação é melhor que os backups pois ela garante que todos os bancos de dados vão se encontrar o tempo tudo sincronizados. Em caso de falha, basta reconfigurar um banco de dados secundário como primário.

O ZRS é muito fácil de configurar e tem a vantagem de replicar automaticamente o blob storage, fazendo desnecessária a instalação de sistemas de compartilhamento do file system como o NFS. A limitação principal do ZRS é que os bancos de dados secundários devem funcionar em modo read-only, ou seja, não se pode escrever em um banco de dados secundário.

Uma configuração típica de ZRS (com servidores rodando com os endereços IP 10.0.0.1 e 10.0.0.2) inclui como mínimo as seguintes diretivas no ZEO master:

[zeoserver]
recipe = plone.recipe.zeoserver[zrs]
zeo-address = 8100
replicate-to = 5000

No ZEO slave devemos usar como mínimo as seguintes diretivas:

[zeoserver]
recipe = plone.recipe.zeoserver[zrs]
zeo-address = 8100
replicate-from = 10.0.0.1:5000
read-only = true

Esses endereços devem também ser informados nas instâncias:

[instance]
...
shared-blob = off
zeo-address = 10.0.0.1:8100 10.0.0.2:8100
zeo-client = on
zeo-client-read-only-fallback = on

A diretiva zeo-address lista os endereços e portas de todos os servidores ZEO. A diretiva zeo-client-read-only-fallback indica que, caso de falha no ZEO server master, a instância pode tentar se conetar ao ZEO server slave em modo read-only.

RelStorage

O RelStorage é uma implementação de storage da ZODB que permite armazenar os pickles num banco de dados relacional. O RelStorage suporta PostgreSQL, MySQL e Oracle.

Um storage usando o RelStorage tem algumas vantagens sobre o ZRS, sendo a mais evidente a possibilidade de suportar failover em bancos de dados replicados. O RelStorage adiciona também uma complexidade maior ao ter que gerenciar uma tecnologia completamente diferente.