Como usar o Redis para sobrecarregar suas APIs da Web

Início » Tecnologia » Como usar o Redis para sobrecarregar suas APIs da Web


O desempenho é um parâmetro essencial a ser considerado ao projetar qualquer peça de software. É particularmente importante quando se trata do que acontece nos bastidores.

Nós, como desenvolvedores e tecnólogos, adotamos vários ajustes e implementações para melhorar o desempenho. É aqui que o cache entra em jogo.

O armazenamento em cache é definido como um mecanismo para armazenar dados ou arquivos em um local de armazenamento temporário, de onde pode ser acessado instantaneamente sempre que necessário.

Atualmente, o cache tornou-se um item obrigatório em aplicativos da web. Podemos usar o Redis para sobrecarregar nossas APIs da web – criadas usando o Node.js e o MongoDB.

“O cache aparentemente ainda desempenharia um papel super importante de 100 a 200 anos depois”.

Redis: Visão geral de um leigo

Redis, de acordo com a documentação oficial, é definido como um armazenamento de estrutura de dados em memória usado como banco de dados, intermediário de mensagens ou armazenamento em cache. Ele suporta estruturas de dados como strings, hashes, listas, conjuntos, conjuntos classificados com consultas de intervalo, bitmaps, hiperloglogs, índices geoespaciais com consultas e fluxos de raio.

Ok, existem várias estruturas de dados ali. Apenas para simplificar, quase todas as estruturas de dados suportadas podem ser condensadas em uma forma de string ou outra. Você terá mais clareza à medida que avançamos na implementação.

Mas uma coisa é clara. O Redis é poderoso e, quando usado corretamente, pode tornar nossos aplicativos não apenas mais rápidos, mas incrivelmente eficientes. Chega de conversa. Vamos sujar as mãos.

Vamos conversar sobre código

Antes de começarmos, você precisará obter a configuração do redis em seu sistema local. Você pode seguir isso configuração rápida processo para obter o redis instalado e funcionando.

Feito? Legal. Vamos começar. Temos um aplicativo simples criado no Express que utiliza uma instância no MongoDB Atlas para ler e gravar dados.

Temos duas principais APIs criadas no /blogs arquivo de rota.

...// GET - Fetches all blog posts for required userblogsRouter.route('/:user')    .get(async (req, res, next) => {        const blogs = await Blog.find({ user: req.params.user });        res.status(200).json({            blogs,        });    });// POST - Creates a new blog postblogsRouter.route('/')    .post(async (req, res, next) => {        const existingBlog = await Blog.findOne({ title: req.body.title });        if (!existingBlog) {            let newBlog = new Blog(req.body);            const result = await newBlog.save();            return res.status(200).json({                message: `Blog ${result.id} is successfully created`,                result,            });        }        res.status(200).json({            message: 'Blog with same title exists',        });    });    ...
./routes/blogs.js

Polvilhando alguns Redis Goodness

Começamos baixando o pacote npm redis para se conectar ao servidor redis local.

const mongoose = require('mongoose');const redis = require('redis');const util = require('util');const redisUrl = 'redis://127.0.0.1:6379';const client = redis.createClient(redisUrl);client.hget = util.promisify(client.hget);...
./services/cache.js

Nós fazemos uso do utils.promisify função para transformar o client.hget para retornar uma promessa em vez de um retorno de chamada. Você pode ler mais sobre promisification aqui.

A conexão Redis está no lugar. Antes de começarmos a escrever mais códigos de cache, vamos dar um passo atrás e tentar entender quais são os requisitos que precisamos atender e os prováveis ​​desafios que podemos enfrentar.

Nossa estratégia de armazenamento em cache deve ser capaz de abordar os seguintes pontos.

  • Armazenar em cache a solicitação de todas as postagens do blog para um usuário específico
  • Limpar cache sempre que uma nova postagem no blog é criada

Os prováveis ​​desafios dos quais devemos ter cuidado ao seguir nossa estratégia são:

  • A maneira correta de lidar com a criação de chaves para armazenar dados em cache
  • Lógica de expiração de cache e expiração forçada para manter a atualização do cache
  • Implementação reutilizável da lógica de armazenamento em cache

Tudo certo. Temos nossos pontos anotados e redis conectados. Para o próximo passo.

Substituindo a função padrão do Mongoose Exec

Queremos que nossa lógica de cache seja reutilizável. E não apenas reutilizável, também queremos que seja o primeiro ponto de verificação antes de fazer qualquer consulta ao banco de dados. Isso pode ser feito facilmente usando um simples truque de retrocesso na função exec do mangusto.

...const exec = mongoose.Query.prototype.exec;...mongoose.Query.prototype.exec = async function() {	... 	const result = await exec.apply(this, arguments);    console.log('Data Source: Database');    return result;}...
./services/cache.js

Utilizamos o objeto prototype do mangusto para adicionar nosso código lógico de cache como a primeira execução na consulta.

Adicionando cache como uma consulta

Para indicar quais consultas devem ser colocadas em cache, criamos uma consulta de mangusto. Nós fornecemos a capacidade de passar o user para ser usado como um hashkey através do options objeto.

...mongoose.Query.prototype.cache = function(options = {}) {    this.enableCache = true;    this.hashKey = JSON.stringify(options.key || 'default');    return this;};...
./services/cache.js

Feito isso, podemos usar facilmente o cache() consulta junto com as consultas que queremos armazenar em cache da seguinte maneira.

...    const blogs = await Blog                    .find({ user: req.params.user })                    .cache({ key: req.params.user });          ...
./routes/blogs.js

Criando a lógica do cache

Criamos uma consulta reutilizável comum para indicar quais consultas precisam ser armazenadas em cache. Vamos em frente e escreva a lógica central de armazenamento em cache.

