use std::collections::HashMap; use std::sync::atomic::Ordering; use etcd_client::PutOptions; use crate::error::{AppError, AppResult}; use super::EtcdRegistry; use super::types::ServiceInstance; impl EtcdRegistry { pub async fn register_self(&self, service_name: &str) -> AppResult<()> { let ttl = self.inner.config.etcd_lease_ttl()?; let listen_addr = self.inner.config.rpc_self_listen_addr()?; let instance_id = uuid::Uuid::now_v7().to_string(); let key = format!( "{}services/{service_name}/{instance_id}", self.inner.key_prefix ); let instance = ServiceInstance { addr: listen_addr.clone(), metadata: HashMap::new(), }; let value = serde_json::to_string(&instance)?; let lease_resp = { let mut client = self.inner.client.lock().await; client .lease_grant(ttl as i64, None) .await .map_err(|e| AppError::Config(format!("etcd lease_grant failed: {e}")))? }; let lease_id = lease_resp.id(); self.inner.lease_id.store(lease_id, Ordering::SeqCst); { let mut client = self.inner.client.lock().await; let opts = PutOptions::new().with_lease(lease_id); client .put(key.clone(), value, Some(opts)) .await .map_err(|e| AppError::Config(format!("etcd put failed: {e}")))?; } tracing::info!( service = service_name, addr = listen_addr.as_str(), lease_id = lease_id, "registered self in etcd" ); self.spawn_keep_alive(lease_id, key); Ok(()) } fn spawn_keep_alive(&self, lease_id: i64, _key: String) { let inner = self.inner.clone(); tokio::spawn(async move { let (mut keeper, mut stream) = { let mut client = inner.client.lock().await; match client.lease_keep_alive(lease_id).await { Ok(pair) => pair, Err(e) => { tracing::error!(lease_id, error = %e, "failed to start lease keepalive"); return; } } }; let interval_secs = inner.config.etcd_keep_alive_interval().unwrap_or(10); let mut interval = tokio::time::interval(std::time::Duration::from_secs(interval_secs)); loop { interval.tick().await; if let Err(e) = keeper.keep_alive().await { tracing::warn!(lease_id, error = %e, "lease keepalive failed"); } let _ = stream.message().await; } }); } }