Mettre en cache des données avec Memorystore

Pour certaines tâches, les applications Web évolutives à hautes performances utilisent souvent un cache de données en mémoire distribué en plus ou à la place de solutions robustes de stockage persistant. Nous vous recommandons d'utiliser Memorystore pour Redis en tant que service de mise en cache. Notez que Memorystore pour Redis ne propose pas de version gratuite. Pour en savoir plus, consultez la page Tarifs de Memorystore.

Avant de commencer, assurez-vous que votre application reste dans les quotas attribués à Memorystore pour Redis.

Quand utiliser un cache mémoire ?

Les données de session, les préférences utilisateur et les autres données renvoyées par les requêtes pour les pages Web sont de bons candidats pour la mise en cache. En général, si une requête fréquemment exécutée renvoie un ensemble de résultats qui n'ont pas besoin d'apparaître immédiatement dans votre application, vous pouvez mettre en cache ces résultats. Les requêtes suivantes peuvent consulter le cache, et n'effectuer la requête de base de données que si les résultats ne s'y trouvent pas ou sont arrivés à expiration.

Si vous stockez une valeur uniquement dans Memorystore sans la sauvegarder dans un espace de stockage persistant, assurez-vous que votre application réagit convenablement si la valeur expire et qu'elle est supprimée du cache. Par exemple, si l'absence soudaine des données liées à une session d'utilisateur est susceptible de causer un dysfonctionnement de la session, il serait préférable de stocker ces données dans la base de données en plus de Memorystore.

Comprendre les autorisations de Memorystore

Chaque interaction avec un service Google Cloud doit être autorisée. Par exemple, pour interagir avec une base de données Redis hébergée par Memorystore, votre application doit fournir les identifiants d'un compte autorisé à accéder à Memorystore.

Par défaut, votre application fournit les identifiants du compte de service App Engine par défaut, autorisé à accéder aux bases de données du même projet que votre application.

Si l'une des conditions suivantes est remplie, vous devez utiliser une autre technique d'authentification qui fournit explicitement des identifiants :

  • Votre application et la base de données Memorystore font partie de différents projets Google Cloud.

  • Vous avez modifié les rôles attribués au compte de service App Engine par défaut.

Pour plus d'informations sur les autres techniques d'authentification, consultez la page Configurer l'authentification pour des applications de production serveur à serveur.

Utiliser Memorystore

Pour utiliser Memorystore dans votre application, procédez comme suit :

  1. Configurez Memorystore pour Redis. Pour cela, vous devez créer une instance Redis sur Memorystore et un accès au VPC sans serveur qui permettra à votre application de communiquer avec l'instance Redis.

  2. Installez une bibliothèque cliente pour Redis et mettez en cache les données à l'aide des commandes Redis.

    Memorystore pour Redis est compatible avec toute bibliothèque cliente pour Redis.

    Go

    Ce guide explique comment envoyer des commandes Redis depuis votre application à l'aide de la bibliothèque cliente redigo.

    Java

    Ce guide décrit comment utiliser la bibliothèque cliente Jedis pour envoyer des commandes Redis depuis votre application. Pour en savoir plus sur l'utilisation de Jedis, consultez le wiki dédié à Jedis.

    Node.js

    Ce guide explique comment envoyer des commandes Redis depuis votre application à l'aide de la bibliothèque cliente node_redis.

    PHP

    Ce guide explique comment envoyer des commandes Redis depuis votre application à l'aide de la bibliothèque cliente PHPRedis.

    Python

    Ce guide explique comment envoyer des commandes Redis depuis votre application à l'aide de la bibliothèque cliente redis-py 3.0.

    Ruby

    Ce guide explique comment envoyer des commandes Redis depuis votre application à l'aide de la bibliothèque cliente redis-rb.

  3. Testez vos mises à jour.

  4. Déployez l'application sur App Engine.

Configurer Memorystore pour Redis

Pour configurer Memorystore pour Redis :

  1. Créez une instance Redis dans Memorystore.

    Lorsque vous êtes invité à choisir une région pour votre instance Redis, sélectionnez la région dans laquelle se trouve votre application App Engine.

  2. Prenez note de l'adresse IP et du numéro de port de l'instance Redis que vous créez. Vous utiliserez ces informations au moment de créer un client Redis dans votre code.

  3. Associez votre application App Engine à un réseau VPC. Votre application ne peut communiquer avec Memorystore que via un connecteur VPC.

    Veillez à ajouter les informations de connexion VPC à votre fichier app.yaml comme décrit dans la section Configurer une application pour utiliser un connecteur.

Installer les dépendances

Go

Pour que la bibliothèque cliente Redigo soit disponible pour votre application lorsqu'elle s'exécute dans App Engine, ajoutez-la aux dépendances de votre application. Par exemple, si vous utilisez un fichier go.mod pour déclarer des dépendances, ajoutez la ligne suivante à votre fichier go.mod :

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

Découvrez comment spécifier des dépendances pour votre application Go.

Java

Pour que la bibliothèque cliente Jedis soit disponible pour votre application lorsqu'elle s'exécute dans App Engine, ajoutez-la aux dépendances de l'application. Par exemple, si vous utilisez Maven, ajoutez la dépendance suivante à votre fichier pom.xml :

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

Node.js

Pour que la bibliothèque cliente node_redis soit disponible pour votre application lorsqu'elle s'exécute dans App Engine, ajoutez-la au fichier package.json de votre application.

Exemple :

{
  "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"
  }
}

Découvrez comment spécifier des dépendances pour votre application Node.js.

PHP

Pour que la bibliothèque cliente PHPRedis soit disponible pour votre application lorsqu'elle s'exécute dans App Engine, ajoutez l'extension redis.so au fichier php.ini de votre application. Exemple :

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