...mongoose.Query.prototype.exec = async function() {    if (!this.enableCache) {        console.log('Data Source: Database');        return exec.apply(this, arguments);    }    const key = JSON.stringify(Object.assign({}, this.getQuery(), {        collection: this.mongooseCollection.name,    }));    const cachedValue = await client.hget(this.hashKey, key);    if (cachedValue) {        const parsedCache = JSON.parse(cachedValue);        console.log('Data Source: Cache');        return Array.isArray(parsedCache)                 ?  parsedCache.map(doc => new this.model(doc))                 :  new this.model(parsedCache);    }    const result = await exec.apply(this, arguments);        client.hmset(this.hashKey, key, JSON.stringify(result), 'EX', 300);    console.log('Data Source: Database');    return result;};...
./services/cache.js

Sempre que usamos o cache() consulta junto com nossa consulta principal, definimos o enableCache chave para ser verdade.

Se a chave for falsa, retornamos o principal exec consulta como padrão. Caso contrário, primeiro formamos a chave para buscar e armazenar / atualizar os dados do cache.

Nós usamos o collection nome juntamente com a consulta padrão como o nome da chave em nome da exclusividade. A chave de hash usada é o nome do user que já definimos anteriormente no cache() definição de função.

Os dados em cache são buscados usando o client.hget() função que requer a chave de hash e a chave conseqüente como parâmetros.

Nota: Nós sempre usamos JSON.parse() ao buscar quaisquer dados dos redis. E da mesma forma, usamos JSON.stringify() na chave e nos dados antes de armazenar qualquer coisa no redis. Isso é feito porque o redis não suporta estruturas de dados JSON.

Depois de obtermos os dados em cache, temos que transformar cada um dos objetos em cache em um modelo de mangusto. Isso pode ser feito simplesmente usando new this.model().

Se o cache não contiver os dados necessários, fazemos uma consulta no banco de dados. Depois, retornando os dados para a API, atualizamos o cache usando client.hmset(). Também definimos um tempo de expiração padrão do cache de 300 segundos. Isso é personalizável com base na sua estratégia de cache.

A lógica de armazenamento em cache está em vigor. Também definimos um tempo de expiração padrão. A seguir, forçaremos a expiração do cache sempre que uma nova postagem no blog for criada.

Expiração Forçada do Cache

Em certos casos, como quando um usuário cria uma nova postagem no blog, o usuário espera que a nova postagem esteja disponível quando buscar todas as postagens.

Para fazer isso, precisamos limpar o cache relacionado a esse usuário e atualizá-lo com novos dados. Então, temos que forçar a expiração. Podemos fazer isso invocando o del() função fornecida por redis.

...module.exports = {    clearCache(hashKey) {        console.log('Cache cleaned');        client.del(JSON.stringify(hashKey));    }}...
./services/cache.js

Também devemos ter em mente que estaremos forçando a expiração em várias rotas. Uma maneira extensível é usar esse clearCache() como um middleware e chame-o assim que qualquer consulta relacionada a uma rota concluir a execução.

const { clearCache } = require('../services/cache');module.exports = async (req, res, next) => {    // wait for route handler to finish running    await next();         clearCache(req.body.user);}
./middleware/cleanCache.js

Esse middleware pode ser facilmente chamado em uma rota específica da seguinte maneira.

...blogsRouter.route('/')    .post(cleanCache, async (req, res, next) => {        ...        }    ...
./routes/blogs.js

E nós terminamos. Eu concordo que era muito código. Mas com essa última parte, configuramos o redis com nosso aplicativo e resolvemos quase todos os desafios prováveis. É hora de ver nossa estratégia de cache em ação.

Redis em Ação

Nós fazemos uso de Carteiro como o cliente da API para ver nossa estratégia de cache em ação. Aqui vamos nós. Vamos executar as operações da API, uma por uma.

  1. Criamos uma nova postagem no blog usando o /blogs rota

Criação de nova postagem no blog

2. Em seguida, buscamos todas as postagens de blog relacionadas ao usuário tejaz

Buscando todas as postagens do blog tejaz

3. Buscamos todas as postagens do blog para o usuário tejaz mais uma vez.

Buscar todas as postagens do usuário tejaz Mais uma vez

Você pode ver claramente que, quando buscamos no cache, o tempo gasto diminuiu de 409ms para 24ms. Isso sobrecarrega sua API diminuindo o tempo gasto em quase 95%.

Além disso, podemos ver claramente que as operações de expiração e atualização de cache funcionam conforme o esperado.

Você pode encontrar o código fonte completo no redis-express pasta aqui.

tarique93102 / trechos de artigos

Repositório contendo aplicativos de protótipo e trechos de código relacionados à disseminação de conceitos – tarique93102 / article-snippets

Conclusão

O armazenamento em cache é uma etapa obrigatória para qualquer aplicativo com desempenho eficiente e uso intensivo de dados. O Redis ajuda você a conseguir isso facilmente em seus aplicativos da web. É uma ferramenta super poderosa e, se usada corretamente, pode definitivamente proporcionar uma excelente experiência para desenvolvedores e usuários ao redor.

Você pode encontrar o conjunto completo de comandos redis aqui. Você pode usá-lo com redis-cli para monitorar seus dados de cache e processos de aplicativos.

As possibilidades oferecidas por qualquer tecnologia em particular são verdadeiramente infinitas. Se você tiver alguma dúvida, entre em contato comigo em LinkedIn.

Enquanto isso, continue codificando.



Fonte

Avalie este post

Clique nas estrelas

Média da classificação 0 / 5. Número de votos: 0

Nenhum voto até agora! Seja o primeiro a avaliar este post.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *