Check if a service is running (health check)


Health check is for checking if the service is available. Create a Go module

mkdir health-check
cd health-check
go mod init gohealth
touch main.go

inside main.go file

package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
)

var _livenessfile = "/tmp/myservice-healthy"

func main() {
	r := gin.Default()
	rdb := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})
	r.GET("/health", func(ctx *gin.Context) {
		// check if all clients' connection is still able to work
		if err := rdb.Ping(ctx).Err(); err != nil {
			ctx.String(http.StatusInternalServerError, "Redis client isn't working")
			return
		}
		ctx.String(http.StatusOK, "Clients are available")
	})

	srv := &http.Server{
		Addr:    ":8080",
		Handler: r,
	}

	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()

	// before running create a file to check if service is running
	os.Create(_livenessfile)
	// remove after the service has stopped
	defer os.Remove(_livenessfile)

	// to do liveness check, it requires to implement graceful-shutdown
	go func() {
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("listen: %s\\n", err)
		}
	}()

	<-ctx.Done()
	stop()
	log.Println("shutting down gracefully, press Ctrl+C again to force")

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := srv.Shutdown(ctx); err != nil {
		log.Fatal("Server forced to shutdown: ", err)
	}
	log.Println("Server exiting")
}

You will see this application has only one endpoint which is /health. When you try to access it, the server will check all clients connection. This will be depended on a service, it is about how many other services are connected to the service. In my application, there is just a Redis client. If all services still be available, the endpoint will return status 200 or 500 if one service is failed to connect. It is good to know the service readiness before we interact with data.

Also, there is a code that create a tmp file before the service is started, it is for checking if the service is running. It is different from /health endpoint which really check all connection related to the service. the tmp file exists when the service is running and it will be deleted when the service is stopped.

sources