Memorizzazione nella cache dei dati con Memorystore

Le applicazioni web scalabili ad alte prestazioni spesso utilizzano una cache di dati in memoria distribuita davanti o al posto di un'archiviazione permanente robusta per alcune attività. Consigliamo di utilizzare Memorystore for Redis come servizio di memorizzazione nella cache. Tieni presente che Memorystore for Redis non offre un livello gratuito. Per i dettagli, vedi Prezzi di Memorystore.

Prima di iniziare, assicurati che la tua app rimanga entro le quote di Memorystore for Redis.

Quando utilizzare la cache di memoria

I dati relativi alle sessioni, le preferenze degli utenti e altri dati restituiti dalle query per le pagine web sono ideali per la memorizzazione nella cache. In generale, se una query eseguita di frequente restituisce un insieme di risultati che non devono essere visualizzati immediatamente nella tua app, puoi memorizzarli nella cache. Le richieste successive possono controllare la cache ed eseguire query sul database solo se i risultati sono assenti o sono scaduti.

Se archivi un valore solo in Memorystore senza eseguire il backup in un'archiviazione permanente, assicurati che l'applicazione si comporti in modo accettabile se il valore scade e viene rimosso dalla cache. Ad esempio, se l'assenza improvvisa di dati sulla sessione di un utente causa il malfunzionamento della sessione, è probabile che tali dati vengano archiviati nel database in aggiunta a Memorystore.

Informazioni sulle autorizzazioni Memorystore

Ogni interazione con un servizio Google Cloud deve essere autorizzata. Ad esempio, per interagire con un database Redis ospitato da Memorystore, la tua app deve fornire le credenziali di un account autorizzato ad accedere a Memorystore.

Per impostazione predefinita, la tua app fornisce le credenziali dell'account di servizio predefinito di App Engine, che è autorizzato ad accedere ai database nello stesso progetto della tua app.

Se una delle seguenti condizioni è vera, devi utilizzare una tecnica di autenticazione alternativa che fornisca esplicitamente le credenziali:

  • L'app e il database Memorystore si trovano in progetti Google Cloud diversi.

  • Hai modificato i ruoli assegnati all'account di servizio App Engine predefinito.

Per informazioni sulle tecniche di autenticazione alternative, consulta Configurazione dell'autenticazione per applicazioni di produzione da server a server.

Panoramica dell'utilizzo di Memorystore

Per utilizzare Memorystore nella tua app:

  1. Configura Memorystore for Redis, che richiede la creazione di un'istanza Redis su Memorystore e l'accesso VPC serverless, utilizzati dalla tua app per comunicare con l'istanza Redis.

  2. Installa una libreria client per Redis e utilizza i comandi Redis per memorizzare nella cache i dati.

    Memorystore for Redis è compatibile con qualsiasi libreria client per Redis. Questa guida descrive l'uso della libreria client di Jedis per inviare comandi Redis dall'app. Per i dettagli sull'utilizzo di Jedis, consulta il wiki di Jedis.

  3. Testa gli aggiornamenti.

  4. Esegui il deployment dell'app in App Engine.

Configurazione di Memorystore for Redis

Per configurare Memorystore for Redis:

  1. Crea un'istanza Redis in Memorystore.

    Quando ti viene chiesto di selezionare un'area geografica per l'istanza Redis, seleziona la stessa area geografica in cui si trova l'app App Engine.

  2. Prendi nota dell'indirizzo IP e del numero di porta dell'istanza Redis che crei. Utilizzerai queste informazioni quando crei un client Redis nel tuo codice.

  3. Connetti App Engine a una rete VPC. La tua app può comunicare con Memorystore solo tramite un connettore VPC.

    Assicurati di aggiungere le informazioni sulla connessione VPC al file app.yaml, come descritto in Configurazione dell'app utilizzando il connettore.

Installazione delle dipendenze

Per rendere disponibile la libreria client Jedis per la tua app quando viene eseguita in App Engine, aggiungi la libreria alle dipendenze dell'app. Ad esempio, se utilizzi Maven aggiungi la seguente dipendenza nel file pom.xml:
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>4.2.2</version>
</dependency>

Creazione di un client Redis

Per interagire con un database Redis, devi creare un client Redis per gestire la connessione al database Redis. Le seguenti sezioni descrivono la creazione di un client Redis mediante la libreria client di Jedis.

Specifica delle variabili di ambiente

La libreria client di Jedis utilizza due variabili di ambiente per creare l'URL del database Redis:

  • Variabile per identificare l'indirizzo IP del database Redis creato in Memorystore.
  • Variabile per identificare il numero di porta del database Redis creato in Memorystore.

Ti consigliamo di definire queste variabili nel file app.yaml dell'app anziché definirle direttamente nel codice. In questo modo è più semplice eseguire l'app in ambienti diversi, ad esempio un ambiente locale e App Engine.

Ad esempio, aggiungi le seguenti righe al file app.yaml:

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

Importazione di Jedis e creazione del client

Quando utilizzi la libreria Jedis, ti consigliamo di creare un JedisPool, quindi di utilizzare il pool per creare un client. Le seguenti righe di codice utilizzano le variabili di ambiente redis.host e redis.host definite in precedenza per creare 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
      }
    }
  }
}

Per creare un client dal pool, utilizza il metodo JedisPool.getResource(). Ad esempio:


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());
    }
  }
}

Utilizzo dei comandi Redis per archiviare e recuperare i dati nella cache

Mentre il database Memorystore Redis supporta la maggior parte dei comandi Redis, è sufficiente utilizzare alcuni comandi per archiviare e recuperare i dati dalla cache. La tabella seguente suggerisce i comandi Redis che puoi utilizzare per memorizzare nella cache i dati. Per scoprire come chiamare questi comandi dall'app, consulta la documentazione della libreria client.

Attività Comando Redis
Crea una voce nella cache dei dati e
imposta una scadenza per la voce
SETNX
MSETNX
Recuperare i dati dalla cache RICHIEDI
MGET
Sostituisci i valori della cache esistenti IMPOSTA
MSET
Incremento o riduzione dei valori numerici della cache INCR
INCRBY
DECR
DECRBY
Elimina voci dalla cache CANC
SCOLLEGA
Supporto di interazioni simultanee con la cache Vedi i dettagli sulle transazioni Redis.

Test degli aggiornamenti

Quando testi la tua app localmente, puoi eseguire un'istanza locale di Redis per evitare di interagire con i dati di produzione (Memorystore non fornisce un emulatore). Per installare ed eseguire Redis localmente, segui le istruzioni nella documentazione di Redis. Tieni presente che al momento non è possibile eseguire Redis localmente su Windows.

Deployment dell'applicazione

Quando la tua app è in esecuzione nel server di sviluppo locale senza errori:

  1. Testa l'app su App Engine.

  2. Se l'app viene eseguita senza errori, utilizza la suddivisione del traffico per aumentare lentamente il traffico dell'app aggiornata. Monitora attentamente l'app per individuare eventuali problemi del database prima di indirizzare un volume maggiore di traffico all'app aggiornata.