- add: session-service
- move: redis_cache from database service to utils so it can be reused - update: redis_cache set function to allow creating a key without a lifetime - update: services to use the new get_or_generate_service_id function
This commit is contained in:
@@ -34,7 +34,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let health_check_url = format!("http://{}:{}/health", service_address, health_port);
|
let health_check_url = format!("http://{}:{}/health", service_address, health_port);
|
||||||
|
|
||||||
// Register service with Consul
|
// Register service with Consul
|
||||||
let service_id = consul_registration::generate_service_id();
|
let service_id = consul_registration::get_or_generate_service_id();
|
||||||
let tags = vec!["version-1.0".to_string()];
|
let tags = vec!["version-1.0".to_string()];
|
||||||
let meta = HashMap::new();
|
let meta = HashMap::new();
|
||||||
consul_registration::register_service(
|
consul_registration::register_service(
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let db_nodes = get_service_address(&consul_url, "database-service").await?;
|
let db_nodes = get_service_address(&consul_url, "database-service").await?;
|
||||||
|
|
||||||
// Register service with Consul
|
// Register service with Consul
|
||||||
let service_id = consul_registration::generate_service_id();
|
let service_id = consul_registration::get_or_generate_service_id();
|
||||||
let tags = vec!["version-1.0".to_string()];
|
let tags = vec!["version-1.0".to_string()];
|
||||||
let mut meta = HashMap::new();
|
let mut meta = HashMap::new();
|
||||||
consul_registration::register_service(
|
consul_registration::register_service(
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let db_nodes = get_service_address(&consul_url, "database-service").await?;
|
let db_nodes = get_service_address(&consul_url, "database-service").await?;
|
||||||
|
|
||||||
// Register service with Consul
|
// Register service with Consul
|
||||||
let service_id = consul_registration::generate_service_id();
|
let service_id = consul_registration::get_or_generate_service_id();
|
||||||
let tags = vec!["version-1.0".to_string()];
|
let tags = vec!["version-1.0".to_string()];
|
||||||
let mut meta = HashMap::new();
|
let mut meta = HashMap::new();
|
||||||
meta.insert("name".to_string(), "Rose".to_string());
|
meta.insert("name".to_string(), "Rose".to_string());
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use sqlx::{FromRow, Row};
|
use sqlx::{FromRow, Row};
|
||||||
use crate::redis_cache::{Cache, RedisCache}; // Import RedisCache
|
use utils::redis_cache::{Cache, RedisCache}; // Import RedisCache
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use database_service::db::Database;
|
|||||||
use database_service::grpc::database_service::MyDatabaseService;
|
use database_service::grpc::database_service::MyDatabaseService;
|
||||||
use database_service::grpc::user_service_server::UserServiceServer;
|
use database_service::grpc::user_service_server::UserServiceServer;
|
||||||
use database_service::grpc::character_service_server::CharacterServiceServer;
|
use database_service::grpc::character_service_server::CharacterServiceServer;
|
||||||
use database_service::redis_cache::RedisCache;
|
use utils::redis_cache::RedisCache;
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
use sqlx::postgres::PgPoolOptions;
|
use sqlx::postgres::PgPoolOptions;
|
||||||
use std::env;
|
use std::env;
|
||||||
@@ -36,7 +36,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let health_check_url = format!("http://{}:{}/health", service_address, health_port);
|
let health_check_url = format!("http://{}:{}/health", service_address, health_port);
|
||||||
|
|
||||||
// Register service with Consul
|
// Register service with Consul
|
||||||
let service_id = consul_registration::generate_service_id();
|
let service_id = consul_registration::get_or_generate_service_id();
|
||||||
let tags = vec!["version-1.0".to_string()];
|
let tags = vec!["version-1.0".to_string()];
|
||||||
let meta = HashMap::new();
|
let meta = HashMap::new();
|
||||||
consul_registration::register_service(
|
consul_registration::register_service(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use sqlx::{FromRow, Row};
|
use sqlx::{FromRow, Row};
|
||||||
use crate::redis_cache::{RedisCache, Cache}; // Import RedisCache and Cache Trait
|
use utils::redis_cache::{RedisCache, Cache}; // Import RedisCache and Cache Trait
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let auth_node = get_service_address(&consul_url, "auth-service").await?;
|
let auth_node = get_service_address(&consul_url, "auth-service").await?;
|
||||||
|
|
||||||
// Register service with Consul
|
// Register service with Consul
|
||||||
let service_id = consul_registration::generate_service_id();
|
let service_id = consul_registration::get_or_generate_service_id();
|
||||||
let tags = vec!["version-1.0".to_string()];
|
let tags = vec!["version-1.0".to_string()];
|
||||||
let mut meta = HashMap::new();
|
let mut meta = HashMap::new();
|
||||||
consul_registration::register_service(
|
consul_registration::register_service(
|
||||||
|
|||||||
36
proto/session_service_api.proto
Normal file
36
proto/session_service_api.proto
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package session_service_api;
|
||||||
|
|
||||||
|
service SessionService {
|
||||||
|
rpc CreateSession (CreateSessionRequest) returns (SessionResponse);
|
||||||
|
rpc GetSession (GetSessionRequest) returns (SessionResponse);
|
||||||
|
rpc DeleteSession (DeleteSessionRequest) returns (Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateSessionRequest {
|
||||||
|
string session_id = 1;
|
||||||
|
int32 user_id = 2;
|
||||||
|
string username = 3;
|
||||||
|
int32 character_id = 4;
|
||||||
|
string ip_address = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetSessionRequest {
|
||||||
|
string session_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteSessionRequest {
|
||||||
|
string session_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SessionResponse {
|
||||||
|
string session_id = 1;
|
||||||
|
int32 user_id = 2;
|
||||||
|
string username = 3;
|
||||||
|
int32 character_id = 4;
|
||||||
|
string login_time = 5;
|
||||||
|
string ip_address = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Empty {}
|
||||||
20
session-service/Cargo.toml
Normal file
20
session-service/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "session-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
utils = { path = "../utils" }
|
||||||
|
dotenv = "0.15"
|
||||||
|
tokio = { version = "1.41.1", features = ["full"] }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
tracing = "0.1"
|
||||||
|
tracing-subscriber = "0.3.18"
|
||||||
|
tonic = "0.12.3"
|
||||||
|
prost = "0.13.4"
|
||||||
|
warp = "0.3.7"
|
||||||
|
chrono = "0.4.39"
|
||||||
|
serde_json = "1.0.133"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tonic-build = "0.12.3"
|
||||||
33
session-service/Dockerfile
Normal file
33
session-service/Dockerfile
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
FROM rust:1.83 AS builder
|
||||||
|
LABEL authors="raven"
|
||||||
|
|
||||||
|
WORKDIR /usr/src/proto
|
||||||
|
COPY ./proto .
|
||||||
|
|
||||||
|
WORKDIR /usr/src/utils
|
||||||
|
COPY ./utils .
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& DEBIAN_FRONTEND=noninteractive \
|
||||||
|
apt-get install --no-install-recommends --assume-yes \
|
||||||
|
protobuf-compiler
|
||||||
|
|
||||||
|
WORKDIR /usr/src/session-service
|
||||||
|
COPY ./session-service .
|
||||||
|
|
||||||
|
RUN cargo build --release
|
||||||
|
|
||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& DEBIAN_FRONTEND=noninteractive \
|
||||||
|
apt-get install --no-install-recommends --assume-yes \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY --from=builder /usr/src/session-service/target/release/session-service /usr/local/bin/session-service
|
||||||
|
|
||||||
|
EXPOSE 50055
|
||||||
|
|
||||||
|
CMD ["session-service"]
|
||||||
16
session-service/build.rs
Normal file
16
session-service/build.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
fn main() {
|
||||||
|
// gRPC Server code
|
||||||
|
tonic_build::configure()
|
||||||
|
.build_server(true) // Generate gRPC server code
|
||||||
|
.compile_well_known_types(true)
|
||||||
|
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]")
|
||||||
|
.compile_protos(&["../proto/session_service_api.proto"], &["../proto"])
|
||||||
|
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
||||||
|
|
||||||
|
// gRPC Client code
|
||||||
|
tonic_build::configure()
|
||||||
|
.build_server(false)
|
||||||
|
.compile_well_known_types(true)
|
||||||
|
.compile_protos(&["../proto/user_db_api.proto", "../proto/auth.proto"], &["../proto"])
|
||||||
|
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
||||||
|
}
|
||||||
77
session-service/src/main.rs
Normal file
77
session-service/src/main.rs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
mod session_service;
|
||||||
|
|
||||||
|
use dotenv::dotenv;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::env;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::{select, signal};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use tonic::transport::Server;
|
||||||
|
use tracing::Level;
|
||||||
|
use utils::consul_registration;
|
||||||
|
use utils::redis_cache::RedisCache;
|
||||||
|
use utils::service_discovery::get_service_address;
|
||||||
|
use crate::api::session_service_server::SessionServiceServer;
|
||||||
|
use crate::session_service::SessionServiceImpl;
|
||||||
|
|
||||||
|
pub mod api {
|
||||||
|
tonic::include_proto!("session_service_api");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
dotenv().ok();
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_max_level(Level::from_str(&env::var("LOG_LEVEL").unwrap_or_else(|_| "info".to_string())).unwrap_or_else(|_| Level::INFO))
|
||||||
|
.init();
|
||||||
|
|
||||||
|
// Set the gRPC server address
|
||||||
|
let addr = env::var("LISTEN_ADDR").unwrap_or_else(|_| "0.0.0.0".to_string());
|
||||||
|
let port = env::var("SESSION_SERVICE_PORT").unwrap_or_else(|_| "50055".to_string());
|
||||||
|
let health_port = env::var("HEALTH_CHECK_PORT").unwrap_or_else(|_| "8084".to_string());
|
||||||
|
|
||||||
|
let redis_url = std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
|
||||||
|
let consul_url = env::var("CONSUL_URL").unwrap_or_else(|_| "http://127.0.0.1:8500".to_string());
|
||||||
|
let service_name = env::var("SERVICE_NAME").unwrap_or_else(|_| "session-service".to_string());
|
||||||
|
let service_address = env::var("SESSION_SERVICE_ADDR").unwrap_or_else(|_| "127.0.0.1".to_string());
|
||||||
|
let service_port = port.clone();
|
||||||
|
let health_check_url = format!("http://{}:{}/health", service_address, health_port);
|
||||||
|
|
||||||
|
// Register service with Consul
|
||||||
|
let service_id = consul_registration::get_or_generate_service_id();
|
||||||
|
let tags = vec!["version-1.0".to_string()];
|
||||||
|
let meta = HashMap::new();
|
||||||
|
consul_registration::register_service(
|
||||||
|
&consul_url,
|
||||||
|
service_id.as_str(),
|
||||||
|
service_name.as_str(),
|
||||||
|
service_address.as_str(),
|
||||||
|
service_port.parse().unwrap_or(50055),
|
||||||
|
tags,
|
||||||
|
meta,
|
||||||
|
&health_check_url,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Start health-check endpoint
|
||||||
|
consul_registration::start_health_check(addr.as_str()).await?;
|
||||||
|
|
||||||
|
let full_addr = format!("{}:{}", &addr, port);
|
||||||
|
let address = full_addr.parse().expect("Invalid address");
|
||||||
|
let redis_cache = Arc::new(Mutex::new(RedisCache::new(&redis_url)));
|
||||||
|
let session_service = SessionServiceImpl {
|
||||||
|
redis: redis_cache
|
||||||
|
};
|
||||||
|
|
||||||
|
tokio::spawn(Server::builder()
|
||||||
|
.add_service(SessionServiceServer::new(session_service))
|
||||||
|
.serve(address));
|
||||||
|
|
||||||
|
select! {
|
||||||
|
_ = signal::ctrl_c() => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
consul_registration::deregister_service(&consul_url, service_id.as_str()).await.expect("");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
94
session-service/src/session_service.rs
Normal file
94
session-service/src/session_service.rs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use tonic::{Request, Response, Status};
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use crate::api::{
|
||||||
|
CreateSessionRequest, SessionResponse, GetSessionRequest, DeleteSessionRequest, Empty,
|
||||||
|
};
|
||||||
|
use crate::api::session_service_server::SessionService;
|
||||||
|
use utils::redis_cache::{Cache, RedisCache};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Session {
|
||||||
|
pub user_id: i32,
|
||||||
|
pub username: String,
|
||||||
|
pub character_id: i32,
|
||||||
|
pub login_time: String,
|
||||||
|
pub ip_address: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SessionServiceImpl {
|
||||||
|
pub redis: Arc<Mutex<RedisCache>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tonic::async_trait]
|
||||||
|
impl SessionService for SessionServiceImpl {
|
||||||
|
async fn create_session(
|
||||||
|
&self,
|
||||||
|
request: Request<CreateSessionRequest>,
|
||||||
|
) -> Result<Response<SessionResponse>, Status> {
|
||||||
|
let req = request.into_inner();
|
||||||
|
|
||||||
|
let session = Session {
|
||||||
|
user_id: req.user_id,
|
||||||
|
username: req.username,
|
||||||
|
character_id: req.character_id,
|
||||||
|
login_time: chrono::Utc::now().to_rfc3339(),
|
||||||
|
ip_address: req.ip_address,
|
||||||
|
};
|
||||||
|
|
||||||
|
let session_id = req.session_id;
|
||||||
|
let session_data = serde_json::to_string(&session).map_err(|_| Status::internal("Failed to serialize session"))?;
|
||||||
|
|
||||||
|
let conn = self.redis.lock().await;
|
||||||
|
conn.set(&session_id.clone(), &session_data, 0).await.map_err(|_| Status::internal("Failed to store session in Redis"))?;
|
||||||
|
|
||||||
|
let response = SessionResponse {
|
||||||
|
session_id,
|
||||||
|
user_id: session.user_id,
|
||||||
|
username: session.username,
|
||||||
|
character_id: session.character_id,
|
||||||
|
login_time: session.login_time,
|
||||||
|
ip_address: session.ip_address,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Response::new(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_session(
|
||||||
|
&self,
|
||||||
|
request: Request<GetSessionRequest>,
|
||||||
|
) -> Result<Response<SessionResponse>, Status> {
|
||||||
|
let req = request.into_inner();
|
||||||
|
let conn = self.redis.lock().await;
|
||||||
|
|
||||||
|
if let Some(session_data) = conn.get::<String>(&req.session_id).await.map_err(|_| Status::internal("Failed to fetch session from Redis"))? {
|
||||||
|
let session: Session = serde_json::from_str(&session_data).map_err(|_| Status::internal("Failed to deserialize session"))?;
|
||||||
|
|
||||||
|
let response = SessionResponse {
|
||||||
|
session_id: req.session_id,
|
||||||
|
user_id: session.user_id,
|
||||||
|
username: session.username,
|
||||||
|
character_id: session.character_id,
|
||||||
|
login_time: session.login_time,
|
||||||
|
ip_address: session.ip_address,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Response::new(response))
|
||||||
|
} else {
|
||||||
|
Err(Status::not_found("Session not found"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_session(
|
||||||
|
&self,
|
||||||
|
request: Request<DeleteSessionRequest>,
|
||||||
|
) -> Result<Response<Empty>, Status> {
|
||||||
|
let req = request.into_inner();
|
||||||
|
let mut conn = self.redis.lock().await;
|
||||||
|
|
||||||
|
conn.delete(&req.session_id).await.map_err(|_| Status::internal("Failed to delete session from Redis"))?;
|
||||||
|
|
||||||
|
Ok(Response::new(Empty {}))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod consul_registration;
|
pub mod consul_registration;
|
||||||
pub mod service_discovery;
|
pub mod service_discovery;
|
||||||
pub mod null_string;
|
pub mod null_string;
|
||||||
|
pub mod redis_cache;
|
||||||
|
|||||||
@@ -54,7 +54,11 @@ impl Cache for RedisCache {
|
|||||||
"Serialization error",
|
"Serialization error",
|
||||||
format!("Serialization error: {}", err),
|
format!("Serialization error: {}", err),
|
||||||
)))?;
|
)))?;
|
||||||
|
if ttl > 0 {
|
||||||
conn.set_ex(key, serialized_value, ttl).await
|
conn.set_ex(key, serialized_value, ttl).await
|
||||||
|
} else {
|
||||||
|
conn.set(key, serialized_value).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get<T: for<'de> Deserialize<'de> + Send + Sync>(&self, key: &String) -> Result<Option<T>, redis::RedisError> {
|
async fn get<T: for<'de> Deserialize<'de> + Send + Sync>(&self, key: &String) -> Result<Option<T>, redis::RedisError> {
|
||||||
@@ -28,7 +28,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let db_nodes = get_service_address(&consul_url, "database-service").await?;
|
let db_nodes = get_service_address(&consul_url, "database-service").await?;
|
||||||
|
|
||||||
// Register service with Consul
|
// Register service with Consul
|
||||||
let service_id = consul_registration::generate_service_id();
|
let service_id = consul_registration::get_or_generate_service_id();
|
||||||
let tags = vec!["version-1.0".to_string()];
|
let tags = vec!["version-1.0".to_string()];
|
||||||
let mut meta = HashMap::new();
|
let mut meta = HashMap::new();
|
||||||
meta.insert("name".to_string(), "Athena".to_string());
|
meta.insert("name".to_string(), "Athena".to_string());
|
||||||
|
|||||||
Reference in New Issue
Block a user