Colocar dados em cache com o Memorystore

As aplicações Web escaláveis de elevado desempenho usam frequentemente uma cache de dados distribuída na memória antes ou em vez de um armazenamento persistente robusto para algumas tarefas. Recomendamos que use o Memorystore para Redis como serviço de colocação em cache. Tenha em atenção que o Memorystore for Redis não oferece um nível gratuito. Consulte os preços do Memorystore para ver detalhes.

Antes de começar, certifique-se de que a sua app se mantém dentro das cotas do Memorystore para Redis.

Quando usar uma cache de memória

Os dados de sessões, as preferências do utilizador e outros dados devolvidos por consultas de páginas Web são bons candidatos para o armazenamento em cache. Em geral, se uma consulta executada com frequência devolver um conjunto de resultados que não precisam de aparecer imediatamente na sua app, pode colocar os resultados em cache. Os pedidos subsequentes podem verificar a cache e apenas consultar a base de dados se os resultados estiverem ausentes ou tiverem expirado.

Se armazenar um valor apenas no Memorystore sem fazer uma cópia de segurança no armazenamento persistente, certifique-se de que a sua aplicação se comporta de forma aceitável se o valor expirar e for removido da cache. Por exemplo, se a ausência repentina dos dados de sessão de um utilizador fizer com que a sessão funcione incorretamente, esses dados devem provavelmente ser armazenados na base de dados, além do Memorystore.

Compreender as autorizações do Memorystore

Todas as interações com um serviço Google Cloud têm de ser autorizadas. Por exemplo, para interagir com uma base de dados Redis alojada pelo Memorystore, a sua app tem de fornecer as credenciais de uma conta autorizada a aceder ao Memorystore.

Por predefinição, a sua app fornece as credenciais da conta de serviço predefinida do App Engine, que está autorizada a aceder a bases de dados no mesmo projeto que a sua app.

Se alguma das seguintes condições for verdadeira, tem de usar uma técnica de autenticação alternativa que forneça explicitamente credenciais:

  • A sua app e a base de dados do Memorystore estão emGoogle Cloud projetos diferentes.

  • Alterou as funções atribuídas à conta de serviço predefinida do App Engine.

Para informações sobre técnicas de autenticação alternativas, consulte o artigo Configurar a autenticação para aplicações de produção de servidor a servidor.

Vista geral da utilização do Memorystore

Para usar o Memorystore na sua app:

  1. Configure o Memorystore for Redis, o que requer que crie uma instância do Redis no Memorystore e crie um acesso a VPC sem servidor que a sua app usa para comunicar com a instância do Redis.

  2. Instale uma biblioteca cliente para o Redis e use comandos do Redis para colocar dados em cache.

    O Memorystore for Redis é compatível com qualquer biblioteca de cliente para Redis.

    Go

    Este guia descreve a utilização da biblioteca cliente redigo para enviar comandos Redis a partir da sua app.

    Java

    Este guia descreve a utilização da biblioteca cliente Jedis para enviar comandos Redis a partir da sua app. Para ver detalhes sobre a utilização do Jedis, consulte a wiki do Jedis.

    Node.js

    Este guia descreve a utilização da biblioteca cliente node_redis para enviar comandos Redis a partir da sua app.

    PHP

    Este guia descreve a utilização da biblioteca cliente PHPRedis para enviar comandos Redis a partir da sua app.

    Python

    Este guia descreve a utilização da biblioteca cliente redis-py 3.0 para enviar comandos Redis a partir da sua app.

    Ruby

    Este guia descreve a utilização da biblioteca cliente redis-rb para enviar comandos Redis a partir da sua app.

  3. Teste as atualizações.

  4. Implemente a sua app no App Engine.

Configurar o Memorystore for Redis

Para configurar o Memorystore for Redis:

  1. Crie uma instância do Redis no Memorystore.

    Quando lhe for pedido que selecione uma região para a sua instância do Redis, selecione a mesma região em que a sua app do App Engine está localizada.

  2. Tome nota do endereço IP e do número da porta da instância do Redis que criar. Vai usar estas informações quando criar um cliente Redis no seu código.

  3. Associe o App Engine a uma rede VPC. A sua app só pode comunicar com o Memorystore através de um conector de VPC.

    Certifique-se de que adiciona as informações de ligação da VPC ao ficheiro app.yaml, conforme descrito em Configurar a sua app para usar o conetor.

