-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
examples: add example to show how to use the health service #3381
Merged
Merged
Changes from 6 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
b7bd287
gh-2770: stubbed out client and server demonstration
mjpitz 28aa0ac
gh-2770: added explanation to readme
mjpitz ddac5c6
gh-2770: added preface about the health api
mjpitz 20eddff
gh-2770: updated readme with feedback from PR.
mjpitz 5b6d518
gh-2770: updated code to demonstrate the transparent use of healthchecks
mjpitz fbae36b
gh-2770: added header and updated client to use manual resolver
mjpitz d73db23
gh-2770: fixup copyright date and simplified print logic
mjpitz 33eae55
gh-2770: ran gofmt
mjpitz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# Health | ||
|
||
gRPC provides a health library to communicate a system's health to their clients. | ||
It works by providing a service definition via the [health/v1](https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto) api. | ||
|
||
By using the health library, clients can gracefully avoid using servers as they encounter issues. | ||
Most languages provide an implementation out of box, making it interoperable between systems. | ||
|
||
## Try it | ||
|
||
``` | ||
go run server/main.go -port=50051 -sleep=5s | ||
go run server/main.go -port=50052 -sleep=10s | ||
``` | ||
|
||
``` | ||
go run client/main.go | ||
``` | ||
|
||
## Explanation | ||
|
||
### Client | ||
|
||
Clients have two ways to monitor a servers health. | ||
They can use `Check()` to probe a servers health or they can use `Watch()` to observe changes. | ||
|
||
In most cases, clients do not need to directly check backend servers. | ||
Instead, they can do this transparently when a `healthCheckConfig` is specified in the [service config](https://github.com/grpc/proposal/blob/master/A17-client-side-health-checking.md#service-config-changes). | ||
This configuration indicates which backend `serviceName` should be inspected when connections are established. | ||
An empty string (`""`) typically indicates the overall health of a server should be reported. | ||
|
||
```go | ||
// import grpc/health to enable transparent client side checking | ||
import _ "google.golang.org/grpc/health" | ||
|
||
// set up appropriate service config | ||
serviceConfig := grpc.WithDefaultServiceConfig(`{ | ||
"loadBalancingPolicy": "round_robin", | ||
"healthCheckConfig": { | ||
"serviceName": "" | ||
} | ||
}`) | ||
|
||
conn, err := grpc.Dial(..., serviceConfig) | ||
``` | ||
|
||
See [A17 - Client-Side Health Checking](https://github.com/grpc/proposal/blob/master/A17-client-side-health-checking.md) for more details. | ||
|
||
### Server | ||
|
||
Servers control their serving status. | ||
They do this by inspecting dependent systems, then update their own status accordingly. | ||
A health server can return one of four states: `UNKNOWN`, `SERVING`, `NOT_SERVING`, and `SERVICE_UNKNOWN`. | ||
|
||
`UNKNOWN` indicates the current state is not yet known. | ||
This state is often seen at the start up of a server instance. | ||
|
||
`SERVING` means that the system is healthy and ready to service requests. | ||
Conversely, `NOT_SERVING` indicates the system is unable to service requests at the time. | ||
|
||
`SERVICE_UNKNOWN` communicates the `serviceName` requested by the client is not known by the server. | ||
This status is only reported by the `Watch()` call. | ||
|
||
A server may toggle its health using `healthServer.SetServingStatus("serviceName", servingStatus)`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/* | ||
* | ||
* Copyright 2018 gRPC authors. | ||
* | ||
* 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" | ||
"flag" | ||
"fmt" | ||
"log" | ||
"time" | ||
|
||
"google.golang.org/grpc" | ||
pb "google.golang.org/grpc/examples/features/proto/echo" | ||
_ "google.golang.org/grpc/health" | ||
"google.golang.org/grpc/resolver" | ||
"google.golang.org/grpc/resolver/manual" | ||
) | ||
|
||
var serviceConfig = `{ | ||
"loadBalancingPolicy": "round_robin", | ||
"healthCheckConfig": { | ||
"serviceName": "" | ||
} | ||
}` | ||
|
||
func callUnaryEcho(c pb.EchoClient) { | ||
ctx, cancel := context.WithTimeout(context.Background(), time.Second) | ||
defer cancel() | ||
r, err := c.UnaryEcho(ctx, &pb.EchoRequest{}) | ||
if err != nil { | ||
fmt.Println(fmt.Sprintf("UnaryEcho: _, %v", err)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be simplified a bit:
|
||
} else { | ||
fmt.Println("UnaryEcho: ", r.GetMessage()) | ||
} | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
|
||
r, cleanup := manual.GenerateAndRegisterManualResolver() | ||
defer cleanup() | ||
r.InitialState(resolver.State{ | ||
Addresses: []resolver.Address{ | ||
{ Addr: "localhost:50051" }, | ||
{ Addr: "localhost:50052" }, | ||
}, | ||
}) | ||
|
||
address := fmt.Sprintf("%s:///unused", r.Scheme()) | ||
|
||
options := []grpc.DialOption{ | ||
grpc.WithInsecure(), | ||
grpc.WithBlock(), | ||
grpc.WithDefaultServiceConfig(serviceConfig), | ||
} | ||
|
||
conn, err := grpc.Dial(address, options...) | ||
if err != nil { | ||
log.Fatalf("did not connect %v", err) | ||
} | ||
defer conn.Close() | ||
|
||
echoClient := pb.NewEchoClient(conn) | ||
|
||
for { | ||
callUnaryEcho(echoClient) | ||
time.Sleep(time.Second) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* | ||
* | ||
* Copyright 2018 gRPC authors. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please update date. |
||
* | ||
* 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" | ||
"flag" | ||
"fmt" | ||
"log" | ||
"net" | ||
"time" | ||
|
||
"google.golang.org/grpc" | ||
pb "google.golang.org/grpc/examples/features/proto/echo" | ||
"google.golang.org/grpc/health" | ||
healthpb "google.golang.org/grpc/health/grpc_health_v1" | ||
) | ||
|
||
var ( | ||
port = flag.Int("port", 50051, "the port to serve on") | ||
sleep = flag.Duration("sleep", time.Second * 5, "duration between changes in health") | ||
|
||
system = "" // empty string represents the health of the system | ||
) | ||
|
||
type echoServer struct { | ||
pb.UnimplementedEchoServer | ||
} | ||
|
||
func (e *echoServer) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { | ||
return &pb.EchoResponse{ | ||
Message: fmt.Sprintf("hello from localhost:%d", *port), | ||
}, nil | ||
} | ||
|
||
var _ pb.EchoServer = &echoServer{} | ||
|
||
func main() { | ||
flag.Parse() | ||
|
||
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) | ||
if err != nil { | ||
log.Fatalf("failed to listen: %v", err) | ||
} | ||
|
||
s := grpc.NewServer() | ||
healthcheck := health.NewServer() | ||
healthpb.RegisterHealthServer(s, healthcheck) | ||
pb.RegisterEchoServer(s, &echoServer{}) | ||
|
||
go func() { | ||
// asynchronously inspect dependencies and toggle serving status as needed | ||
next := healthpb.HealthCheckResponse_SERVING | ||
|
||
for { | ||
healthcheck.SetServingStatus(system, next) | ||
|
||
if next == healthpb.HealthCheckResponse_SERVING { | ||
next = healthpb.HealthCheckResponse_NOT_SERVING | ||
} else { | ||
next = healthpb.HealthCheckResponse_SERVING | ||
} | ||
|
||
time.Sleep(*sleep) | ||
} | ||
}() | ||
|
||
if err := s.Serve(lis); err != nil { | ||
log.Fatalf("failed to serve: %v", err) | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please update to 2020