Pour en savoir plus sur l'activation des extensions PHP dans App Engine, consultez la section Extensions chargeables dynamiquement.

Python

Pour que la bibliothèque cliente redis-py soit disponible pour votre application lorsqu'elle s'exécute dans App Engine, ajoutez la ligne suivante au fichier requirements.txt de votre application :

  redis

L'environnement d'exécution Python 3 d'App Engine importe automatiquement toutes les bibliothèques figurant dans le fichier requirements.txt au moment du déploiement de l'application.

Pour le développement local, nous vous recommandons d'installer des dépendances dans un environnement virtuel tel que venv.

Ruby

Pour que la bibliothèque cliente redis-rb soit disponible pour votre application lorsqu'elle s'exécute dans App Engine, ajoutez-la au fichier Gemfile de votre application.

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

  gem "redis-rb"

Créer un client Redis

Pour interagir avec une base de données Redis, votre code doit créer un client Redis qui gèrera la connexion à votre base de données Redis. Les sections suivantes décrivent la création d'un client Redis à l'aide de la bibliothèque cliente Redis.

Spécifier des variables d'environnement

La bibliothèque cliente Redis utilise deux variables d'environnement pour assembler l'URL de votre base de données Redis :

  • Une variable permettant d'identifier l'adresse IP de la base de données Redis que vous avez créée dans Memorystore
  • Une variable permettant d'identifier le numéro de port de la base de données Redis que vous avez créée dans Memorystore

Nous vous recommandons de définir ces variables dans le fichier app.yaml de votre application au lieu de les définir directement dans votre code. Cela facilitera l'exécution de votre application dans différents environnements, tels qu'un environnement local et App Engine. Pour en savoir plus sur les variables d'environnement, consultez la page de référence sur app.yaml.

Go

Par exemple, ajoutez les lignes suivantes à votre fichier app.yaml :

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

Java

Par exemple, ajoutez les lignes suivantes à votre fichier app.yaml :

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

Node.js

Par exemple, ajoutez les lignes suivantes à votre fichier app.yaml :

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

PHP

Par exemple, ajoutez les lignes suivantes à votre fichier app.yaml :

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

Python

Par exemple, ajoutez les lignes suivantes à votre fichier app.yaml :

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

Ruby

Par exemple, ajoutez les lignes suivantes à votre fichier app.yaml :

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

Importer Redis et créer le client

Go

Après avoir défini les variables d'environnement REDISHOST et REDISPORT, utilisez les lignes suivantes pour importer la bibliothèque redigo, créer un pool de connexions, puis extraire un client Redis du pool :


// 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

Lorsque vous utilisez la bibliothèque Jedis, nous vous recommandons de créer un JedisPool, puis d'utiliser le pool pour créer un client. Les lignes de code suivantes utilisent les variables d'environnement redis.host et redis.port, que vous avez définies précédemment pour créer un pool :


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
      }
    }
  }
}

Pour créer un client à partir du pool, utilisez la méthode JedisPool.getResource(). Exemple :


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

Après avoir défini les variables d'environnement REDISHOST et REDISPORT, utilisez les lignes suivantes pour importer la bibliothèque node_redis et créer un client 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

Après avoir défini les variables d'environnement REDIS_HOST et REDIS_PORT, vous pouvez utiliser les lignes suivantes pour créer un client 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

Après avoir défini les variables d'environnement REDISHOST et REDISPORT, utilisez les lignes suivantes pour importer la bibliothèque redis-py et créer un client :

  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)

Si vous avez utilisé une version plus ancienne de redis-py pour d'autres applications, il est possible que vous ayez utilisé la classe StrictClient au lieu de Client. Cependant, redis-py recommande désormais d'utiliser Client au lieu de StrictClient.

Ruby

Aucune information supplémentaire pour cet environnement d'exécution.

Utiliser les commandes Redis pour stocker et récupérer des données dans le cache

Bien que la base de données Redis Memorystore soit compatible avec la plupart des commandes Redis, vous n'avez besoin que de quelques commandes pour stocker et récupérer des données à partir du cache. Le tableau suivant suggère des commandes Redis que vous pouvez utiliser pour mettre en cache des données. Pour savoir comment appeler ces commandes depuis votre application, consultez la documentation de votre bibliothèque cliente.

Tâche Commande Redis
Créer une entrée dans le cache de données et
définir un délai d'expiration pour l'entrée
SETNX
MSETNX
Récupérer les données du cache GET
MGET
Remplacer les valeurs de cache existantes SET
MSET
Augmenter ou diminuer les valeurs du cache numérique INCR
INCRBY
DECR
DECRBY
Supprimer les entrées du cache DEL
UNLINK
Accepter les interactions simultanées avec le cache Consultez les informations concernant les transactions avec Redis.

Pour Python, la bibliothèque cliente redis-py exige que toutes les transactions aient lieu dans un pipeline.

Tester vos mises à jour

Lorsque vous testez votre application en local, songez à exécuter une instance locale de Redis pour éviter toute interaction avec les données de production (Memorystore ne fournit pas d'émulateur). Pour installer et exécuter Redis en local, suivez les instructions de la documentation Redis. Il est actuellement impossible d'exécuter Redis en local sous Windows.

Pour en savoir plus sur les tests de vos applications, consultez la page Tester et déployer une application.

Déployer l'application

Une fois que votre application est exécutée sur le serveur de développement local sans erreur :

  1. Testez l'application sur App Engine.

  2. Si l'application s'exécute sans erreur, répartissez le trafic pour augmenter lentement le trafic de votre application mise à jour. Surveillez attentivement les éventuels problèmes de base de données avant d'acheminer davantage de trafic vers l'application mise à jour.