mod character_db_client; mod character_service; pub mod database { tonic::include_proto!("character_db_api"); } use crate::character_db_client::CharacterDbClient; use crate::character_service::character::character_service_server::CharacterServiceServer; use crate::character_service::MyCharacterService; use dotenv::dotenv; use std::env; use std::str::FromStr; use std::sync::Arc; use tokio::sync::oneshot; use tokio::time::{timeout, Duration}; use tonic::transport::Server; use tracing::{info, error, warn, Level}; use tracing_subscriber::EnvFilter; use utils::logging; use utils::service_discovery::get_kube_service_endpoints_by_dns; #[tokio::main] async fn main() -> Result<(), Box> { dotenv().ok(); let app_name = env!("CARGO_PKG_NAME"); logging::setup_logging(app_name, &["character_service"]); // Set the gRPC server address let addr = env::var("LISTEN_ADDR").unwrap_or_else(|_| "0.0.0.0".to_string()); let port = env::var("SERVICE_PORT").unwrap_or_else(|_| "50053".to_string()); let db_url = format!( "http://{}", get_kube_service_endpoints_by_dns("database-service", "tcp", "database-service") .await? .get(0) .unwrap() ); let full_addr = format!("{}:{}", &addr, port); let address = full_addr.parse().expect("Invalid address"); let character_db_client = Arc::new(CharacterDbClient::connect(&db_url).await?); let character_service = MyCharacterService { character_db_client }; let (mut health_reporter, health_service) = tonic_health::server::health_reporter(); health_reporter .set_serving::>() .await; // Create shutdown signal channel let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>(); let server_task = tokio::spawn(async move { let server = Server::builder() .add_service(health_service) .add_service(CharacterServiceServer::new(character_service)) .serve_with_shutdown(address, async { shutdown_rx.await.ok(); info!("Character service gRPC server shutdown signal received"); }); if let Err(e) = server.await { error!("Character service gRPC server error: {}", e); } else { info!("Character service gRPC server shut down gracefully"); } }); info!("Character Service running on {}", address); // Wait for shutdown signal info!("Character service is running. Waiting for shutdown signal..."); utils::signal_handler::wait_for_signal().await; info!("Shutdown signal received. Beginning graceful shutdown..."); // Signal the gRPC server to stop accepting new connections if let Err(_) = shutdown_tx.send(()) { warn!("Failed to send shutdown signal to gRPC server (receiver may have been dropped)"); } // Wait for the gRPC server to finish with a timeout match timeout(Duration::from_secs(30), server_task).await { Ok(result) => { if let Err(e) = result { error!("Character service gRPC server task failed: {}", e); } else { info!("Character service shut down successfully"); } } Err(_) => { error!("Character service gRPC server shutdown timed out after 30 seconds"); } } Ok(()) }