mirror of https://github.com/gofiber/fiber.git
✨ feat: Add Startup Probe to Healthcheck Middleware (#3069)
* added startup default probe endpoint * added test case * updated docs * updated test order * added test case * fixed go fmt and md lint * fixed go fmt and md lint * updated doc as per coderabbitai suggestions * changed healhtcheck route register to use default const instead of string for test cases * updated whats new with healthcheck content * updated whats new doc with coderabbitai sugg * updated migration guidepull/3075/head
parent
091a59472c
commit
4f1dc49894
|
@ -4,7 +4,7 @@ id: healthcheck
|
|||
|
||||
# Health Check
|
||||
|
||||
Liveness and readiness probes middleware for [Fiber](https://github.com/gofiber/fiber) that provides two endpoints for checking the liveness and readiness state of HTTP applications.
|
||||
Liveness, readiness and startup probes middleware for [Fiber](https://github.com/gofiber/fiber) that provides three endpoints for checking the liveness, readiness, and startup state of HTTP applications.
|
||||
|
||||
## Overview
|
||||
|
||||
|
@ -16,6 +16,10 @@ Liveness and readiness probes middleware for [Fiber](https://github.com/gofiber/
|
|||
- **Default Endpoint**: `/readyz`
|
||||
- **Behavior**: By default returns `true` immediately when the server is operational.
|
||||
|
||||
- **Startup Probe**: Checks if the application has completed its startup sequence and is ready to proceed with initialization and readiness checks.
|
||||
- **Default Endpoint**: `/startupz`
|
||||
- **Behavior**: By default returns `true` immediately when the server is operational.
|
||||
|
||||
- **HTTP Status Codes**:
|
||||
- `200 OK`: Returned when the checker function evaluates to `true`.
|
||||
- `503 Service Unavailable`: Returned when the checker function evaluates to `false`.
|
||||
|
@ -44,6 +48,8 @@ After you initiate your [Fiber](https://github.com/gofiber/fiber) app, you can u
|
|||
app.Get(healthcheck.DefaultLivenessEndpoint, healthcheck.NewHealthChecker())
|
||||
// Provide a minimal config for readiness check
|
||||
app.Get(healthcheck.DefaultReadinessEndpoint, healthcheck.NewHealthChecker())
|
||||
// Provide a minimal config for startup check
|
||||
app.Get(healthcheck.DefaultStartupEndpoint, healthcheck.NewHealthChecker())
|
||||
// Provide a minimal config for check with custom endpoint
|
||||
app.Get("/live", healthcheck.NewHealthChecker())
|
||||
|
||||
|
@ -59,6 +65,12 @@ app.Get(healthcheck.DefaultReadinessEndpoint, healthcheck.NewHealthChecker(healt
|
|||
return true
|
||||
},
|
||||
}))
|
||||
// And it works the same for startup, just change the route
|
||||
app.Get(healthcheck.DefaultStartupEndpoint, healthcheck.NewHealthChecker(healthcheck.Config{
|
||||
Probe: func(c fiber.Ctx) bool {
|
||||
return true
|
||||
},
|
||||
}))
|
||||
// With a custom route and custom probe
|
||||
app.Get("/live", healthcheck.NewHealthChecker(healthcheck.Config{
|
||||
Probe: func(c fiber.Ctx) bool {
|
||||
|
@ -90,6 +102,10 @@ type Config struct {
|
|||
// Function used for checking the liveness of the application. Returns true if the application
|
||||
// is running and false if it is not. The liveness probe is typically used to indicate if
|
||||
// the application is in a state where it can handle requests (e.g., the server is up and running).
|
||||
// The readiness probe is typically used to indicate if the application is ready to start accepting traffic (e.g., all necessary components
|
||||
// are initialized and dependent services are available) and the startup probe typically used to
|
||||
// indicate if the application has completed its startup sequence and is ready to proceed with
|
||||
// initialization and readiness checks
|
||||
//
|
||||
// Optional. Default: func(c fiber.Ctx) bool { return true }
|
||||
Probe HealthChecker
|
||||
|
|
|
@ -33,6 +33,7 @@ Here's a quick overview of the changes in Fiber `v3`:
|
|||
- [Session](#session)
|
||||
- [Filesystem](#filesystem)
|
||||
- [Monitor](#monitor)
|
||||
- [Healthcheck](#healthcheck)
|
||||
- [📋 Migration guide](#-migration-guide)
|
||||
|
||||
## Drop for old Go versions
|
||||
|
@ -330,6 +331,25 @@ DRAFT section
|
|||
|
||||
Monitor middleware is now in Contrib package.
|
||||
|
||||
### Healthcheck
|
||||
|
||||
The Healthcheck middleware has been enhanced to support more than two routes, with default endpoints for liveliness, readiness, and startup checks. Here's a detailed breakdown of the changes and how to use the new features.
|
||||
|
||||
1. **Support for More Than Two Routes**:
|
||||
- The updated middleware now supports multiple routes beyond the default liveliness and readiness endpoints. This allows for more granular health checks, such as startup probes.
|
||||
|
||||
2. **Default Endpoints**:
|
||||
- Three default endpoints are now available:
|
||||
- **Liveness**: `/livez`
|
||||
- **Readiness**: `/readyz`
|
||||
- **Startup**: `/startupz`
|
||||
- These endpoints can be customized or replaced with user-defined routes.
|
||||
|
||||
3. **Simplified Configuration**:
|
||||
- The configuration for each health check endpoint has been simplified. Each endpoint can be configured separately, allowing for more flexibility and readability.
|
||||
|
||||
Refer to the [healthcheck middleware migration guide](./middleware/healthcheck.md) or the [general migration guide](#-migration-guide) to review the changes.
|
||||
|
||||
## 📋 Migration guide
|
||||
|
||||
- [🚀 App](#-app-1)
|
||||
|
@ -480,3 +500,48 @@ app.Use(static.New("", static.Config{
|
|||
MaxAge: 3600,
|
||||
}))
|
||||
```
|
||||
|
||||
### Healthcheck
|
||||
|
||||
Previously, the Healthcheck middleware was configured with a combined setup for liveliness and readiness probes:
|
||||
|
||||
```go
|
||||
//before
|
||||
app.Use(healthcheck.New(healthcheck.Config{
|
||||
LivenessProbe: func(c *fiber.Ctx) bool {
|
||||
return true
|
||||
},
|
||||
LivenessEndpoint: "/live",
|
||||
ReadinessProbe: func(c *fiber.Ctx) bool {
|
||||
return serviceA.Ready() && serviceB.Ready() && ...
|
||||
},
|
||||
ReadinessEndpoint: "/ready",
|
||||
}))
|
||||
```
|
||||
|
||||
With the new version, each health check endpoint is configured separately, allowing for more flexibility:
|
||||
|
||||
```go
|
||||
// after
|
||||
|
||||
// Default liveness endpoint configuration
|
||||
app.Get(healthcheck.DefaultLivenessEndpoint, healthcheck.NewHealthChecker(healthcheck.Config{
|
||||
Probe: func(c *fiber.Ctx) bool {
|
||||
return true
|
||||
},
|
||||
}))
|
||||
|
||||
// Default readiness endpoint configuration
|
||||
app.Get(healthcheck.DefaultReadinessEndpoint, healthcheck.NewHealthChecker())
|
||||
|
||||
// New default startup endpoint configuration
|
||||
// Default endpoint is /startupz
|
||||
app.Get(healthcheck.DefaultStartupEndpoint, healthcheck.NewHealthChecker(healthcheck.Config{
|
||||
Probe: func(c *fiber.Ctx) bool {
|
||||
return serviceA.Ready() && serviceB.Ready() && ...
|
||||
},
|
||||
}))
|
||||
|
||||
// Custom liveness endpoint configuration
|
||||
app.Get("/live", healthcheck.NewHealthChecker())
|
||||
```
|
||||
|
|
|
@ -24,6 +24,7 @@ type Config struct {
|
|||
const (
|
||||
DefaultLivenessEndpoint = "/livez"
|
||||
DefaultReadinessEndpoint = "/readyz"
|
||||
DefaultStartupEndpoint = "/startupz"
|
||||
)
|
||||
|
||||
func defaultProbe(fiber.Ctx) bool { return true }
|
||||
|
|
|
@ -34,30 +34,38 @@ func Test_HealthCheck_Strict_Routing_Default(t *testing.T) {
|
|||
StrictRouting: true,
|
||||
})
|
||||
|
||||
app.Get("/livez", NewHealthChecker())
|
||||
app.Get("/readyz", NewHealthChecker())
|
||||
app.Get(DefaultLivenessEndpoint, NewHealthChecker())
|
||||
app.Get(DefaultReadinessEndpoint, NewHealthChecker())
|
||||
app.Get(DefaultStartupEndpoint, NewHealthChecker())
|
||||
|
||||
shouldGiveOK(t, app, "/readyz")
|
||||
shouldGiveOK(t, app, "/livez")
|
||||
shouldGiveOK(t, app, "/startupz")
|
||||
shouldGiveNotFound(t, app, "/readyz/")
|
||||
shouldGiveNotFound(t, app, "/livez/")
|
||||
shouldGiveNotFound(t, app, "/startupz/")
|
||||
shouldGiveNotFound(t, app, "/notDefined/readyz")
|
||||
shouldGiveNotFound(t, app, "/notDefined/livez")
|
||||
shouldGiveNotFound(t, app, "/notDefined/startupz")
|
||||
}
|
||||
|
||||
func Test_HealthCheck_Default(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
app := fiber.New()
|
||||
app.Get("/livez", NewHealthChecker())
|
||||
app.Get("/readyz", NewHealthChecker())
|
||||
app.Get(DefaultLivenessEndpoint, NewHealthChecker())
|
||||
app.Get(DefaultReadinessEndpoint, NewHealthChecker())
|
||||
app.Get(DefaultStartupEndpoint, NewHealthChecker())
|
||||
|
||||
shouldGiveOK(t, app, "/readyz")
|
||||
shouldGiveOK(t, app, "/livez")
|
||||
shouldGiveOK(t, app, "/startupz")
|
||||
shouldGiveOK(t, app, "/readyz/")
|
||||
shouldGiveOK(t, app, "/livez/")
|
||||
shouldGiveOK(t, app, "/startupz/")
|
||||
shouldGiveNotFound(t, app, "/notDefined/readyz")
|
||||
shouldGiveNotFound(t, app, "/notDefined/livez")
|
||||
shouldGiveNotFound(t, app, "/notDefined/startupz")
|
||||
}
|
||||
|
||||
func Test_HealthCheck_Custom(t *testing.T) {
|
||||
|
@ -80,6 +88,11 @@ func Test_HealthCheck_Custom(t *testing.T) {
|
|||
}
|
||||
},
|
||||
}))
|
||||
app.Get(DefaultStartupEndpoint, NewHealthChecker(Config{
|
||||
Probe: func(_ fiber.Ctx) bool {
|
||||
return false
|
||||
},
|
||||
}))
|
||||
|
||||
// Setup custom liveness and readiness probes to simulate application health status
|
||||
// Live should return 200 with GET request
|
||||
|
@ -97,6 +110,8 @@ func Test_HealthCheck_Custom(t *testing.T) {
|
|||
// Ready should return 503 with GET request before the channel is closed
|
||||
shouldGiveStatus(t, app, "/ready", fiber.StatusServiceUnavailable)
|
||||
|
||||
shouldGiveStatus(t, app, "/startupz", fiber.StatusServiceUnavailable)
|
||||
|
||||
// Ready should return 200 with GET request after the channel is closed
|
||||
c1 <- struct{}{}
|
||||
shouldGiveOK(t, app, "/ready")
|
||||
|
@ -155,13 +170,15 @@ func Test_HealthCheck_Next(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
app.Get("/readyz", checker)
|
||||
app.Get("/livez", checker)
|
||||
app.Get(DefaultLivenessEndpoint, checker)
|
||||
app.Get(DefaultReadinessEndpoint, checker)
|
||||
app.Get(DefaultStartupEndpoint, checker)
|
||||
|
||||
// This should give not found since there are no other handlers to execute
|
||||
// so it's like the route isn't defined at all
|
||||
shouldGiveNotFound(t, app, "/readyz")
|
||||
shouldGiveNotFound(t, app, "/livez")
|
||||
shouldGiveNotFound(t, app, "/startupz")
|
||||
}
|
||||
|
||||
func Benchmark_HealthCheck(b *testing.B) {
|
||||
|
@ -169,6 +186,7 @@ func Benchmark_HealthCheck(b *testing.B) {
|
|||
|
||||
app.Get(DefaultLivenessEndpoint, NewHealthChecker())
|
||||
app.Get(DefaultReadinessEndpoint, NewHealthChecker())
|
||||
app.Get(DefaultStartupEndpoint, NewHealthChecker())
|
||||
|
||||
h := app.Handler()
|
||||
fctx := &fasthttp.RequestCtx{}
|
||||
|
@ -190,6 +208,7 @@ func Benchmark_HealthCheck_Parallel(b *testing.B) {
|
|||
|
||||
app.Get(DefaultLivenessEndpoint, NewHealthChecker())
|
||||
app.Get(DefaultReadinessEndpoint, NewHealthChecker())
|
||||
app.Get(DefaultStartupEndpoint, NewHealthChecker())
|
||||
|
||||
h := app.Handler()
|
||||
|
||||
|
|
Loading…
Reference in New Issue