Caching
O Cache acelera sua aplicação armazenando os dados – uma vez que sejam difíceis de recuperar – para uso futuro. Nós lhe mostraremos:
- Como usar o cache
- Como mudar o armazenamento do cache
- Como invalidar adequadamente o cache
O uso do cache é muito fácil em Nette, enquanto que ele também cobre necessidades de cache muito avançadas. Ele é projetado para desempenho e 100% de durabilidade. Basicamente, você encontrará adaptadores para o armazenamento backend mais comum. Permite a invalidação baseada em tags, proteção de carimbos de cache, expiração de tempo, etc.
Instalação
Baixe e instale o pacote usando o Composer:
Utilização básica
O centro de trabalho com o cache é o objeto Nette\Caching\Cache. Criamos sua instância e passamos
o chamado armazenamento para o construtor como parâmetro. Que é um objeto que representa o local onde os dados serão
armazenados fisicamente (banco de dados, Memcached, arquivos em disco, …). Você obtém o objeto de armazenamento
passando-o usando a injeção de dependência com o tipo
Nette\Caching\Storage
. Você encontrará tudo o que é essencial na seção
Armazenamento.
Na versão 3.0, a interface ainda tinha o I
prefix, so the name was
Nette\Caching\IStorage
. Além disso, as constantes da classe Cache
foram capitalizadas, portanto, por
exemplo Cache::EXPIRE
em vez de Cache::Expire
.
Para os exemplos a seguir, suponha que tenhamos um pseudônimo Cache
e um armazenamento na variável
$storage
.
O cache é, na verdade, uma loja de valores-chave, portanto, lemos e escrevemos dados sob chaves, assim como as matrizes associativas. As aplicações consistem em várias partes independentes, e se todas elas usassem um armazenamento (para idéia: um diretório em um disco), mais cedo ou mais tarde haveria uma colisão de chaves. O Nette Framework resolve o problema dividindo o espaço inteiro em espaços de nomes (subdiretórios). Cada parte do programa utiliza então seu próprio espaço com um nome único e nenhuma colisão pode ocorrer.
O nome do espaço é especificado como o segundo parâmetro do construtor da classe Cache:
Agora podemos usar o objeto $cache
para ler e escrever a partir do cache. O método load()
é usado
para ambos. O primeiro argumento é a chave e o segundo é a chamada de retorno do PHP, que é chamada quando a chave não é
encontrada no cache. A chamada de retorno gera um valor, devolve-o e o armazena em cache:
Se o segundo parâmetro não for especificado $value = $cache->load($key)
, o null
é devolvido se
o item não estiver no cache.
O grande problema é que quaisquer estruturas serializáveis podem ser colocadas em cache, não apenas cordas. E o mesmo se aplica às chaves.
O item é removido do cache usando o método remove()
:
Você também pode armazenar um item usando o método $cache->save($key, $value, array $dependencies = [])
.
Entretanto, o método acima usando load()
é o preferido.
Memoization
Memoization significa memorizar o resultado de uma função ou método para que você possa usá-lo da próxima vez em vez de calcular a mesma coisa repetidamente.
Os métodos e funções podem ser chamados de memotize utilizando call(callable $callback, ...$args)
:
A função gethostbyaddr()
é chamada apenas uma vez para cada parâmetro $ip
e na próxima vez
o valor do cache será devolvido.
Também é possível criar uma embalagem memorizada para um método ou função que pode ser chamada mais tarde:
Expiração & Invalidação
Com o cache, é necessário abordar a questão de que alguns dos dados salvos anteriormente se tornarão inválidos com o tempo. Nette Framework fornece um mecanismo, como limitar a validade dos dados e como apagá-los de forma controlada („invalidá-los“, usando a terminologia do framework).
A validade dos dados é definida no momento da gravação usando o terceiro parâmetro do método save()
, por
exemplo:
Ou usando o parâmetro $dependencies
passado por referência ao retorno de chamada no método
load()
, por exemplo:
Ou usando o 3º parâmetro no método load()
, por exemplo
Nos exemplos a seguir, assumiremos a segunda variante e, portanto, a existência de uma variável
$dependencies
.
Validade
A expiração mais simples é o limite de tempo. Veja aqui como armazenar dados válidos por 20 minutos:
Se quisermos prolongar o período de validade com cada leitura, isto pode ser conseguido desta forma, mas cuidado, isto aumentará a sobrecarga do cache:
A opção útil é a capacidade de deixar os dados expirarem quando um determinado arquivo é alterado ou um de vários arquivos. Isto pode ser usado, por exemplo, para armazenar os dados resultantes da procissão destes arquivos. Use caminhos absolutos.
Podemos deixar um item no cache expirar quando outro item (ou um de vários outros) expirar. Isto pode ser usado quando
armazenamos a página HTML inteira e fragmentos dela em cache sob outras chaves. Uma vez que o snippet muda, a página inteira se
torna inválida. Se tivermos fragmentos armazenados sob chaves como frag1
e frag2
, nós usaremos:
A expiração também pode ser controlada usando funções personalizadas ou métodos estáticos, que sempre decidem ao ler se
o item ainda é válido. Por exemplo, podemos deixar o item expirar sempre que a versão PHP mudar. Criaremos uma função que
compara a versão atual com o parâmetro, e ao salvar adicionaremos um array na forma [function name, ...arguments]
para as dependências:
Naturalmente, todos os critérios podem ser combinados. O cache então expira quando pelo menos um critério não é atendido.
Invalidação usando etiquetas
As etiquetas são uma ferramenta de invalidação muito útil. Podemos atribuir uma lista de tags, que são strings arbitrárias, a cada item armazenado no cache. Por exemplo, suponha que tenhamos uma página HTML com um artigo e comentários, que desejamos que seja armazenada em cache. Assim, especificamos as tags ao salvar em cache:
Agora, vamos passar para a administração. Aqui temos um formulário para a edição de artigos. Junto com salvar o artigo em
um banco de dados, chamamos o comando clean()
, que apagará os artigos em cache por tag:
Da mesma forma, no lugar de acrescentar um novo comentário (ou editar um comentário), não esqueceremos de invalidar a etiqueta relevante:
O que conseguimos? Que nosso cache HTML será invalidado (excluído) sempre que o artigo ou comentários forem alterados. Ao
editar um artigo com ID = 10, a tag article/10
é forçada a ser invalidada e a página HTML com a tag é excluída
do cache. O mesmo acontece quando você insere um novo comentário sob o artigo relevante.
As etiquetas exigem o Journal.
Invalidação por Prioridade
Podemos definir a prioridade para itens individuais no cache, e será possível apagá-los de forma controlada quando, por exemplo, o cache exceder um determinado tamanho:
Eliminar todos os itens com prioridade igual ou inferior a 100:
As prioridades exigem o chamado Diário.
Cache claro
O parâmetro Cache::All
limpa tudo:
Leitura a granel
Para leitura e escrita em massa em cache, é usado o método bulkLoad()
, onde passamos uma série de chaves e
obtemos uma série de valores:
O método bulkLoad()
funciona de forma semelhante ao load()
com o segundo parâmetro de retorno de
chamada, para o qual a chave do item gerado é passada:
Uso com o PSR-16
Para usar o Nette Cache com a interface PSR-16, você pode utilizar o site PsrCacheAdapter
. Ele permite uma
integração perfeita entre o Nette Cache e qualquer código ou biblioteca que espere um cache compatível com PSR-16.
Agora você pode usar o $psrCache
como um cache PSR-16:
O adaptador suporta todos os métodos definidos no PSR-16, incluindo getMultiple()
, setMultiple()
e
deleteMultiple()
.
Caching de saída
A saída pode ser capturada e armazenada em cache de forma muito elegante:
Caso a saída já esteja presente no cache, o método capture()
imprime-a e retorna null
, de modo
que a condição não será executada. Caso contrário, ele começa a armazenar a saída e retorna o objeto
$capture
, usando o qual finalmente salvamos os dados no cache.
Na versão 3.0, o método foi chamado $cache->start()
.
Caching em Latte
O cache em modelos Latte é muito fácil, basta embrulhar parte do modelo com tags
{cache}...{/cache}
. O cache é automaticamente invalidado quando o modelo fonte muda (incluindo quaisquer modelos
incluídos dentro das tags {cache}
). As tags {cache}
podem ser aninhadas, e quando um bloco aninhado é
invalidado (por exemplo, por uma tag), o bloco pai também é invalidado.
Na etiqueta é possível especificar as chaves às quais o cache será vinculado (aqui a variável $id
) e definir
as etiquetas de expiração e invalidação
Todos os parâmetros são opcionais, portanto não é necessário especificar a expiração, etiquetas ou chaves.
O uso do cache também pode ser condicionado por if
– o conteúdo será então armazenado em cache somente se
a condição for atendida:
Armazenagens
Um armazenamento é um objeto que representa o local onde os dados são fisicamente armazenados. Podemos usar um banco de dados, um servidor Memcached ou o armazenamento mais disponível, que são arquivos em disco.
Armazenamento | Descrição |
---|---|
FileStorage | armazenamento padrão com gravação em arquivos em disco |
MemcachedStorage | utiliza o servidor Memcached |
MemoryStorage | os dados estão temporariamente na memória |
SQLite Os dados são armazenados no banco de dados | |
DevNullStorage | os dados não são armazenados – para fins de teste |
Você obtém o objeto de armazenamento passando-o usando a injeção
de dependência com o tipo Nette\Caching\Storage
. Por padrão, a Nette fornece um objeto FileStorage que
armazena dados em uma subpasta cache
no diretório para arquivos temporários.
Você pode alterar o armazenamento na configuração:
FileStorage
Escreve o cache em arquivos em disco. O armazenamento Nette\Caching\Storages\FileStorage
é muito bem otimizado
para o desempenho e, acima de tudo, garante a atomicidade total das operações. O que isso significa? Que ao usar o cache,
não pode acontecer que lemos um arquivo que ainda não tenha sido completamente escrito por outro fio, ou que alguém o apagaria
„sob suas mãos“. O uso do cache é, portanto, completamente seguro.
Este armazenamento também tem uma importante característica incorporada que impede um aumento extremo no uso da CPU quando o cache é limpo ou frio (ou seja, não criado). Isto é prevenção de "debandada do cache:https://en.wikipedia.org/…che_stampede ". Acontece que em um momento há várias solicitações simultâneas que querem a mesma coisa do cache (por exemplo, o resultado de uma consulta SQL cara) e, como não está em cache, todos os processos começam a executar a mesma consulta SQL. A carga do processador é multiplicada e pode até acontecer que nenhuma thread possa responder dentro do limite de tempo, o cache não é criado e a aplicação trava. Felizmente, o cache em Nette funciona de tal forma que quando há várias solicitações simultâneas para um item, ele é gerado apenas pelo primeiro thread, os outros esperam e depois usam o resultado gerado.
Exemplo de criação de um FileStorage:
MemcachedStorage
O servidor Memcached é um sistema de armazenamento distribuído de alto desempenho cujo
adaptador é Nette\Caching\Storages\MemcachedStorage
. Na configuração, especifique o endereço IP e a porta, caso
seja diferente do padrão 11211.
Requer extensão PHP memcached
.
MemoryStorage
Nette\Caching\Storages\MemoryStorage
é um armazenamento que armazena dados em uma matriz PHP e, portanto, se
perde quando a solicitação é encerrada.
SQLiteStorage
O banco de dados SQLite e o adaptador Nette\Caching\Storages\SQLiteStorage
oferecem uma maneira de fazer
o cache em um único arquivo em disco. A configuração irá especificar o caminho para este arquivo.
Requer extensões PHP pdo
e pdo_sqlite
.
DevNullStorage
Uma implementação especial de armazenamento é Nette\Caching\Storages\DevNullStorage
, que na verdade não
armazena dados de forma alguma. Portanto, é adequado para testes se quisermos eliminar o efeito do cache.
Usando Cache em Código
Ao utilizar o cache em código, você tem duas maneiras de fazer isso. A primeira é que você obtém o objeto de
armazenamento passando-o usando a injeção de dependência e depois
cria um objeto Cache
:
A segunda maneira é que você obtenha o objeto de armazenamento Cache
:
O objeto Cache
é então criado diretamente na configuração como se segue:
Jornal
A Nette armazena etiquetas e prioridades em uma chamada revista. Por padrão, SQLite e arquivo journal.s3db
são
usados para isso, e São necessárias extensões PHP pdo
e pdo_sqlite
.
Você pode alterar a configuração da revista:
Serviços DI
Esses serviços são adicionados ao contêiner DI:
Nome | Tipo | Descrição |
---|---|---|
cache.journal |
Nette\Caching\Storages\Journal | journal |
cache.storage |
Nette\Caching\Storage | repositório |
Desativação do cache
Uma das maneiras de desativar o cache no aplicativo é definir o armazenamento como DevNullStorage:
Essa configuração não afeta o armazenamento em cache dos modelos no Latte ou no contêiner DI, pois essas bibliotecas não usam os serviços do nette/caching e gerenciam seu cache de forma independente. Além disso, seu cache não precisa ser desativ ado no modo de desenvolvimento.