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

Sie können von der App Engine-Standardumgebung aus eine Verbindung zu einer Redis-Instanz herstellen, indem Sie den serverlosen VPC-Zugriff verwenden.

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.

Serverlosen VPC-Zugriff konfigurieren

Um eine Verbindung von Ihrer App Engine-Anwendung zum autorisierten VPC-Netzwerk Ihrer Redis-Instanz herzustellen, müssen Sie den serverlosen VPC-Zugriff einrichten.

  1. Suchen Sie mit dem folgenden Befehl nach dem autorisierten Netzwerk Ihrer Redis-Instanz:

    gcloud beta redis instances describe [INSTANCE_ID] --region [REGION]
    
  2. Folgen Sie der Anleitung unter Connector erstellen, um einen Connector für serverlosen VPC-Zugriff zu erstellen. Achten Sie darauf, dass Sie den Connector in derselben Region wie Ihre Anwendung erstellen und dass der Connector mit dem autorisierten VPC-Netzwerk der Redis-Instanz verbunden ist. Merken Sie sich den Namen des Connectors.

Beispielanwendung

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

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

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.

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.3
gunicorn==22.0.0
redis==5.0.1
Werkzeug==3.0.3
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-Anwendung für die Verwendung des Connectors für serverlosen VPC-Zugriff konfiguriert sein und Sie müssen die Verbindungsdetails der Redis-Instanz angeben.

  1. Wenn Sie noch keine haben, erstellen Sie eine App Engine-Anwendung.

  2. Aktualisieren Sie die Konfiguration der Anwendung, um den Connector für den serverlosen VPC-Zugriff sowie die IP-Adresse und den Port Ihrer Redis-Instanz anzugeben:

    Go

    Aktualisieren Sie die Datei gae_standard_deployment/app.yaml:

    runtime: go111
    
    # Update with Redis instance details
    env_variables:
      REDISHOST: '<REDIS_IP>'
      REDISPORT: '6379'
    
    # Update with Serverless VPC Access connector details
    vpc_access_connector:
      name: 'projects/<PROJECT_ID>/locations/<REGION>/connectors/<CONNECTOR_NAME>'

    Weitere Informationen finden Sie unter app.yaml-Konfigurationsdatei.

    Java

    Aktualisieren Sie die Datei gae_standard_deployment/appengine-web.xml, um den Connector für serverlosen VPC-Zugriff anzugeben:

    <appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
      <runtime>java8</runtime>
      <threadsafe>true</threadsafe>
      <vpc-access-connector>
        <name>projects/[PROJECT_ID]/locations/[REGION]/connectors/[CONNECTOR_NAME]</name>
      </vpc-access-connector>
    </appengine-web-app>

    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 der Anwendung finden Sie in der Referenz zu appengine-web.xml.

    Node.js

    Aktualisieren Sie die Datei gae_standard_deployment/app.yaml:

    runtime: nodejs10
    
    # Update with Redis instance details
    env_variables:
      REDISHOST: '<REDIS_IP>'
      REDISPORT: '6379'
    
    # Update with Serverless VPC Access connector details
    vpc_access_connector:
      name: 'projects/<PROJECT_ID>/locations/<REGION>/connectors/<CONNECTOR_NAME>'

    Weitere Informationen finden Sie unter app.yaml-Konfigurationsdatei.

    Python

    Aktualisieren Sie die Datei gae_standard_deployment/app.yaml:

    runtime: python37
    entrypoint: gunicorn -b :$PORT main:app
    
    # Update with Redis instance details
    env_variables:
      REDISHOST: '<REDIS_IP>'
      REDISPORT: '6379'
    
    # Update with Serverless VPC Access connector details
    vpc_access_connector:
      name: 'projects/<PROJECT_ID>/locations/<REGION>/connectors/<CONNECTOR_NAME>'

    Weitere Informationen finden Sie unter app.yaml-Konfigurationsdatei.

Anwendung in der App Engine-Standardumgebung bereitstellen

So stellen Sie die Anwendung bereit:

  1. Kopieren Sie die erforderlichen Konfigurationsdateien in das Quellverzeichnis:

    Go

    Kopieren Sie die Dateien app.yaml und go.mod in das Quellverzeichnis:

    cp gae_standard_deployment/{app.yaml,go.mod} .
    

    Java

    Kopieren Sie die Datei appengine-web.xml in das Quellverzeichnis:

    mkdir -p src/main/webapp/WEB-INF
    cp gae_standard_deployment/appengine-web.xml src/main/webapp/WEB-INF/
    

    Node.js

    Kopieren Sie die Datei app.yaml in das Quellverzeichnis:

    cp gae_standard_deployment/app.yaml .
    

    Python

    Kopieren Sie die Datei app.yaml in das Quellverzeichnis:

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

    Go

    gcloud app deploy
    

    Java

    mvn package appengine:stage
    gcloud app deploy target/appengine-staging/app.yaml
    

    Node.js

    gcloud app deploy
    

    Python

    gcloud app deploy
    

Nach Abschluss der Bereitstellung wird mit dem Befehl die URL ausgegeben, über die Sie die Anwendung aufrufen können. Wenn Sie diese URL besuchen, erhöht sich die Anzahl der Redis-Instanz bei jedem Laden der Seite.