quickstarts/hello-app-redis/main.go (78 lines of code) (raw):
/**
* Copyright 2020 Google Inc.
*
* 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
*
* http://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.
*/
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"sync"
"github.com/redis/go-redis/v9"
)
// Start of resource pool code.
const resourcePoolSize = 50
type resourcePool struct {
mtx sync.Mutex
allocated int
}
var pool resourcePool
var redisClusterClient *redis.ClusterClient
var ctx = context.Background()
func (p *resourcePool) alloc() bool {
p.mtx.Lock()
defer p.mtx.Unlock()
if p.allocated < resourcePoolSize*0.9 {
p.allocated++
return true
}
return false
}
func (p *resourcePool) release() {
p.mtx.Lock()
defer p.mtx.Unlock()
p.allocated--
}
func (p *resourcePool) hasResources() bool {
p.mtx.Lock()
defer p.mtx.Unlock()
return p.allocated < resourcePoolSize
}
// End of resource pool code.
func main() {
// use PORT environment variable, or default to 8080
port := "8080"
if fromEnv := os.Getenv("PORT"); fromEnv != "" {
port = fromEnv
}
// register hello function to handle all requests
server := http.NewServeMux()
server.HandleFunc("/healthz", healthz)
server.HandleFunc("/", hello)
// connect to redis cluster
redisClusterClient = redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{"redis-cluster:6379"},
RouteByLatency: true})
defer redisClusterClient.Close()
// start the web server on port and accept requests
log.Printf("Server listening on port %s", port)
err := http.ListenAndServe(":"+port, server)
log.Fatal(err)
}
// Start of healthz code.
func healthz(w http.ResponseWriter, r *http.Request) {
// Log to make it simple to validate if healt checks are happening.
log.Printf("Serving healthcheck: %s", r.URL.Path)
if pool.hasResources() {
fmt.Fprintf(w, "Ok\n")
return
}
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte("503 - Error due to tight resource constraints in the pool!"))
}
// End of healthz code.
// hello responds to the request with a plain-text "Hello, world" message and a timestamp.
func hello(w http.ResponseWriter, r *http.Request) {
log.Printf("Serving request: %s", r.URL.Path)
if !pool.alloc() {
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte("503 - Error due to tight resource constraints in the pool!\n"))
return
} else {
defer pool.release()
}
count, err := redisClusterClient.Incr(ctx, "hits").Result()
if err != nil {
w.Write([]byte("500 - Error due to redis cluster broken!\n" + fmt.Sprintf("%v", err)))
return
}
fmt.Fprintf(w, "I have been hit [%v] times since deployment!", count)
}