Skip to content

Commit f897d1d

Browse files
authored
*: implements health check (#518)
This PR provides simple health check implementations referring to the go version. It provides a HealthService to maintain the service statuses and wake up watchers. I use a standalone crate to avoid introducing protobuf specific code into the grpcio crate. And I don't reuse grpcio-proto to avoid the dependency of protobuf-build which just make things complicated by introducing a lot of dependencies and resulting in a dependency circle. Signed-off-by: Jay Lee <[email protected]>
1 parent c640299 commit f897d1d

File tree

13 files changed

+1204
-100
lines changed

13 files changed

+1204
-100
lines changed

Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,14 @@ log = "0.4"
2727
parking_lot = "0.11"
2828

2929
[workspace]
30-
members = ["proto", "benchmark", "compiler", "interop", "tests-and-examples"]
30+
members = [
31+
"proto",
32+
"benchmark",
33+
"compiler",
34+
"health",
35+
"interop",
36+
"tests-and-examples"
37+
]
3138

3239
[features]
3340
default = ["protobuf-codec", "secure", "use-bindgen"]

health/Cargo.toml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[package]
2+
name = "grpcio-health"
3+
version = "0.8.0"
4+
edition = "2018"
5+
authors = ["The TiKV Project Developers"]
6+
license = "Apache-2.0"
7+
keywords = ["grpc", "healthcheck"]
8+
repository = "https://github.com/tikv/grpc-rs"
9+
homepage = "https://github.com/tikv/grpc-rs"
10+
documentation = "https://docs.rs/grpcio-health"
11+
description = "Health check wrappers for grpcio"
12+
categories = ["network-programming"]
13+
readme = "README.md"
14+
15+
[features]
16+
default = ["protobuf-codec", "use-bindgen"]
17+
protobuf-codec = ["grpcio/protobuf-codec", "protobuf"]
18+
prost-codec = ["grpcio/prost-codec", "prost"]
19+
use-bindgen = ["grpcio/use-bindgen"]
20+
21+
[dependencies]
22+
futures = "0.3"
23+
grpcio = { path = "..", features = ["secure"], version = "0.8.0", default-features = false }
24+
prost = { version = "0.7", optional = true }
25+
protobuf = { version = "2", optional = true }
26+
log = "0.4"

health/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# grpcio-healthcheck
2+
3+
[![Crates.io](https://img.shields.io/crates/v/grpcio-health.svg?maxAge=2592000)](https://crates.io/crates/grpcio-health)
4+
[![docs.rs](https://docs.rs/grpcio-health/badge.svg)](https://docs.rs/grpcio-health)
5+
6+
grpcio-health provides health check protos as well as some helper structs to make
7+
health check easily.
8+

health/src/lib.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2021 TiKV Project Authors. Licensed under Apache-2.0.
2+
3+
//! grpcio-health provides health check protos as well as some helper structs to make
4+
//! health check easily. For the detail design of health checking service, see
5+
//! https://github.com/grpc/grpc/blob/master/doc/health-checking.md.
6+
//!
7+
//! ### Usage
8+
//!
9+
//! The crate provides a default implementation of `Health` service, you can use it
10+
//! to maintain the service states. First, you need to register it to the server builder
11+
//! so that it can serve health check service later.
12+
//! ```ignore
13+
//! use grpcio_health::{HealthService, create_health};
14+
//!
15+
//! let service = HealthService::default();
16+
//! let builder = builder.register_service(create_health(service.clone()));
17+
//! ```
18+
//! Then insert service status for query.
19+
//! ```ignore
20+
//! service.set_serving_status("", ServingStatus::Serving);
21+
//! ```
22+
//! `""` means overall health status. You can also provide specific service name.
23+
//!
24+
//! Client can either use `check` to do one time query or `watch` to observe status changes.
25+
//! ```ignore
26+
//! use grpcio_health::proto::HealthCheckRequest;
27+
//!
28+
//! let client = HealthClient::new(ch);
29+
//! let req = HealthCheckRequest { service: "".to_string(), ..Default::default() };
30+
//! let status_resp = client.check_async(&req).await.unwrap();
31+
//! assert_eq!(statuss_resp.status, ServingStatus::Serving);
32+
//! ```
33+
34+
pub mod proto;
35+
mod service;
36+
37+
pub use self::proto::{create_health, HealthClient, ServingStatus};
38+
pub use self::service::HealthService;

health/src/proto.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2021 TiKV Project Authors. Licensed under Apache-2.0.
2+
3+
#[cfg(feature = "prost-codec")]
4+
mod reexports {
5+
include!("proto/grpc.health.v1.rs");
6+
7+
pub use self::health_check_response::ServingStatus;
8+
}
9+
10+
#[cfg(feature = "protobuf-codec")]
11+
#[allow(deprecated)]
12+
mod health;
13+
#[cfg(feature = "protobuf-codec")]
14+
mod health_grpc;
15+
#[cfg(feature = "protobuf-codec")]
16+
mod reexports {
17+
pub use super::health::*;
18+
pub use HealthCheckResponseServingStatus as ServingStatus;
19+
}
20+
21+
pub use self::reexports::*;

health/src/proto/grpc.health.v1.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#[derive(Clone, PartialEq, ::prost::Message)]
2+
pub struct HealthCheckRequest {
3+
#[prost(string, tag="1")]
4+
pub service: ::prost::alloc::string::String,
5+
}
6+
#[derive(Clone, PartialEq, ::prost::Message)]
7+
pub struct HealthCheckResponse {
8+
#[prost(enumeration="health_check_response::ServingStatus", tag="1")]
9+
pub status: i32,
10+
}
11+
/// Nested message and enum types in `HealthCheckResponse`.
12+
pub mod health_check_response {
13+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
14+
#[repr(i32)]
15+
pub enum ServingStatus {
16+
Unknown = 0,
17+
Serving = 1,
18+
NotServing = 2,
19+
/// Used only by the Watch method.
20+
ServiceUnknown = 3,
21+
}
22+
}
23+
const METHOD_HEALTH_CHECK: ::grpcio::Method<HealthCheckRequest, HealthCheckResponse> = ::grpcio::Method{ty: ::grpcio::MethodType::Unary, name: "/grpc.health.v1.Health/Check", req_mar: ::grpcio::Marshaller { ser: ::grpcio::pr_ser, de: ::grpcio::pr_de }, resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pr_ser, de: ::grpcio::pr_de }, };
24+
const METHOD_HEALTH_WATCH: ::grpcio::Method<HealthCheckRequest, HealthCheckResponse> = ::grpcio::Method{ty: ::grpcio::MethodType::ServerStreaming, name: "/grpc.health.v1.Health/Watch", req_mar: ::grpcio::Marshaller { ser: ::grpcio::pr_ser, de: ::grpcio::pr_de }, resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pr_ser, de: ::grpcio::pr_de }, };
25+
#[derive(Clone)]
26+
pub struct HealthClient { client: ::grpcio::Client }
27+
impl HealthClient {
28+
pub fn new(channel: ::grpcio::Channel) -> Self { HealthClient { client: ::grpcio::Client::new(channel) }}
29+
pub fn check_opt(&self, req: &HealthCheckRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<HealthCheckResponse,> { self.client.unary_call(&METHOD_HEALTH_CHECK, req, opt) }
30+
pub fn check(&self, req: &HealthCheckRequest) -> ::grpcio::Result<HealthCheckResponse,> { self.check_opt(req, ::grpcio::CallOption::default()) }
31+
pub fn check_async_opt(&self, req: &HealthCheckRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<HealthCheckResponse>,> { self.client.unary_call_async(&METHOD_HEALTH_CHECK, req, opt) }
32+
pub fn check_async(&self, req: &HealthCheckRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<HealthCheckResponse>,> { self.check_async_opt(req, ::grpcio::CallOption::default()) }
33+
pub fn watch_opt(&self, req: &HealthCheckRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientSStreamReceiver<HealthCheckResponse>,> { self.client.server_streaming(&METHOD_HEALTH_WATCH, req, opt) }
34+
pub fn watch(&self, req: &HealthCheckRequest) -> ::grpcio::Result<::grpcio::ClientSStreamReceiver<HealthCheckResponse>,> { self.watch_opt(req, ::grpcio::CallOption::default()) }
35+
pub fn spawn<F>(&self, f: F) where F: ::futures::Future<Output = ()> + Send + 'static {self.client.spawn(f)}
36+
}
37+
pub trait Health {
38+
fn check(&mut self, ctx: ::grpcio::RpcContext, req: HealthCheckRequest, sink: ::grpcio::UnarySink<HealthCheckResponse>);
39+
fn watch(&mut self, ctx: ::grpcio::RpcContext, req: HealthCheckRequest, sink: ::grpcio::ServerStreamingSink<HealthCheckResponse>);
40+
}
41+
pub fn create_health<S: Health + Send + Clone + 'static>(s: S) -> ::grpcio::Service {
42+
let mut builder = ::grpcio::ServiceBuilder::new();
43+
let mut instance = s.clone();
44+
builder = builder.add_unary_handler(&METHOD_HEALTH_CHECK, move |ctx, req, resp| instance.check(ctx, req, resp));
45+
let mut instance = s;
46+
builder = builder.add_server_streaming_handler(&METHOD_HEALTH_WATCH, move |ctx, req, resp| instance.watch(ctx, req, resp));
47+
builder.build()
48+
}

0 commit comments

Comments
 (0)