Skip to content

Commit

Permalink
Merge pull request #44 from hidevopsio/grpc-health
Browse files Browse the repository at this point in the history
support customized health check
  • Loading branch information
john-deng authored Nov 5, 2018
2 parents 36acabe + 744ee4c commit 309f8af
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 15 deletions.
2 changes: 2 additions & 0 deletions pkg/app/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,11 @@ func (a *BaseApplication) Build() {
instantiateFactory := instantiate.NewInstantiateFactory(a.instances, componentContainer, a.properties)
// TODO: should set or get instance by passing object instantiateFactory
a.instances.Set(factory.InstantiateFactoryName, instantiateFactory)
instantiateFactory.AppendComponent(factory.InstantiateFactoryName, instantiateFactory)

configurableFactory := autoconfigure.NewConfigurableFactory(instantiateFactory, a.configurations)
a.instances.Set(factory.ConfigurableFactoryName, configurableFactory)
instantiateFactory.AppendComponent(factory.ConfigurableFactoryName, configurableFactory)
inject.SetFactory(configurableFactory)
a.configurableFactory = configurableFactory

Expand Down
4 changes: 1 addition & 3 deletions pkg/app/web/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"hidevops.io/hiboot/pkg/at"
"hidevops.io/hiboot/pkg/log"
"hidevops.io/hiboot/pkg/utils/io"
"hidevops.io/hiboot/pkg/utils/reflector"
"hidevops.io/hiboot/pkg/utils/str"
"os"
"regexp"
Expand Down Expand Up @@ -147,8 +146,7 @@ func (a *application) build() (err error) {
func (a *application) RegisterController(controller interface{}) error {
// get from controller map
// parse controller type
controllerInterfaceName := reflector.GetName(controller)
controllers := a.ConfigurableFactory().GetInstances(controllerInterfaceName)
controllers := a.ConfigurableFactory().GetInstances(controller)
if controllers != nil {
return a.dispatcher.register(controllers)
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/at/healthcheck.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package at

// HealthCheckService is the annotation for health check service
type HealthCheckService interface {
}
2 changes: 1 addition & 1 deletion pkg/factory/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type InstantiateFactory interface {
Initialized() bool
SetInstance(params ...interface{}) (err error)
GetInstance(params ...interface{}) (retVal interface{})
GetInstances(name string) (retVal []interface{})
GetInstances(params ...interface{}) (retVal []interface{})
Items() map[string]interface{}
AppendComponent(c ...interface{})
BuildComponents() (err error)
Expand Down
6 changes: 4 additions & 2 deletions pkg/factory/instantiate/instantiate.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ func (f *instantiateFactory) SetInstance(params ...interface{}) (err error) {

ifcField := reflector.GetEmbeddedField(instance, "")
if ifcField.Anonymous {
typeName := ifcField.Name
typeName := reflector.GetLowerCamelFullNameByType(ifcField.Type)
//typeName := ifcField.Name
categorised, ok := f.categorized[typeName]
if !ok {
categorised = make([]interface{}, 0)
Expand Down Expand Up @@ -159,10 +160,11 @@ func (f *instantiateFactory) GetInstance(params ...interface{}) (retVal interfac
}

// GetInstances get instance by name
func (f *instantiateFactory) GetInstances(name string) (retVal []interface{}) {
func (f *instantiateFactory) GetInstances(params ...interface{}) (retVal []interface{}) {
//items := f.Items()
//log.Debug(items)
if f.Initialized() {
name, _ := factory.ParseParams(params...)
retVal = f.categorized[name]
}
return
Expand Down
38 changes: 31 additions & 7 deletions pkg/starter/actuator/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,54 @@ package actuator
import (
"hidevops.io/hiboot/pkg/app"
"hidevops.io/hiboot/pkg/app/web"
"hidevops.io/hiboot/pkg/at"
"hidevops.io/hiboot/pkg/factory"
)

// HealthService is the interface for health check
type HealthService interface {
Name() string
Status() bool
}

// Health is the health check struct
type Health struct {
Status string `json:"status"`
}

type healthController struct {
web.Controller
at.RestController

configurableFactory factory.ConfigurableFactory
}

func init() {
app.Register(newHealthController)
}

func newHealthController() *healthController {
return &healthController{}
func newHealthController(configurableFactory factory.ConfigurableFactory) *healthController {
return &healthController{configurableFactory: configurableFactory}
}

// GET /health
func (c *healthController) Get() {
func (c *healthController) Get(ctx *web.Context) {
healthServices := c.configurableFactory.GetInstances(new(at.HealthCheckService))
healthCheckProfiles := make(map[string]interface{})

health := Health{
Status: "UP",
healthCheckProfiles["status"] = "Up"

if healthServices != nil {
for _, svc := range healthServices {
healthService := svc.(HealthService)
status := "Down"
if healthService.Status() {
status = "Up"
}
healthCheckProfiles[healthService.Name()] = Health{
Status: status,
}
}
}
c.Ctx.JSON(health)

ctx.JSON(healthCheckProfiles)
}
22 changes: 22 additions & 0 deletions pkg/starter/actuator/health_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,33 @@
package actuator

import (
"hidevops.io/hiboot/pkg/app"
"hidevops.io/hiboot/pkg/app/web"
"hidevops.io/hiboot/pkg/at"
"net/http"
"testing"
)

type fakeHealthCheckService struct {
at.HealthCheckService
}

func (s *fakeHealthCheckService) Name() string {
return "fake"
}

func (s *fakeHealthCheckService) Status() bool {
return true
}

func newFakeHealthCheckService() HealthService {
return &fakeHealthCheckService{}
}

func init() {
app.Register(newFakeHealthCheckService)
}

func TestHealthController(t *testing.T) {
web.RunTestApplication(t).
Get("/health").
Expand Down
29 changes: 27 additions & 2 deletions pkg/starter/grpc/autoconfigure.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ package grpc

import (
"google.golang.org/grpc"
"google.golang.org/grpc/health"
pb "google.golang.org/grpc/health/grpc_health_v1"
"hidevops.io/hiboot/pkg/app"
"hidevops.io/hiboot/pkg/factory"
"hidevops.io/hiboot/pkg/utils/cmap"
"hidevops.io/hiboot/pkg/utils/reflector"
"reflect"
)
Expand All @@ -44,6 +47,10 @@ type grpcService struct {
var (
grpcServers []*grpcService
grpcClients []*grpcService

clientMap cmap.ConcurrentMap

registerHealthCheckService = false
)

// RegisterServer register server from application
Expand All @@ -61,8 +68,8 @@ func RegisterServer(register interface{}, server interface{}) {
// Server alias to RegisterServer
var Server = RegisterServer

// RegisterClient register client from application
func RegisterClient(name string, clientConstructors ...interface{}) {
// registerClient register client from application
func registerClient(name string, clientConstructors ...interface{}) {
for _, clientConstructor := range clientConstructors {
svr := &grpcService{
name: name,
Expand All @@ -87,10 +94,28 @@ func RegisterClient(name string, clientConstructors ...interface{}) {
app.Register(new(grpc.ClientConn))
}

// RegisterClient register client from application
func RegisterClient(name string, clientConstructors ...interface{}) {
// register newHealthCheckService if grpc client is enabled
if !registerHealthCheckService {
registerHealthCheckService = true
app.Register(newHealthCheckService)
}

_, ok := clientMap.Get(name)
if !ok {
clientMap.Set(name, true)
clientConstructors = append(clientConstructors, pb.NewHealthClient)
}
registerClient(name, clientConstructors...)
}

// Client register client from application, it is a alias to RegisterClient
var Client = RegisterClient

func init() {
clientMap = cmap.New()
Server(pb.RegisterHealthServer, health.NewServer)
app.Register(newConfiguration)
}

Expand Down
7 changes: 7 additions & 0 deletions pkg/starter/grpc/autoconfigure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"hidevops.io/hiboot/pkg/app"
"hidevops.io/hiboot/pkg/app/web"
"hidevops.io/hiboot/pkg/inject"
"hidevops.io/hiboot/pkg/starter/actuator"
"hidevops.io/hiboot/pkg/starter/grpc"
mockproto "hidevops.io/hiboot/pkg/starter/grpc/mock"
"testing"
Expand Down Expand Up @@ -108,4 +109,10 @@ func TestGrpcServerAndClient(t *testing.T) {
assert.Equal(t, "Hello "+req.Name, resp.Message)
}
})

t.Run("should get health status from client", func(t *testing.T) {
healthCheckService := applicationContext.GetInstance("grpc.healthCheckService").(actuator.HealthService)
assert.Equal(t, grpc.Profile, healthCheckService.Name())
assert.Equal(t, true, healthCheckService.Status())
})
}
35 changes: 35 additions & 0 deletions pkg/starter/grpc/health.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package grpc

import (
"context"
pb "google.golang.org/grpc/health/grpc_health_v1"
"hidevops.io/hiboot/pkg/at"
)

// controller
type healthCheckService struct {
at.HealthCheckService
// declare HelloServiceClient
healthClient pb.HealthClient
}

// Init inject helloServiceClient
func newHealthCheckService(healthClient pb.HealthClient) *healthCheckService {
return &healthCheckService{
healthClient: healthClient,
}
}

// Status return health check display name grpc
func (c *healthCheckService) Name() (name string) {
return Profile
}

// Status return grpc health check status as bool
func (c *healthCheckService) Status() (up bool) {
resp, err := c.healthClient.Check(context.TODO(), &pb.HealthCheckRequest{})
if err == nil {
up = resp.Status == pb.HealthCheckResponse_SERVING
}
return
}

0 comments on commit 309f8af

Please sign in to comment.