Instalar dependências

Go

Para disponibilizar a biblioteca de cliente redigo à sua app quando é executada no App Engine, adicione a biblioteca às dependências da app. Por exemplo, se usar um ficheiro go.mod para declarar dependências, adicione a seguinte linha ao ficheiro go.mod:

module github.com/GoogleCloudPlatform/golang-samples/tree/master/memorystore/redis

Saiba mais sobre como especificar dependências para a sua app Go.

Java

Para disponibilizar a biblioteca de cliente Jedis à sua app quando é executada no App Engine, adicione a biblioteca às dependências da sua app. Por exemplo, se usar o Maven, adicione a seguinte dependência no ficheiro pom.xml:

<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>5.1.0</version>
</dependency>

Node.js

Para disponibilizar a biblioteca de cliente node_redis à sua app quando é executada no App Engine, adicione a biblioteca ao ficheiro package.json da app.

Por exemplo:

{
  "name": "memorystore-redis",
  "description": "An example of using Memorystore(Redis) with Node.js",
  "version": "0.0.1",
  "private": true,
  "license": "Apache Version 2.0",
  "author": "Google Inc.",
  "engines": {
    "node": ">=16.0.0"
  },
  "dependencies": {
    "redis": "^4.0.0"
  }
}

Saiba mais sobre como especificar dependências para a sua app Node.js.

PHP

Para disponibilizar a biblioteca do cliente PHPRedis à sua app quando é executada no App Engine, adicione a extensão redis.so ao ficheiro php.ini da app. Por exemplo:

; Enable the Redis extension on App Engine
extension=redis.so

Para mais informações sobre como ativar extensões PHP no App Engine, consulte as extensões carregáveis dinamicamente.

Python

Para disponibilizar a biblioteca cliente redis-py à sua app quando é executada no App Engine, adicione a seguinte linha ao ficheiro requirements.txt da sua app:

  redis

O tempo de execução do Python 3 do App Engine carrega automaticamente todas as bibliotecas do ficheiro requirements.txt da sua app quando implementa a app.

Para o desenvolvimento local, recomendamos que instale dependências num ambiente virtual, como o venv.

Ruby

Para disponibilizar a biblioteca de cliente redis-rb à sua app quando é executada no App Engine, adicione a biblioteca ao ficheiro Gemfile da sua app.

  source "https://cloud.google.com/memorystore"

  gem "redis-rb"

Criar um cliente Redis

Para interagir com uma base de dados Redis, o seu código tem de criar um cliente Redis para gerir a ligação à sua base de dados Redis. As secções seguintes descrevem como criar um cliente Redis com a biblioteca de cliente Redis.

Especificar variáveis de ambiente

A biblioteca cliente do Redis usa duas variáveis de ambiente para criar o URL da sua base de dados Redis:

  • Uma variável para identificar o endereço IP da base de dados Redis que criou no Memorystore.
  • Uma variável para identificar o número da porta da base de dados Redis que criou no Memorystore.

Recomendamos que defina estas variáveis no ficheiro app.yaml da sua app em vez de as definir diretamente no código. Isto facilita a execução da sua app em diferentes ambientes, como um ambiente local e o App Engine. Saiba mais acerca das variáveis de ambiente na app.yamlpágina de referência.

Go

Por exemplo, adicione as seguintes linhas ao ficheiro app.yaml:

  env_variables:
       REDISHOST: '10.112.12.112'
       REDISPORT: '6379'

Java

Por exemplo, adicione as seguintes linhas ao ficheiro app.yaml:

  env_variables:
       redis.host: '10.112.12.112'
       redis.port: '6379'

Node.js

Por exemplo, adicione as seguintes linhas ao ficheiro app.yaml:

  env_variables:
       REDISHOST: '10.112.12.112'
       REDISPORT: '6379'

PHP

Por exemplo, adicione as seguintes linhas ao ficheiro app.yaml:

  env_variables:
       REDIS_HOST: '10.112.12.112'
       REDIS_PORT: '6379'

