Files
f3121b045b Health check implementation (feature request #890) (#1056)
* #890 health check implementation (fully working, but lacks tests)

* #890 added tests for client health check implementation

* #890 defer Unlock instead of calling manually

* go.sum reverted to the original state

* #890 switches in main.go reworked to ServeMux, backoff implementation replaced with existing module, health check function simplified

* Update mod files

* #890 client health check simplified and refactored

* #890 test error handling, necessary comments

* #890 health service initialization reqorked; minor fixes in tests

* #890 test error handling fixed

* #890 test timeout handling reworked

* Update go/grpcweb/health_test.go

Co-authored-by: Johan Brandhorst-Satzkorn <[email protected]>

* Update go/grpcweb/health_test.go

Co-authored-by: Johan Brandhorst-Satzkorn <[email protected]>

* #890 docs regenerated

* #890 proper repo name

Co-authored-by: Evgeny Mikerin <[email protected]>
Co-authored-by: Johan Brandhorst-Satzkorn <[email protected]>
2021-10-20 11:52:41 -04:00

66 lines
1.9 KiB
Go

package grpcweb
import (
"context"
"time"
backoff "github.com/cenkalti/backoff/v4"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/grpc/status"
)
const healthCheckMethod = "/grpc.health.v1.Health/Watch"
// Client health check function is also part of the grpc/internal package
// The following code is a simplified version of client.go
// For more details see: https://pkg.go.dev/google.golang.org/grpc/health
func ClientHealthCheck(ctx context.Context, backendConn *grpc.ClientConn, service string, setServingStatus func(serving bool)) error {
shouldBackoff := false // No need for backoff on the first connection attempt
backoffSrc := backoff.NewExponentialBackOff()
healthClient := healthpb.NewHealthClient(backendConn)
for {
// Backs off if the connection has failed in some way without receiving a message in the previous retry.
if shouldBackoff {
select {
case <-time.After(backoffSrc.NextBackOff()):
case <-ctx.Done():
return nil
}
}
shouldBackoff = true // we should backoff next time, since we attempt connecting below
req := healthpb.HealthCheckRequest{Service: service}
s, err := healthClient.Watch(ctx, &req)
if err != nil {
continue
}
resp := new(healthpb.HealthCheckResponse)
for {
err = s.RecvMsg(resp)
if err != nil {
setServingStatus(false)
// The health check functionality should be disabled if health check service is not implemented on the backend
if status.Code(err) == codes.Unimplemented {
return err
}
// breaking out of the loop, since we got an error from Recv, triggering reconnect
break
}
// As a message has been received, removes the need for backoff for the next retry.
shouldBackoff = false
backoffSrc.Reset()
if resp.Status == healthpb.HealthCheckResponse_SERVING {
setServingStatus(true)
} else {
setServingStatus(false)
}
}
}
}