Navegador de Arquivos
Introdução
Algumas das tarefas de recuperação ou processamento de dados realizadas pela sua aplicação podem exigir muita CPU ou demorar vários segundos para concluir. Nesse caso, é comum armazenar os dados recuperados em cache por um tempo para que possam ser recuperados rapidamente em solicitações subsequentes pelos mesmos dados. Os dados armazenados em cache geralmente são salvos em um repositório de dados muito rápido, como o Memcached ou o Redis.
A propósito, o Laravel fornece uma API expressiva e uniforme para vários servidores de armazenamento em cache, permitindo que você se beneficie da recuperação rápida de dados deles e acelere seu aplicativo web.
Configuração
O arquivo de configuração do cache da aplicação está localizado em config/cache.php
. Nele, você pode especificar qual armazenamento de cache deseja ser usado por padrão em toda a aplicação. O Laravel suporta backends de caching populares como Memcached, Redis, DynamoDB e bancos de dados relacionais fora da caixa. Além disso, um driver de cache baseado em arquivo está disponível, enquanto os drivers de cache "array" e "null" oferecem backends de cache convenientes para seus testes automáticos.
O arquivo de configuração do cache também contém uma variedade de outras opções que você pode analisar. Por padrão, o Laravel é configurado para usar o driver de cache "database", que armazena os objetos serializados e encriptados no banco de dados da aplicação.
Pré-requisitos do driver
Banco de dados
Para usar o driver de cache "database", você precisa ter uma tabela de banco de dados para conter os dados do cache. Normalmente, isto é incluído na migração padrão 0001_01_01_000001_create_cache_table.php
do Laravel migração de banco de dados; no entanto, se sua aplicação não possuir essa migração, você poderá usar o comando Artisan make:cache-table
para criá-la:
php artisan make:cache-table
php artisan migrate
Memcached
O uso do driver Memcached requer o pacote PECL Memcached para ser instalado. Você pode listar todos os seus servidores Memcached no arquivo de configuração config/cache.php
. Esse arquivo já contém uma entrada memcached.servers
para começar:
'memcached' => [
// ...
'servers' => [
[
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
'port' => env('MEMCACHED_PORT', 11211),
'weight' => 100,
],
],
],
Se necessário, você pode definir a opção host
para um caminho de soquete UNIX. Se você fizer isso, a opção port
deverá ser definida como 0
:
'memcached' => [
// ...
'servers' => [
[
'host' => '/var/run/memcached/memcached.sock',
'port' => 0,
'weight' => 100
],
],
],
Redis
Antes de usar um cache Redis com o Laravel, será necessário instalar a extensão PHP PhpRedis via PECL ou instalar o pacote predis/predis
(~2.0) através do Composer. Laravel Sail já inclui essa extensão. Além disso, as plataformas de implantação oficiais do Laravel, como o Laravel Forge e o Laravel Vapor, têm a extensão PhpRedis instalada por padrão.
Para mais informações sobre como configurar o Redis, consulte a página de documentação do Redis no Laravel.
DynamoDB
Antes de usar o driver de cache DynamoDB, você deve criar uma tabela DynamoDB para armazenar todos os dados do cache. Normalmente, essa tabela deve ser nomeada como cache
. No entanto, você deve nomear a tabela com base no valor da configuração stores.dynamodb.table
dentro do arquivo de configuração cache
. O nome da tabela também pode ser definido pela variável de ambiente DYNAMODB_CACHE_TABLE
.
Essa tabela também deve possuir uma chave de partição em string com um nome que corresponda ao valor do item de configuração stores.dynamodb.attributes.key
dentro do arquivo de configuração cache
da aplicação. Por padrão, a chave de partição deve ser denominada key
.
Em seguida, instale o AWS SDK para que seu aplicativo Laravel possa se comunicar com a DynamoDB:
composer require aws/aws-sdk-php
Além disso, certifique-se de que os valores são fornecidos para as opções de configuração do armazenamento do cache DynamoDB. Normalmente, essas opções, como AWS_ACCESS_KEY_ID
e AWS_SECRET_ACCESS_KEY
, devem ser definidas no arquivo de configuração .env
da aplicação:
'dynamodb' => [
'driver' => 'dynamodb',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
'endpoint' => env('DYNAMODB_ENDPOINT'),
],
Uso do Cache
Obtendo uma instância de cache
Para obter uma instância do armazenamento de cache, é possível usar a facade Cache
, que será usada ao longo desta documentação. A facade Cache
fornece um acesso conveniente e conciso às implementações subjacentes dos contratos de cache do Laravel:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Cache;
class UserController extends Controller
{
/**
* Mostre uma lista de todos os usuários do aplicativo.
*/
public function index(): array
{
$value = Cache::get('key');
return [
// ...
];
}
}
Acessando vários repositórios de cache
Usando a facade Cache
, você pode acessar vários repositórios de cache através do método store
. A chave passada ao método store
deve corresponder a um dos repositórios listados na matriz de configuração stores
em seu arquivo de configuração cache
:
$value = Cache::store('file')->get('foo');
Cache::store('redis')->put('bar', 'baz', 600); // 10 Minutos
Recuperar itens do cache
O método get
da facade Cache
é utilizado para recuperar itens do cache. Se o item não existir no cache, será retornado null
. Caso queira, pode passar um segundo argumento ao método get
, especificando o valor padrão que deseja que seja retornado caso o item não exista:
$value = Cache::get('key');
$value = Cache::get('key', 'default');
É possível usar um bloqueio como valor padrão. Se o item especificado não existir no cache, o resultado do bloqueio será retornado. Ao passar um bloqueio, você pode adiar a obtenção de valores por padrão de um banco de dados ou outro serviço externo:
$value = Cache::get('key', function () {
return DB::table(/* ... */)->get();
});
Determinando a existência de um item
O método has
pode ser utilizado para determinar se um item existe no cache. Se o item existir mas o seu valor for null
, este método também irá retornar false
:
if (Cache::has('key')) {
// ...
}
Aumentar/diminuir valores
Os métodos increment
e decrement
podem ser utilizados para ajustar o valor de itens inteiros no cache. Ambos os métodos aceitam um segundo argumento opcional que indica a quantidade em que se deve incrementar ou decrementar o valor do item:
// Inicialize o valor se ele não existir...
Cache::add('key', 0, now()->addHours(4));
// Aumentar ou diminuir o valor...
Cache::increment('key');
Cache::increment('key', $amount);
Cache::decrement('key');
Cache::decrement('key', $amount);
Recuperar e armazenar
Às vezes pode ser necessário recuperar um item do cache, mas também guardar um valor por defeito caso o item solicitado não exista. Por exemplo, pode ser que seja necessário recuperar todos os utilizadores a partir do cache ou, no caso de eles não existirem, recuperá-los da base de dados e adicioná-los ao cache. Pode fazer isto usando o método Cache::remember
:
$value = Cache::remember('users', $seconds, function () {
return DB::table('users')->get();
});
Se o elemento não existir na memória cache, será executado o closure passado para o método remember
e seu resultado será colocado na memória cache.
Você pode usar o método rememberForever
para recuperar um item do cache ou armazená-lo para sempre se ele não existir:
$value = Cache::rememberForever('users', function () {
return DB::table('users')->get();
});
Recuperar e excluir
Se você precisar recuperar um elemento do cache e depois excluí-lo, poderá usar o método pull
. Assim como no caso do método get
, será retornado null
se o item não existir no cache:
$value = Cache::pull('key');
$value = Cache::pull('key', 'default');
Armazenar itens no cache
Você pode utilizar o método put
na faceta do Cache
para armazenar itens no cache:
Cache::put('key', 'value', $seconds = 10);
Se o tempo de armazenamento não for passado à função put
, o item será armazenado indefinidamente:
Cache::put('key', 'value');
Em vez de passar o número de segundos como um inteiro, você também pode passar uma instância DateTime
que representa o tempo de validade desejado dos itens armazenados no cache:
Cache::put('key', 'value', now()->addMinutes(10));
Armazenar se não estiver presente
O método add
somente adiciona o item ao cache se ele ainda não existir no armazenamento de cache. Se o item tiver sido adicionado, o método retorna true
. Caso contrário, o método retorna false
. O método add
é uma operação atômica:
Cache::add('key', 'value', $seconds);
Armazenar itens para sempre
O método forever
pode ser usado para armazenar permanentemente um item no cache. Uma vez que esses itens não têm data de validade, eles devem ser removidos do cache manualmente com o uso do método forget
:
Cache::forever('key', 'value');
NOTA
Se você estiver utilizando o driver Memcached, os itens que são armazenados "para sempre" podem ser eliminados quando o limite do tamanho de cache for atingido.
Remoção de itens do cache
Você pode remover itens do cache usando o método forget
:
Cache::forget('key');
Também é possível remover itens fornecendo um número de validade negativo ou zero:
Cache::put('key', 'value', 0);
Cache::put('key', 'value', -5);
Você pode limpar o cache inteiro usando o método flush
:
Cache::flush();
ATENÇÃO
Ao eliminar o cache, ele ignora seu "prefixo" de cache configurado e exclui todas as entradas do cache. Antecipe esta ação com cuidado ao limpar um cache compartilhado por outras aplicações.
O auxiliar cache
Além do uso da interface Cache
, é possível também usar a função global cache
para recuperar e armazenar dados através do cache. Quando a função cache
for chamada com um único argumento do tipo string, ela retornará o valor da chave indicada:
$value = cache('key');
Se você fornecer uma matriz de pares chave/valor e um tempo de validade para a função, ela armazenará os valores no cache por um período especificado:
cache(['key' => 'value'], $seconds);
cache(['key' => 'value'], now()->addMinutes(10));
Quando a função cache
é chamada sem nenhum argumento, ela retorna uma instância da implementação de Illuminate\Contracts\Cache\Factory
, permitindo que você chame outros métodos de armazenamento em cache:
cache()->remember('users', $seconds, function () {
return DB::table('users')->get();
});
NOTA
Ao testar a chamada para a função global cache
, você pode usar o método Cache::shouldReceive
como se estivesse testando a facade.
Bloqueios Atómicos
ATENÇÃO
Para utilizar este recurso, a aplicação deve usar o motor de memória memcached
, redis
, dynamodb
, database
, file
ou array
. Além disso, todos os servidores devem comunicar com o mesmo servidor central de cache.
Gerenciar bloqueios
Os bloqueios atômicos permitem a manipulação de bloqueios distribuídos sem se preocupar com condições de corrida. Por exemplo, o Laravel Forge usa bloqueios atômicos para garantir que apenas uma tarefa remota seja executada num servidor a cada vez. Você pode criar e gerir os seus bloqueios utilizando o método Cache::lock
:
use Illuminate\Support\Facades\Cache;
$lock = Cache::lock('foo', 10);
if ($lock->get()) {
// Bloqueio adquirido por 10 segundos...
$lock->release();
}
O método get
também aceita um bloqueio. Depois que o bloqueio for executado, o Laravel libera o bloqueio automaticamente:
Cache::lock('foo', 10)->get(function () {
// Bloqueio adquirido por 10 segundos e liberado automaticamente...
});
Se o bloqueio não estiver disponível no momento em que foi solicitado, você pode instruir o Laravel para esperar por um número especificado de segundos. Se o lock não puder ser adquirido dentro do limite de tempo especificado, uma Illuminate\Contracts\Cache\LockTimeoutException
será lançada:
use Illuminate\Contracts\Cache\LockTimeoutException;
$lock = Cache::lock('foo', 10);
try {
$lock->block(5);
// Bloqueio adquirido após esperar no máximo 5 segundos...
} catch (LockTimeoutException $e) {
// Não foi possível adquirir o bloqueio...
} finally {
$lock?->release();
}
O exemplo acima pode ser simplificado passando um closure para o método block
. Quando um closure é passado para este método, O Laravel tentará adquirir o bloqueio por um número específico de segundos e liberá-lo automaticamente assim que o closure tiver sido executado:
Cache::lock('foo', 10)->block(5, function () {
// Bloqueio adquirido após esperar no máximo 5 segundos...
});
Gerir bloqueios em processos diferentes
Às vezes, você pode pretender adquirir um bloqueio num processo e liberá-lo noutro. Por exemplo, você pode adquirir um bloqueio durante uma solicitação Web e desejar liberar o bloqueio no final de um trabalho agendado que é acionado por essa solicitação. Neste cenário, se deve passar o "token do proprietário" associado ao bloqueio ao trabalho agendado para que este possa recriar o bloqueio utilizando o token fornecido.
No exemplo abaixo, será enviada uma tarefa aguardando o envio caso um bloqueio seja adquirido com sucesso. Além disso, passamos o token do proprietário do bloqueio para a tarefa aguardando através do método owner
do bloqueio:
$podcast = Podcast::find($id);
$lock = Cache::lock('processing', 120);
if ($lock->get()) {
ProcessPodcast::dispatch($podcast, $lock->owner());
}
Na tarefa do nosso aplicativo ProcessPodcast
, podemos restaurar e liberar o bloqueio usando o token do proprietário.
Cache::restoreLock('processing', $this->owner)->release();
Se você quiser liberar um bloqueio sem respeitar o atual proprietário, poderá usar o método forceRelease
:
Cache::lock('processing')->forceRelease();
Adicionando drivers de cache personalizados
Escrevendo o driver
Para criar nosso motor de cache personalizado, primeiro precisamos implementar o contrato Illuminate\Contracts\Cache\Store
. Por isso, a implementação do cache do MongoDB pode parecer algo assim:
<?php
namespace App\Extensions;
use Illuminate\Contracts\Cache\Store;
class MongoStore implements Store
{
public function get($key) {}
public function many(array $keys) {}
public function put($key, $value, $seconds) {}
public function putMany(array $values, $seconds) {}
public function increment($key, $value = 1) {}
public function decrement($key, $value = 1) {}
public function forever($key, $value) {}
public function forget($key) {}
public function flush() {}
public function getPrefix() {}
}
Basta implementar cada um destes métodos usando uma conexão MongoDB. Para saber como implementar cada um destes métodos, consulte o Illuminate\Cache\MemcachedStore
no código-fonte do framework Laravel. Depois de terminarmos a nossa implementação, podemos concluir o registro do nosso driver personalizado chamando o método extend
da facade Cache
:
Cache::extend('mongo', function (Application $app) {
return Cache::repository(new MongoStore);
});
NOTA
Se você se pergunta onde colocar o seu código de driver de cache personalizado, poderá criar um namespace Extensions
dentro do diretório app
. No entanto, tenha em mente que o Laravel não tem uma estrutura rígida da aplicação e você é livre para organizar sua aplicação conforme suas preferências.
Registrando o driver
Para registrar o driver de cache personalizado com Laravel, usaremos o método extend
da facade Cache
. Uma vez que outros provedores de serviços podem tentar ler os valores armazenados em cache no seu método boot
, iremos registrar nosso driver personalizado dentro do um callback de booting
. Usando o callback de booting
, podemos garantir que o driver customizado seja registrado imediatamente antes da chamada ao método boot
dos provedores de serviços da aplicação, mas depois da chamada ao método register
para todos os provedores de serviços. Iremos registrar nosso callback de booting
dentro do método register
do App\Providers\AppServiceProvider
da nossa aplicação:
<?php
namespace App\Providers;
use App\Extensions\MongoStore;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Registre quaisquer serviços de aplicativo.
*/
public function register(): void
{
$this->app->booting(function () {
Cache::extend('mongo', function (Application $app) {
return Cache::repository(new MongoStore);
});
});
}
/**
* Inicialize qualquer serviço de aplicativo.
*/
public function boot(): void
{
// ...
}
}
O primeiro argumento passado para o método extend
é o nome do driver. Isso corresponde à opção driver
no arquivo de configuração config/cache.php
. O segundo argumento é um closure que deve retornar uma instância da classe Illuminate\Cache\Repository
. O closure recebe como parâmetro a instância $app
, que é uma instância do conjunto de serviços.
Depois de sua extensão ser registrada, atualize a variável de ambiente CACHE_STORE
ou a opção default
dentro do arquivo de configuração da aplicação, config/cache.php
, com o nome da sua extensão.
Eventos
Para executar o código em todas as operações de memória transparente, você pode ouvir vários eventos disponíveis, que são enviados pela memória transparente:
Nome do evento |
---|
Illuminate\Cache\Events\CacheHit |
Illuminate\Cache\Events\CacheMissed |
Illuminate\Cache\Events\KeyForgotten |
Illuminate\Cache\Events\KeyWritten |
Para aumentar o desempenho, você pode desativar eventos de cache definindo a opção de configuração events
para false
para um determinado armazenamento de cache no arquivo de configuração do seu aplicativo config/cache.php
:
'database' => [
'driver' => 'database',
// ...
'events' => false,
],