Menghubungkan ke instance Redis dari aplikasi lingkungan fleksibel App Engine

Aplikasi App Engine harus berada di jaringan yang memiliki otorisasi yang sama dengan instance Redis agar dapat mengaksesnya.

Penyiapan

Jika sudah menginstal Google Cloud CLI dan telah membuat instance Redis, Anda dapat melewati langkah-langkah ini.

  1. Instal gcloud CLI dan lakukan inisialisasi:

    gcloud init
    
  2. Ikuti Panduan Memulai untuk membuat instance Redis. Catat zona, alamat IP, dan port instance Redis.

Contoh aplikasi

Contoh aplikasi server HTTP ini membuat koneksi ke instance Redis dari instance lingkungan fleksibel App Engine.

Clone repositori untuk bahasa pemrograman yang Anda inginkan dan buka folder yang berisi kode contoh:

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

Aplikasi contoh ini menambahkan penghitung Redis setiap kali endpoint / diakses.

Go

Aplikasi ini menggunakan klien github.com/gomodule/redigo/redis. Instal paket tersebut dengan menjalankan perintah berikut:

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

Aplikasi ini berbasis Jetty 3.1 servlet.

Kode ini menggunakan library Jedis:

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

Class AppServletContextListener digunakan untuk membuat kumpulan koneksi Redis yang berumur panjang:


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

Class VisitCounterServlet adalah servlet web yang menambahkan penghitung Redis:


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

Aplikasi ini menggunakan 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

Aplikasi ini menggunakan Flask untuk penayangan web dan paket redis-py untuk berkomunikasi dengan instance Redis.

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)

Menyiapkan aplikasi untuk deployment

Untuk mengakses instance Redis, instance App Engine harus di-deploy pada jaringan yang sama dengan instance Redis, dan Anda harus memberikan detail koneksi instance Redis Anda. Anda dapat menemukan jaringan, alamat IP, dan port yang diizinkan untuk instance Redis dengan menjalankan perintah berikut:

 gcloud redis instances describe [INSTANCE_ID] --region [REGION]
  1. Buat aplikasi App Engine.

  2. Update konfigurasi aplikasi untuk menentukan alamat IP, port, dan jaringan instance Redis Anda:

    Go

    Update file 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

    Lihat Mengonfigurasi Aplikasi dengan app.yaml untuk detail selengkapnya.

    Java

    Update file gae_flex_deployment/app.yaml untuk menentukan jaringan instance Redis Anda:

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

    Update file src/main/resources/application.properties dengan alamat IP dan port instance Redis Anda:

    redis.host=REDIS_HOST_IP
    redis.port=6379

    Untuk mengetahui informasi selengkapnya tentang cara mengonfigurasi aplikasi, lihat Mengonfigurasi Aplikasi dengan app.yaml.

    Node.js

    Update file 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

    Lihat Mengonfigurasi Aplikasi dengan app.yaml untuk detail selengkapnya.

    Python

    Update file 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

    Lihat Mengonfigurasi Aplikasi dengan app.yaml untuk detail selengkapnya.

Men-deploy aplikasi ke lingkungan fleksibel App Engine

Untuk men-deploy aplikasi:

  1. Salin file konfigurasi yang diperlukan ke dalam direktori sumber:

    Go

    Salin file app.yaml ke direktori sumber:

    cp gae_flex_deployment/app.yaml .
    

    Java

    Salin file app.yaml ke direktori sumber:

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

    Node.js

    Salin file app.yaml ke direktori sumber:

    cp gae_flex_deployment/app.yaml .
    

    Python

    Salin file app.yaml ke direktori sumber:

    cp gae_flex_deployment/app.yaml .
    
  2. Jalankan perintah deploy:

    Go

    gcloud app deploy
    

    Tindakan ini mungkin memerlukan waktu beberapa menit.

    Java

    mvn appengine:deploy
    

    Tindakan ini mungkin memerlukan waktu beberapa menit.

    Node.js

    gcloud app deploy
    

    Tindakan ini mungkin memerlukan waktu beberapa menit.

    Python

    gcloud app deploy
    

    Tindakan ini mungkin memerlukan waktu beberapa menit.

Setelah deployment selesai, buka aplikasi Anda di URL berikut, dengan mengganti [PROJECT_ID] dengan project ID Google Cloud Anda:

https://[PROJECT_ID].appspot.com

Jumlah instance Redis Anda meningkat setiap kali aplikasi dikunjungi.