Verbindung zu einer Redis-Instanz über eine flexible App Engine-Umgebung herstellen

App Engine-Anwendungen müssen sich im selben autorisierten Netzwerk wie die Redis-Instanz befinden, um darauf zugreifen zu können.

Einrichtung

Wenn Sie die Google Cloud CLI bereits installiert und eine Redis-Instanz erstellt haben, können Sie diese Schritte überspringen.

  1. Installieren Sie die gcloud CLI und initialisieren Sie:

    gcloud init
    
  2. Folgen Sie der Schnellstartanleitung zum Erstellen einer Redis-Instanz. Notieren Sie sich die Zone, die IP-Adresse und den Port der Redis-Instanz.

Beispielanwendung

Diese HTTP-Server-Beispielanwendung stellt von einer Instanz der flexiblen App Engine-Umgebung eine Verbindung zu einer Redis-Instanz her.

Klonen Sie das Repository für die gewünschte Programmiersprache und rufen Sie den Ordner auf, der den Beispielcode enthält:

Einfach loslegen (Go)

git clone https://github.com/GoogleCloudPlatform/golang-samples
cd golang-samples/memorystore/redis

Java

git clone https://github.com/GoogleCloudPlatform/java-docs-samples
cd java-docs-samples/memorystore/redis

Node.js

git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples
cd nodejs-docs-samples/memorystore/redis

Python

git clone https://github.com/GoogleCloudPlatform/python-docs-samples
cd python-docs-samples/memorystore/redis

Diese Beispielanwendung erhöht einen Redis-Zähler jedes Mal, wenn auf den Endpunkt / zugegriffen wird.

Einfach loslegen (Go)

Diese Anwendung verwendet den github.com/gomodule/redigo/redis-Client. Installieren Sie diesen mit dem folgenden Befehl:

go get github.com/gomodule/redigo/redis

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

Diese Anwendung basiert auf dem Jetty-Servlet 3.1.

Sie verwendet die Jedis-Bibliothek:

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

Die Klasse AppServletContextListener wird zum Erstellen eines langlebigen Redis-Verbindungspools verwendet:


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

Die Klasse VisitCounterServlet ist ein Web-Servlet, das einen Redis-Zähler schrittweise erhöht.


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

Diese Anwendung verwendet das Modul redis.

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

'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);

Python

Diese Anwendung verwendet Flask für das Web-Serving und das redis-py-Paket für die Kommunikation mit der Redis-Instanz.

Flask==3.0.0
gunicorn==22.0.0
redis==5.0.1
Werkzeug==3.0.1
import logging
import os

from flask import Flask
import redis

app = Flask(__name__)

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

@app.route("/")
def index():
    value = redis_client.incr("counter", 1)
    return f"Visitor number: {value}"

@app.errorhandler(500)
def server_error(e):
    logging.exception("An error occurred during a request.")
    return (
        """
    An internal error occurred: <pre>{}</pre>
    See logs for full stacktrace.
    """.format(
            e
        ),
        500,
    )

if __name__ == "__main__":
    # This is used when running locally. Gunicorn is used to run the
    # application on Google App Engine and Cloud Run.
    # See entrypoint in app.yaml or Dockerfile.
    app.run(host="127.0.0.1", port=8080, debug=True)

Anwendung für die Bereitstellung vorbereiten

Für den Zugriff auf die Redis-Instanz muss die App Engine-Instanz im selben autorisierten Netzwerk wie die Redis-Instanz bereitgestellt werden. Außerdem müssen Sie die Verbindungsdetails Ihrer Redis-Instanz angeben. Mit dem folgenden Befehl können Sie das autorisierte Netzwerk, die IP-Adresse und den Port Ihrer Redis-Instanz ermitteln:

 gcloud redis instances describe [INSTANCE_ID] --region [REGION]
  1. Erstellen Sie eine App Engine-Anwendung.

  2. Aktualisieren Sie die Konfiguration der Anwendung, um die IP-Adresse, den Port und das Netzwerk Ihrer Redis-Instanz anzugeben:

    Einfach loslegen (Go)

    Aktualisieren Sie die Datei gae_flex_deployment/app.yaml:

    runtime: go
    env: flex
    
    # Update with Redis instance details
    env_variables:
      REDISHOST: '<REDIS_IP>'
      REDISPORT: '6379'
    
    # Update with Redis instance network name
    network:
      name: default

    Weitere Informationen finden Sie unter App mit app.yaml konfigurieren.

    Java

    Aktualisieren Sie die Datei gae_flex_deployment/app.yaml, um das Netzwerk Ihrer Redis-Instanz anzugeben:

    runtime: java
    env: flex
    
    # Update with Redis instance network name
    network:
      name: default

    Und aktualisieren Sie die Datei src/main/resources/application.properties mit der IP-Adresse und dem Port Ihrer Redis-Instanz:

    redis.host=REDIS_HOST_IP
    redis.port=6379

    Weitere Informationen zum Konfigurieren Ihrer Anwendung finden Sie unter Anwendung mit app.yaml konfigurieren.

    Node.js

    Aktualisieren Sie die Datei gae_flex_deployment/app.yaml:

    runtime: nodejs
    env: flex
    
    # Update with Redis instance details
    env_variables:
      REDISHOST: '<REDIS_IP>'
      REDISPORT: '6379'
    
    # Update with Redis instance network name
    network:
      name: default

    Weitere Informationen finden Sie unter App mit app.yaml konfigurieren.

    Python

    Aktualisieren Sie die Datei gae_flex_deployment/app.yaml:

    runtime: python
    env: flex
    entrypoint: gunicorn -b :$PORT main:app
    
    runtime_config:
      python_version: 3
    
    # Update with Redis instance IP and port
    env_variables:
      REDISHOST: '<REDIS_IP>'
      REDISPORT: '6379'
    
    # Update with Redis instance network name
    network:
      name: default

    Weitere Informationen finden Sie unter App mit app.yaml konfigurieren.

Anwendung in der flexiblen App Engine-Umgebung bereitstellen

So stellen Sie die Anwendung bereit:

  1. Kopieren Sie die erforderlichen Konfigurationsdateien in das Quellverzeichnis:

    Einfach loslegen (Go)

    Kopieren Sie die Datei app.yaml in das Quellverzeichnis:

    cp gae_flex_deployment/app.yaml .
    

    Java

    Kopieren Sie die Datei app.yaml in das Quellverzeichnis:

    mkdir -p src/main/appengine
    cp gae_flex_deployment/app.yaml src/main/appengine/
    

    Node.js

    Kopieren Sie die Datei app.yaml in das Quellverzeichnis:

    cp gae_flex_deployment/app.yaml .
    

    Python

    Kopieren Sie die Datei app.yaml in das Quellverzeichnis:

    cp gae_flex_deployment/app.yaml .
    
  2. Führen Sie den Bereitstellungsbefehl aus:

    Einfach loslegen (Go)

    gcloud app deploy
    

    Dies kann einige Minuten dauern.

    Java

    mvn appengine:deploy
    

    Dies kann einige Minuten dauern.

    Node.js

    gcloud app deploy
    

    Dies kann einige Minuten dauern.

    Python

    gcloud app deploy
    

    Dies kann einige Minuten dauern.

Rufen Sie nach Abschluss der Bereitstellung Ihre Anwendung unter der folgenden URL auf und ersetzen Sie [PROJECT_ID] durch Ihre Google Cloud-Projekt-ID:

https://[PROJECT_ID].appspot.com

Die Anzahl der Redis-Instanzen erhöht sich bei jedem Besuch der Anwendung.