Connecting to a Redis instance from an App Engine Flexible Environment application

App Engine applications must be in the same region as the Redis instance and on the same network to be authorized to access the Redis instance.

Setup

If you have already installed the Cloud SDK and have created a Redis instance, you can skip these steps.

  1. Install the Cloud SDK and initialize:

    gcloud init
    
  2. Follow the Quickstart Guide to create a Redis instance. Take note of the zone, IP address, and port of the Redis instance.

Sample application

This sample HTTP server application establishes a connection to a Redis instance from a App Engine flexible environment instance.

First clone the repository in your desired programming language:

Go

This application uses the github.com/garyburd/redigo/redis client.

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

Java

This application uses the Jedis client.

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

Node.js

This application uses the node_redis module.

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

Python

This application uses the redis-py package.

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

This sample code increments a Redis counter every time the "/" endpoint is accessed.

Go

First, install Redigo:

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.NewPool(func() (redis.Conn, error) {
		return redis.Dial("tcp", redisAddr)
	}, maxConnections)

	http.HandleFunc("/", incrementHandler)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Java

This application is Jetty 3.1 servlet-based.

Add the Jedis library to your pom.xml as shown here:

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

The AppServletContextListener class is used to create a long-lived Redis connection 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
      }
    }
  }
}

The VisitCounterServlet class is a web servlet that increments a Redis counter.

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

Update application.properties to match your Redis instance's IP address and port number. If you're not sure of your instance's IP address or port number, run the following command:

   gcloud redis instances describe [INSTANCE_ID] --region [REGION]
redis.host=REDIS_HOST_IP
redis.port=6379

Node.js

{
  "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": ">=8"
  },
  "dependencies": {
    "redis": "^2.8.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

Flask==1.0.2
gunicorn==19.9.0
redis==3.1.0
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 'Visitor number: {}'.format(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. See entrypoint in app.yaml.
    app.run(host='127.0.0.1', port=8080, debug=True)

Preparing the application for deployment

Create an App Engine application in the same region as the Redis instance.

To access the Redis instance, the App Engine instance must be deployed on the same network as the Redis instance. If you don’t know your Redis instance’s authorized network, enter the following command:

 gcloud beta redis instances describe [INSTANCE_ID] --region [REGION]

Go

app.yaml:

# Copyright 2019 Google LLC
#
# 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
#
#     https://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.

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
To ensure the flexible environment instance is deployed on the authorized network, update the app.yaml network section and specify the name parameter. See Configuring your App with app.yaml for more details.

Java

app.yaml:

runtime: java
env: flex

Node.js

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
To ensure the flexible environment instance is deployed on the authorized network, update the app.yaml network section and specify the name parameter. See Configuring your App with app.yaml for more details.

Python

app.yaml:

runtime: python
env: flex
entrypoint: gunicorn -b :$PORT main:app

runtime_config:
  python_version: 3

# update with Redis instance host IP, port
env_variables:
  REDISHOST:  redis-ip
  REDISPORT: 6379

# update with Redis instance network name
network:
  name: default
To ensure the flexible environment instance is deployed on the authorized network, update the app.yaml network section and specify the name parameter. See Configuring your App with app.yaml for more details.

Deploying the application to the App Engine Flexible Environment

Deploy the application:

Go

  1. Update app.yaml with the Redis IP and port.
  2. Enter the following command to deploy the application:

    gcloud app deploy
    

    This may take a few minutes.

  3. Use your custom project ID in this URL https://projectid.appspot.com and send a GET request via cURL or the browser.

Java

  1. Update src/main/resources/application.properties with the Redis host and IP.
  2. Copy the app.yaml file to the source directory:

    mkdir -p src/main/appengine
    cp flex_deployment/app.yaml src/main/appengine/
    
  3. Add the App Engine Maven deployment plugin to your pom.xml:

     <!-- Deployment plugin for App Engine Flexible -->
     <plugin>
       <groupId>com.google.cloud.tools</groupId>
       <artifactId>appengine-maven-plugin</artifactId>
       <version>1.3.2</version>
    </plugin>

  4. Enter the following command to deploy the application:

    mvn appengine:deploy
    

    This may take a few minutes.

  5. Use your custom project ID in this URL https://projectid.appspot.com and send a GET request via cURL or the browser.

Node.js

  1. Update app.yaml with the Redis IP and port.
  2. Enter the following command to deploy the application:

    gcloud app deploy
    

    This may take a few minutes.

  3. Use your custom project ID in this URL https://projectid.appspot.com and send a GET request via cURL or the browser.

Python

  1. Update app.yaml with the Redis IP and port.
  2. Enter the following command to deploy the application:

    gcloud app deploy
    

    This may take a few minutes.

  3. Use your custom project ID in this URL https://projectid.appspot.com and send a GET request via cURL or the browser.

Kunde den här sidan hjälpa dig? Berätta:

Skicka feedback om ...

Google Cloud Memorystore for Redis