Python

Por exemplo, adicione as seguintes linhas ao ficheiro app.yaml:

  env_variables:
       REDISHOST: '10.112.12.112'
       REDISPORT: '6379'

Ruby

Por exemplo, adicione as seguintes linhas ao ficheiro app.yaml:

  env_variables:
       REDISHOST: '10.112.12.112'
       REDISPORT: '6379'

Importar o Redis e criar o cliente

Go

Depois de definir as variáveis de ambiente REDISHOST e REDISPORT, use as seguintes linhas para importar a biblioteca redigo, criar um conjunto de ligações e, em seguida, obter um cliente Redis do conjunto:


// Command redis is a basic app that connects to a managed Redis instance.
package main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/gomodule/redigo/redis"
)

var redisPool *redis.Pool

func incrementHandler(w http.ResponseWriter, r *http.Request) {
	conn := redisPool.Get()
	defer conn.Close()

	counter, err := redis.Int(conn.Do("INCR", "visits"))
	if err != nil {
		http.Error(w, "Error incrementing visitor counter", http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "Visitor number: %d", counter)
}

func main() {
	redisHost := os.Getenv("REDISHOST")
	redisPort := os.Getenv("REDISPORT")
	redisAddr := fmt.Sprintf("%s:%s", redisHost, redisPort)

	const maxConnections = 10
	redisPool = &redis.Pool{
		MaxIdle: maxConnections,
		Dial:    func() (redis.Conn, error) { return redis.Dial("tcp", redisAddr) },
	}

	http.HandleFunc("/", incrementHandler)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Printf("Listening on port %s", port)
	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatal(err)
	}
}

Java

Quando usa a biblioteca Jedis, recomendamos que crie um JedisPool e, em seguida, use o conjunto para criar um cliente. As seguintes linhas de código usam as variáveis de ambiente redis.host e redis.port que definiu anteriormente para criar um conjunto:


package com.example.redis;

import java.io.IOException;
import java.util.Properties;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

@WebListener
public class AppServletContextListener implements ServletContextListener {

  private Properties config = new Properties();

  private JedisPool createJedisPool() throws IOException {
    String host;
    Integer port;
    config.load(
        Thread.currentThread()
            .getContextClassLoader()
            .getResourceAsStream("application.properties"));
    host = config.getProperty("redis.host");
    port = Integer.valueOf(config.getProperty("redis.port", "6379"));

    JedisPoolConfig poolConfig = new JedisPoolConfig();
    // Default : 8, consider how many concurrent connections into Redis you will need under load
    poolConfig.setMaxTotal(128);

    return new JedisPool(poolConfig, host, port);
  }

  @Override
  public void contextDestroyed(ServletContextEvent event) {
    JedisPool jedisPool = (JedisPool) event.getServletContext().getAttribute("jedisPool");
    if (jedisPool != null) {
      jedisPool.destroy();
      event.getServletContext().setAttribute("jedisPool", null);
    }
  }

  // Run this before web application is started
  @Override
  public void contextInitialized(ServletContextEvent event) {
    JedisPool jedisPool = (JedisPool) event.getServletContext().getAttribute("jedisPool");
    if (jedisPool == null) {
      try {
        jedisPool = createJedisPool();
        event.getServletContext().setAttribute("jedisPool", jedisPool);
      } catch (IOException e) {
        // handle exception
      }
    }
  }
}

Para criar um cliente a partir do conjunto, use o método JedisPool.getResource(). Por exemplo:


package com.example.redis;

import java.io.IOException;
import java.net.SocketException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

@WebServlet(name = "Track visits", value = "")
public class VisitCounterServlet extends HttpServlet {

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    try {
      JedisPool jedisPool = (JedisPool) req.getServletContext().getAttribute("jedisPool");

      if (jedisPool == null) {
        throw new SocketException("Error connecting to Jedis pool");
      }
      Long visits;

      try (Jedis jedis = jedisPool.getResource()) {
        visits = jedis.incr("visits");
      }

      resp.setStatus(HttpServletResponse.SC_OK);
      resp.getWriter().println("Visitor counter: " + String.valueOf(visits));
    } catch (Exception e) {
      resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
    }
  }
}

