Skip to content

Commit

Permalink
*: implements health check (#518)
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
BusyJay authored Mar 16, 2021
1 parent c640299 commit f897d1d
Show file tree
Hide file tree
Showing 13 changed files with 1,204 additions and 100 deletions.
9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@ log = "0.4"
parking_lot = "0.11"

[workspace]
members = ["proto", "benchmark", "compiler", "interop", "tests-and-examples"]
members = [
"proto",
"benchmark",
"compiler",
"health",
"interop",
"tests-and-examples"
]

[features]
default = ["protobuf-codec", "secure", "use-bindgen"]
Expand Down
26 changes: 26 additions & 0 deletions health/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "grpcio-health"
version = "0.8.0"
edition = "2018"
authors = ["The TiKV Project Developers"]
license = "Apache-2.0"
keywords = ["grpc", "healthcheck"]
repository = "https://github.com/tikv/grpc-rs"
homepage = "https://github.com/tikv/grpc-rs"
documentation = "https://docs.rs/grpcio-health"
description = "Health check wrappers for grpcio"
categories = ["network-programming"]
readme = "README.md"

[features]
default = ["protobuf-codec", "use-bindgen"]
protobuf-codec = ["grpcio/protobuf-codec", "protobuf"]
prost-codec = ["grpcio/prost-codec", "prost"]
use-bindgen = ["grpcio/use-bindgen"]

[dependencies]
futures = "0.3"
grpcio = { path = "..", features = ["secure"], version = "0.8.0", default-features = false }
prost = { version = "0.7", optional = true }
protobuf = { version = "2", optional = true }
log = "0.4"
8 changes: 8 additions & 0 deletions health/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# grpcio-healthcheck

[![Crates.io](https://img.shields.io/crates/v/grpcio-health.svg?maxAge=2592000)](https://crates.io/crates/grpcio-health)
[![docs.rs](https://docs.rs/grpcio-health/badge.svg)](https://docs.rs/grpcio-health)

grpcio-health provides health check protos as well as some helper structs to make
health check easily.

38 changes: 38 additions & 0 deletions health/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2021 TiKV Project Authors. Licensed under Apache-2.0.

//! grpcio-health provides health check protos as well as some helper structs to make
//! health check easily. For the detail design of health checking service, see
//! https://github.com/grpc/grpc/blob/master/doc/health-checking.md.
//!
//! ### Usage
//!
//! The crate provides a default implementation of `Health` service, you can use it
//! to maintain the service states. First, you need to register it to the server builder
//! so that it can serve health check service later.
//! ```ignore
//! use grpcio_health::{HealthService, create_health};
//!
//! let service = HealthService::default();
//! let builder = builder.register_service(create_health(service.clone()));
//! ```
//! Then insert service status for query.
//! ```ignore
//! service.set_serving_status("", ServingStatus::Serving);
//! ```
//! `""` means overall health status. You can also provide specific service name.
//!
//! Client can either use `check` to do one time query or `watch` to observe status changes.
//! ```ignore
//! use grpcio_health::proto::HealthCheckRequest;
//!
//! let client = HealthClient::new(ch);
//! let req = HealthCheckRequest { service: "".to_string(), ..Default::default() };
//! let status_resp = client.check_async(&req).await.unwrap();
//! assert_eq!(statuss_resp.status, ServingStatus::Serving);
//! ```
pub mod proto;
mod service;

pub use self::proto::{create_health, HealthClient, ServingStatus};
pub use self::service::HealthService;
21 changes: 21 additions & 0 deletions health/src/proto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2021 TiKV Project Authors. Licensed under Apache-2.0.

#[cfg(feature = "prost-codec")]
mod reexports {
include!("proto/grpc.health.v1.rs");

pub use self::health_check_response::ServingStatus;
}

#[cfg(feature = "protobuf-codec")]
#[allow(deprecated)]
mod health;
#[cfg(feature = "protobuf-codec")]
mod health_grpc;
#[cfg(feature = "protobuf-codec")]
mod reexports {
pub use super::health::*;
pub use HealthCheckResponseServingStatus as ServingStatus;
}

pub use self::reexports::*;
48 changes: 48 additions & 0 deletions health/src/proto/grpc.health.v1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct HealthCheckRequest {
#[prost(string, tag="1")]
pub service: ::prost::alloc::string::String,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct HealthCheckResponse {
#[prost(enumeration="health_check_response::ServingStatus", tag="1")]
pub status: i32,
}
/// Nested message and enum types in `HealthCheckResponse`.
pub mod health_check_response {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum ServingStatus {
Unknown = 0,
Serving = 1,
NotServing = 2,
/// Used only by the Watch method.
ServiceUnknown = 3,
}
}
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 }, };
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 }, };
#[derive(Clone)]
pub struct HealthClient { client: ::grpcio::Client }
impl HealthClient {
pub fn new(channel: ::grpcio::Channel) -> Self { HealthClient { client: ::grpcio::Client::new(channel) }}
pub fn check_opt(&self, req: &HealthCheckRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<HealthCheckResponse,> { self.client.unary_call(&METHOD_HEALTH_CHECK, req, opt) }
pub fn check(&self, req: &HealthCheckRequest) -> ::grpcio::Result<HealthCheckResponse,> { self.check_opt(req, ::grpcio::CallOption::default()) }
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) }
pub fn check_async(&self, req: &HealthCheckRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<HealthCheckResponse>,> { self.check_async_opt(req, ::grpcio::CallOption::default()) }
pub fn watch_opt(&self, req: &HealthCheckRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientSStreamReceiver<HealthCheckResponse>,> { self.client.server_streaming(&METHOD_HEALTH_WATCH, req, opt) }
pub fn watch(&self, req: &HealthCheckRequest) -> ::grpcio::Result<::grpcio::ClientSStreamReceiver<HealthCheckResponse>,> { self.watch_opt(req, ::grpcio::CallOption::default()) }
pub fn spawn<F>(&self, f: F) where F: ::futures::Future<Output = ()> + Send + 'static {self.client.spawn(f)}
}
pub trait Health {
fn check(&mut self, ctx: ::grpcio::RpcContext, req: HealthCheckRequest, sink: ::grpcio::UnarySink<HealthCheckResponse>);
fn watch(&mut self, ctx: ::grpcio::RpcContext, req: HealthCheckRequest, sink: ::grpcio::ServerStreamingSink<HealthCheckResponse>);
}
pub fn create_health<S: Health + Send + Clone + 'static>(s: S) -> ::grpcio::Service {
let mut builder = ::grpcio::ServiceBuilder::new();
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_HEALTH_CHECK, move |ctx, req, resp| instance.check(ctx, req, resp));
let mut instance = s;
builder = builder.add_server_streaming_handler(&METHOD_HEALTH_WATCH, move |ctx, req, resp| instance.watch(ctx, req, resp));
builder.build()
}
Loading

0 comments on commit f897d1d

Please sign in to comment.