Cache de Resultados de Execução de Requisições

GolangBeginner
Pratique Agora

Introdução

Neste projeto, você aprenderá como implementar uma biblioteca de cache que suporta tempo de expiração baseado no pacote singleflight em Go. Esta biblioteca de cache pode ser usada em sistemas distribuídos para armazenar em cache os resultados da execução de requisições, melhorando o desempenho da sua aplicação.

👀 Pré-visualização

$ /usr/local/go/bin/go test --race
PASS
ok      cacheflight     1.263s

🎯 Tarefas

Neste projeto, você aprenderá:

  • Como inicializar um módulo Go e instalar os pacotes necessários
  • Como implementar a biblioteca de cache usando o pacote singleflight
  • Como testar a biblioteca de cache para garantir que ela funcione como esperado

🏆 Conquistas

Após concluir este projeto, você será capaz de:

  • Compreender os conceitos básicos de cache e o pacote singleflight em Go
  • Implementar uma biblioteca de cache que suporta tempo de expiração
  • Integrar a biblioteca de cache em seu sistema distribuído para melhorar o desempenho da sua aplicação

Inicialize o Módulo e Instale os Pacotes Necessários

Nesta etapa, você aprenderá como inicializar o módulo e instalar os pacotes necessários para o projeto.

  1. Abra o terminal no diretório /home/labex/project.

  2. Inicialize o módulo usando o seguinte comando:

    /usr/local/go/bin/go mod init cacheflight
    
  3. Instale os pacotes necessários usando os seguintes comandos:

    /usr/local/go/bin/go get github.com/golang/groupcache/singleflight
    /usr/local/go/bin/go get github.com/stretchr/testify/assert
    

Implemente a Biblioteca de Cache

Nesta etapa, você implementará a biblioteca de cache baseada no pacote singleflight.

  1. Abra o arquivo cacheflight.go no diretório /home/labex/project.

  2. Adicione o conteúdo necessário para import.

    import (
        "sync"
        "time"
    
        "github.com/golang/groupcache/singleflight"
    )
    
  3. Adicione o conteúdo necessário para type Group e type cacheResult.

    // cacheResult is cache result
    type cacheResult struct {
        data      interface{} // the cache data
        ctime     time.Time   // cache create time
        isRunning chan bool   // is the function running
    }
    
    // Group is core struct
    type Group struct {
        cacheExpiration time.Duration          // cache expiration
        sfg             *singleflight.Group    // singleflight group
        cache           map[string]cacheResult // cache result
        mu              sync.Mutex             // mutex
    }
    
  4. Implemente a função NewGroup para retornar um novo grupo de cache com o tempo de expiração do cache especificado.

    func NewGroup(cacheExpiration time.Duration) (group *Group) {
        group = &Group{
            sfg:             &singleflight.Group{},
            cache:           make(map[string]cacheResult),
            cacheExpiration: cacheExpiration,
        }
        return
    }
    
  5. Implemente a função do para lidar com a lógica de cache.

    func (g *Group) do(key string, fn func() (interface{}, error)) (ret interface{}, err error) {
        return g.sfg.Do(key, func() (interface{}, error) {
            g.mu.Lock()
            result, ok := g.cache[key]
            if result.isRunning != nil {
                g.mu.Unlock()
                // wait for fn done
                <-result.isRunning
                return g.do(key, fn)
            } else if !ok || result.ctime.Add(g.cacheExpiration).Before(time.Now()) {
                // if the cache is not exist or expired
                var run = make(chan bool)
                result.isRunning = run
                g.cache[key] = result
                // when fn done and close run channel, let other goroutine can get the cache
                defer close(run)
            } else {
                // if the cache is exist and not expired
                g.mu.Unlock()
                return result.data, nil
            }
    
            g.mu.Unlock()
    
            // different key can run at the same time
            ret, err = fn()
            if err != nil {
                return ret, nil
            }
    
            result = cacheResult{
                data:  ret,
                ctime: time.Now(),
            }
            g.mu.Lock()
            g.cache[key] = result
            g.mu.Unlock()
    
            return ret, nil
        })
    }
    
  6. Implemente a função Do para ser um wrapper em torno da função do.

    func (g *Group) Do(key string, fn func() (interface{}, error)) (ret interface{}, err error) {
        return g.do(key, fn)
    }
    

Teste a Biblioteca de Cache

Nesta etapa, você testará a biblioteca de cache para garantir que ela funcione conforme o esperado.

  1. Abra o terminal no diretório /home/labex/project.

  2. Execute o seguinte comando para executar os testes:

    cd /home/labex/project
    /usr/local/go/bin/go test --race
    
  3. Se a função de cache foi implementada corretamente, você verá a seguinte saída:

    PASS
    ok      cacheflight     1.263s
    
  4. Se você encontrar um erro de tempo limite (timeout), execute os seguintes comandos no terminal e, em seguida, clique no botão de reinspeção para passar na verificação:

    cd /home/labex/project
    /usr/local/go/bin/go test --race
    

Parabéns! Você implementou com sucesso a biblioteca de cache baseada no pacote singleflight. Agora você pode usar esta biblioteca em seu sistema distribuído para armazenar em cache os resultados da execução de solicitações e melhorar o desempenho de sua aplicação.

Resumo

Parabéns! Você concluiu este projeto. Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.

✨ Verificar Solução e Praticar✨ Verificar Solução e Praticar✨ Verificar Solução e Praticar