sample-apps/single-req-app/main.go (98 lines of code) (raw):
// Copyright 2023 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
//
// 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"
"os/signal"
"syscall"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// Collector that contains the descriptors for the metrics from the app.
type shortLivedCollector struct {
gauge *prometheus.Desc
counter *prometheus.Desc
}
// Store whether or not we've served our request yet.
var handledSingleRequest bool
// Create channel to listen for signals.
var signalChan chan (os.Signal) = make(chan os.Signal, 1)
func newShortLivedCollector() *shortLivedCollector {
return &shortLivedCollector{
gauge: prometheus.NewDesc("gauge",
"A gauge event has occurred",
nil, nil,
),
counter: prometheus.NewDesc("counter",
"A counter event has occured",
nil, nil,
),
}
}
// Each and every collector must implement the Describe function.
// It essentially writes all descriptors to the prometheus desc channel.
func (collector *shortLivedCollector) Describe(ch chan<- *prometheus.Desc) {
//Update this section with the each metric you create for a given collector
ch <- collector.gauge
ch <- collector.counter
}
// Collect implements required collect function for all prometheus collectors
func (collector *shortLivedCollector) Collect(ch chan<- prometheus.Metric) {
m1 := prometheus.MustNewConstMetric(collector.gauge, prometheus.GaugeValue, float64(time.Now().Unix()))
m2 := prometheus.MustNewConstMetric(collector.counter, prometheus.CounterValue, float64(time.Now().Unix()))
ch <- m1
ch <- m2
}
func entrypointHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "User request received!")
log.Printf("single request app: handled one and only request")
handledSingleRequest = true
}
func livenessProbeHandler(w http.ResponseWriter, r *http.Request) {
if handledSingleRequest {
w.WriteHeader(http.StatusPreconditionFailed)
w.Write([]byte("412 - Service has turned down"))
log.Printf("single request app: handled liveness probe. Returned that it is not live.")
return
}
fmt.Fprintln(w, "Liveness probe received and was successful.")
log.Printf("single request app: handled liveness probe. Liveness succesful.")
}
func startupProbeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Startup probe received")
log.Printf("single request app: handled liveness probe. Liveness succesful.")
}
func main() {
foo := newShortLivedCollector()
prometheus.MustRegister(foo)
entrypointMux := http.NewServeMux()
entrypointMux.HandleFunc("/", entrypointHandler)
entrypointMux.HandleFunc("/startup", startupProbeHandler)
entrypointMux.HandleFunc("/liveness", livenessProbeHandler)
promMux := http.NewServeMux()
promMux.Handle("/metrics", promhttp.Handler())
mainSrv := http.Server{
Addr: ":8000",
Handler: entrypointMux,
}
promSrv := http.Server{
Addr: ":8080",
Handler: promMux,
}
go func() {
mainSrv.ListenAndServe()
}()
go func() {
promSrv.ListenAndServe()
}()
// SIGINT handles Ctrl+C locally.
// SIGTERM handles Cloud Run termination signal.
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
// Receive output from signalChan.
sig := <-signalChan
log.Printf("single request app: %s signal caught", sig)
// Timeout if waiting for connections to return idle.
mainCtx, mainCancel := context.WithTimeout(context.Background(), 5*time.Second)
defer mainCancel()
if err := mainSrv.Shutdown(mainCtx); err != nil {
log.Printf("single request app: server shutdown failed: %+v", err)
}
promCtx, promCancel := context.WithTimeout(context.Background(), 5*time.Second)
defer promCancel()
if err := promSrv.Shutdown(promCtx); err != nil {
log.Printf("single request app: prom server shutdown failed: %+v", err)
}
log.Printf("single request app: shutdown complete")
}