Connecting to a Redis instance from Cloud Functions

You can connect to a Redis instance from Cloud Functions by using Serverless VPC Access.

Setup

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

  1. Install the gcloud CLI 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.

Configuring Serverless VPC Access

To connect from your Cloud Functions to your Redis instance's authorized VPC network, you must set up Serverless VPC Access.

  1. Find your Redis instance's authorized network by running the command:

    gcloud redis instances describe INSTANCE_ID --region REGION
    
  2. Follow the instructions at Creating a connector to create a Serverless VPC Access connector. Make sure you create the connector in the same region where you want to deploy your function, and make sure the connector is attached to the Redis instance's authorized VPC network. Remember the name of the connector.

Sample function

This sample function establishes a connection to a Redis instance from Cloud Functions.

Clone the repository for your desired programming language and navigate to the folder that contains the sample code:

Go

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

Node.js

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

Python

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

The sample code increments a Redis counter every time the function is triggered:

Go

This function uses the github.com/gomodule/redigo/redis client.


// Package visitcount provides a Cloud Function that connects
// to a managed Redis instance.
package visitcount

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
	"github.com/gomodule/redigo/redis"
)

var redisPool *redis.Pool

func init() {
	// Register the HTTP handler with the Functions Framework
	functions.HTTP("VisitCount", visitCount)
}

// initializeRedis initializes and returns a connection pool
func initializeRedis() (*redis.Pool, error) {
	redisHost := os.Getenv("REDISHOST")
	if redisHost == "" {
		return nil, errors.New("REDISHOST must be set")
	}
	redisPort := os.Getenv("REDISPORT")
	if redisPort == "" {
		return nil, errors.New("REDISPORT must be set")
	}
	redisAddr := fmt.Sprintf("%s:%s", redisHost, redisPort)

	const maxConnections = 10
	return &redis.Pool{
		MaxIdle: maxConnections,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", redisAddr)
			if err != nil {
				return nil, fmt.Errorf("redis.Dial: %w", err)
			}
			return c, err
		},
	}, nil
}

// visitCount increments the visit count on the Redis instance
// and prints the current count in the HTTP response.
func visitCount(w http.ResponseWriter, r *http.Request) {
	// Initialize connection pool on first invocation
	if redisPool == nil {
		// Pre-declare err to avoid shadowing redisPool
		var err error
		redisPool, err = initializeRedis()
		if err != nil {
			log.Printf("initializeRedis: %v", err)
			http.Error(w, "Error initializing connection pool", http.StatusInternalServerError)
			return
		}
	}

	conn := redisPool.Get()
	defer conn.Close()

	counter, err := redis.Int(conn.Do("INCR", "visits"))
	if err != nil {
		log.Printf("redis.Int: %v", err)
		http.Error(w, "Error incrementing visit count", http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "Visit count: %d", counter)
}

Node.js

This function uses the redis module.


const functions = require('@google-cloud/functions-framework');
const redis = require('redis');

const REDISHOST = process.env.REDISHOST || 'localhost';
const REDISPORT = process.env.REDISPORT || 6379;

const redisClient = redis.createClient({
  socket: {
    host: REDISHOST,
    port: REDISPORT,
  },
});
redisClient.on('error', err => console.error('ERR:REDIS:', err));
redisClient.connect();

functions.http('visitCount', async (req, res) => {
  try {
    const response = await redisClient.incr('visits');
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end(`Visit count: ${response}`);
  } catch (err) {
    console.log(err);
    res.status(500).send(err.message);
  }
});

Python

This function uses the redis-py package.


import os

import functions_framework
import redis

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)


@functions_framework.http
def visit_count(request):
    value = redis_client.incr("visits", 1)
    return f"Visit count: {value}"

Deploying the sample to Cloud Functions

Deploy the function using the Google Cloud CLI:

Go

gcloud functions deploy visit-count \
--gen2 \
--region=REGION \
--runtime=go116 \
--source=. \
--entry-point=VisitCount \
--trigger-http \
--vpc-connector=projects/PROJECT_ID/locations/REGION/connectors/CONNECTOR_NAME \
--set-env-vars=REDISHOST=REDIS_IP,REDISPORT=REDIS_PORT

Node.js

gcloud functions deploy visit-count \
--gen2 \
--region=REGION \
--runtime=nodejs16 \
--source=. \
--entry-point=visitCount \
--trigger-http \
--vpc-connector=projects/PROJECT_ID/locations/REGION/connectors/CONNECTOR_NAME \
--set-env-vars=REDISHOST=REDIS_IP,REDISPORT=REDIS_PORT

Python

gcloud functions deploy visit-count \
--gen2 \
--region=REGION \
--runtime=python310 \
--source=. \
--entry-point=visit_count \
--trigger-http \
--vpc-connector=projects/PROJECT_ID/locations/REGION/connectors/CONNECTOR_NAME \
--set-env-vars=REDISHOST=REDIS_IP,REDISPORT=REDIS_PORT

where:

  • PROJECT_ID is your Google Cloud project's ID.
  • REGION is the region where you want to deploy the function.
  • CONNECTOR_NAME is the name of your connector.
  • REDIS_IP and REDIS_PORT are the IP address and port number of your Redis instance.

After the function deployment finishes, retrieve your function's URL:

gcloud functions describe visit-count \
--gen2 \
--region=REGION \
--format="value(serviceConfig.uri)"

You can see the counter increase every time you trigger the function by sending a GET request to its URL.