diff options
| author | n1c00o <git.n1c00o@gmail.com> | 2021-08-05 15:18:09 +0200 |
|---|---|---|
| committer | n1c00o <git.n1c00o@gmail.com> | 2021-08-05 15:18:09 +0200 |
| commit | 415ac374f0e2ec721c1f35cc106c2b118babe55c (patch) | |
| tree | d54a6d5c6a37a9c2deba51f3d9dd20e1ac96e4c3 | |
| parent | d2a5d45e86cbcd78971727827c5575b55bb63680 (diff) | |
Add healtch check and required env
| -rw-r--r-- | Cargo.lock | 16 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | build.rs | 12 | ||||
| -rw-r--r-- | clawflake.rs | 83 | ||||
| -rw-r--r-- | scripts/health_check.sh | 4 | ||||
| -rw-r--r-- | src/client.rs | 8 | ||||
| -rw-r--r-- | src/server.rs | 26 |
7 files changed, 134 insertions, 16 deletions
@@ -95,6 +95,7 @@ dependencies = [ "tokio", "tonic", "tonic-build", + "tonic-health", ] [[package]] @@ -815,6 +816,21 @@ dependencies = [ ] [[package]] +name = "tonic-health" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14e6de0a7a1b27d9899031b01b83eb09fdc36f3fe8e6254a81840006a463c6d5" +dependencies = [ + "async-stream", + "bytes", + "prost", + "tokio", + "tokio-stream", + "tonic", + "tonic-build", +] + +[[package]] name = "tower" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -17,6 +17,7 @@ log = "0.4.14" serde_json = "1.0.66" parking_lot = "0.11.1" tonic = "0.5" +tonic-health = "0.4.0" prost = "0.8" tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } @@ -1,4 +1,12 @@ +use std::path::PathBuf; + fn main() -> Result<(), Box<dyn std::error::Error>> { - tonic_build::compile_protos("proto/clawflake.proto")?; - Ok(()) + tonic_build::compile_protos("proto/clawflake.proto")?; + + tonic_build::configure() + .out_dir(PathBuf::from("./")) + .build_server(false) + .compile(&["proto/clawflake.proto"], &["proto"]) + .unwrap(); + Ok(()) } diff --git a/clawflake.rs b/clawflake.rs new file mode 100644 index 0000000..c3c894b --- /dev/null +++ b/clawflake.rs @@ -0,0 +1,83 @@ +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct IdRequest {} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct IdReply { + #[prost(string, tag = "1")] + pub id: ::prost::alloc::string::String, +} +#[doc = r" Generated client implementations."] +pub mod clawflake_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + #[derive(Debug, Clone)] + pub struct ClawflakeClient<T> { + inner: tonic::client::Grpc<T>, + } + impl ClawflakeClient<tonic::transport::Channel> { + #[doc = r" Attempt to create a new client by connecting to a given endpoint."] + pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error> + where + D: std::convert::TryInto<tonic::transport::Endpoint>, + D::Error: Into<StdError>, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl<T> ClawflakeClient<T> + where + T: tonic::client::GrpcService<tonic::body::BoxBody>, + T::ResponseBody: Body + Send + Sync + 'static, + T::Error: Into<StdError>, + <T::ResponseBody as Body>::Error: Into<StdError> + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_interceptor<F>( + inner: T, + interceptor: F, + ) -> ClawflakeClient<InterceptedService<T, F>> + where + F: FnMut(tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status>, + T: tonic::codegen::Service< + http::Request<tonic::body::BoxBody>, + Response = http::Response< + <T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody, + >, + >, + <T as tonic::codegen::Service<http::Request<tonic::body::BoxBody>>>::Error: + Into<StdError> + Send + Sync, + { + ClawflakeClient::new(InterceptedService::new(inner, interceptor)) + } + #[doc = r" Compress requests with `gzip`."] + #[doc = r""] + #[doc = r" This requires the server to support it otherwise it might respond with an"] + #[doc = r" error."] + pub fn send_gzip(mut self) -> Self { + self.inner = self.inner.send_gzip(); + self + } + #[doc = r" Enable decompressing responses with `gzip`."] + pub fn accept_gzip(mut self) -> Self { + self.inner = self.inner.accept_gzip(); + self + } + pub async fn get_id( + &mut self, + request: impl tonic::IntoRequest<super::IdRequest>, + ) -> Result<tonic::Response<super::IdReply>, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static("/clawflake.Clawflake/GetID"); + self.inner.unary(request.into_request(), path, codec).await + } + } +} diff --git a/scripts/health_check.sh b/scripts/health_check.sh new file mode 100644 index 0000000..df89d42 --- /dev/null +++ b/scripts/health_check.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +# requires https://github.com/grpc-ecosystem/grpc-health-probe +grpc_health_probe -addr=\[::1]:50051 -service=clawflake.Clawflake diff --git a/src/client.rs b/src/client.rs index f0b871b..1fc4075 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,12 +1,12 @@ // !Sample implementation of a gRPC Client for Clawflake, not meant to production! -use clawflake::clawflake_client::ClawflakeClient; -use clawflake::IdRequest; - pub mod clawflake { - tonic::include_proto!("clawflake"); + tonic::include_proto!("clawflake"); } +use clawflake::clawflake_client::ClawflakeClient; +use clawflake::IdRequest; + #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let addr = "http://[::1]:50051"; diff --git a/src/server.rs b/src/server.rs index 2d18d97..516deda 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,3 +1,4 @@ +use std::env; use std::net::SocketAddr; use std::process::exit; @@ -5,20 +6,16 @@ use log::info; mod logger; use tonic::{transport::Server, Request, Response, Status}; -use clawflake::clawflake_server::{Clawflake, ClawflakeServer}; -use clawflake::{IdReply, IdRequest}; - pub mod clawflake { - tonic::include_proto!("clawflake"); + tonic::include_proto!("clawflake"); } +use clawflake::clawflake_server::{Clawflake, ClawflakeServer}; +use clawflake::{IdReply, IdRequest}; + mod id_worker; use id_worker::IdWorker; -const EPOCH: i64 = 1_564_790_400_000; // todo(n1c00o): find a good custom epoch for production cuz its fun -const WORKER_ID: i64 = 0; // todo(n1c00o): need a way to detect the worker id from Kubernetes -const DATACENTER_ID: i64 = 0; // todo(n1c00o): need a way to detect the data center id from idk - #[derive(Debug, Default)] pub struct MyClawflakeService { } @@ -31,7 +28,11 @@ impl Clawflake for MyClawflakeService { ) -> Result<Response<IdReply>, Status> { info!("request on GetID"); - let mut worker: IdWorker = IdWorker::new(EPOCH, WORKER_ID, DATACENTER_ID); + let mut worker: IdWorker = IdWorker::new( + env::var("CLAWFLAKE_EPOCH").expect("Missing env `CLAWFLAKE_EPOCH`").parse::<i64>().unwrap(), + env::var("CLAWFLAKE_WORKER_ID").expect("Missing env `CLAWFLAKE_WORKER_ID`").parse::<i64>().unwrap(), + env::var("CLAWFLAKE_DATACENTER_ID").expect("Missing env `CLAWFLAKE_DATACENTER_ID`").parse::<i64>().unwrap() + ); let reply: IdReply = clawflake::IdReply { id: format!("{}", worker.next_id()).into(), @@ -52,13 +53,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { _ => {} } + // init tonic_health + let (mut health_reporter, health_service) = tonic_health::server::health_reporter(); + health_reporter.set_serving::<ClawflakeServer<MyClawflakeService>>().await; + // init tonic and IdWorker - let addr: SocketAddr = "[::1]:50051".parse()?; //todo(n1c00o): make sure we can manage addr, then make the Dockerfile + let addr: SocketAddr = "[::1]:50051".parse()?; let srv: MyClawflakeService = MyClawflakeService::default(); println!("Service listening on {}", addr); Server::builder() + .add_service(health_service) .add_service(ClawflakeServer::new(srv)) .serve(addr) .await?; |