Node.js

Depois de definir as variáveis de ambiente REDISHOST e REDISPORT, pode usar as seguintes linhas para importar a biblioteca node_redis e criar um cliente Redis:

'use strict';
const http = require('http');
const redis = require('redis');

const REDISHOST = process.env.REDISHOST || 'localhost';
const REDISPORT = process.env.REDISPORT || 6379;

const client = redis.createClient(REDISPORT, REDISHOST);
client.on('error', err => console.error('ERR:REDIS:', err));

// create a server
http
  .createServer((req, res) => {
    // increment the visit counter
    client.incr('visits', (err, reply) => {
      if (err) {
        console.log(err);
        res.status(500).send(err.message);
        return;
      }
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end(`Visitor number: ${reply}\n`);
    });
  })
  .listen(8080);

PHP

Depois de definir as variáveis de ambiente REDIS_HOST e REDIS_PORT, pode usar as seguintes linhas para criar um cliente Redis:

<?php
/**
 * Copyright 2019 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Only serve traffic from "/"
switch (@parse_url($_SERVER['REQUEST_URI'])['path']) {
    case '/':
        break;
    default:
        http_response_code(404);
        exit('Not Found');
}

// Connect to Memorystore from App Engine.
if (!$host = getenv('REDIS_HOST')) {
    throw new Exception('The REDIS_HOST environment variable is required');
}

# Memorystore Redis port defaults to 6379
$port = getenv('REDIS_PORT') ?: '6379';

try {
    $redis = new Redis();
    $redis->connect($host, $port);
} catch (Exception $e) {
    return print('Error: ' . $e->getMessage());
}

$value = $redis->incr('counter');

printf('Visitor number: %s', $value);

Python

Depois de definir as variáveis de ambiente REDISHOST e REDISPORT, use as linhas seguintes para importar a biblioteca redis-py e criar um cliente:

  import redis

  redis_host = os.environ.get('REDISHOST', 'localhost')
  redis_port = int(os.environ.get('REDISPORT', 6379))
  redis_client = redis.Redis(host=redis_host, port=redis_port)

Se usou uma versão mais antiga do redis-py para outras apps, pode ter usado a classe StrictClient em vez de Client. No entanto, o redis-py agora recomenda Client em vez de StrictClient.

Ruby

Não existem informações adicionais para esta duração.

Usar comandos Redis para armazenar e obter dados na cache

Embora a base de dados Redis do Memorystore suporte a maioria dos comandos Redis, só precisa de usar alguns comandos para armazenar e obter dados da cache. A tabela seguinte sugere comandos do Redis que pode usar para colocar dados em cache. Para ver como chamar estes comandos a partir da sua app, consulte a documentação da biblioteca de cliente.

Tarefa Comando Redis
Crie uma entrada na cache de dados e
defina um tempo de validade para a entrada
SETNX
MSETNX
Recupere dados da cache GET
MGET
Substituir valores de cache existentes SET
MSET
Aumentar ou diminuir valores numéricos da cache INCR
INCRBY
DECR
DECRBY
Elimine entradas da cache DEL
UNLINK
Suportam interações simultâneas com a cache Veja detalhes sobre as transações Redis.

Para Python, a biblioteca cliente redis-py requer que todas as transações ocorram num pipeline.

Testar as atualizações

Quando testa a sua app localmente, considere executar uma instância local do Redis para evitar interagir com dados de produção (o Memorystore não fornece um emulador). Para instalar e executar o Redis localmente, siga as instruções na documentação do Redis. Tenha em atenção que, atualmente, não é possível executar o Redis localmente no Windows.

Para mais informações sobre como testar as suas apps, consulte o artigo Testar e implementar a sua aplicação.

Implementar a sua app

Assim que a app estiver a ser executada no servidor de desenvolvimento local sem erros:

  1. Teste a app no App Engine.

  2. Se a app for executada sem erros, use a divisão de tráfego para aumentar gradualmente o tráfego para a app atualizada. Monitorize atentamente a app para detetar problemas na base de dados antes de encaminhar mais tráfego para a app atualizada.