| Crates.io | revoke-registry |
| lib.rs | revoke-registry |
| version | 0.3.0 |
| created_at | 2025-07-13 06:01:37.379803+00 |
| updated_at | 2025-07-13 06:01:37.379803+00 |
| description | Service registry and discovery for Revoke microservices framework |
| homepage | |
| repository | https://github.com/revoke/revoke |
| max_upload_size | |
| id | 1750019 |
| size | 79,556 |
Service registry module for the Revoke microservices framework, providing service discovery and health monitoring capabilities.
Add to your Cargo.toml:
[dependencies]
revoke-registry = { version = "0.1", features = ["consul"] }
memory: In-memory registry (default, useful for testing)consul: HashiCorp Consul integrationetcd: etcd v3 integrationkubernetes: Kubernetes service discoveryfull: Enable all backendsuse revoke_registry::{ServiceRegistry, ServiceInfo};
use revoke_core::{ServiceRegistry as RegistryTrait, Protocol};
use uuid::Uuid;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create registry client
let registry = ServiceRegistry::consul("http://localhost:8500").await?;
// Register service
let service = ServiceInfo {
id: Uuid::new_v4(),
name: "user-service".to_string(),
version: "1.0.0".to_string(),
address: "192.168.1.100".to_string(),
port: 8080,
protocol: Protocol::Http,
metadata: Default::default(),
};
registry.register(service).await?;
// Discover services
let services = registry.get_service("user-service").await?;
for svc in services {
println!("Found: {}:{}", svc.address, svc.port);
}
Ok(())
}
In-memory implementation for development and testing:
use revoke_registry::memory::MemoryRegistry;
let registry = MemoryRegistry::new();
// Register with TTL
registry.register_with_ttl(service, Duration::from_secs(30)).await?;
// Get all services
let all_services = registry.list_all_services().await?;
HashiCorp Consul integration with full feature support:
use revoke_registry::consul::{ConsulRegistry, ConsulConfig};
use std::time::Duration;
let config = ConsulConfig {
address: "localhost:8500".to_string(),
datacenter: Some("dc1".to_string()),
token: None, // ACL token if needed
check_interval: Duration::from_secs(10),
deregister_critical: "30s".to_string(),
..Default::default()
};
let registry = ConsulRegistry::new(config).await?;
// Enable health checks
registry.enable_health_check(
service_id,
HealthCheckConfig {
interval: Duration::from_secs(10),
timeout: Duration::from_secs(5),
deregister_after: Duration::from_secs(30),
}
).await?;
etcd v3 API integration:
use revoke_registry::etcd::{EtcdRegistry, EtcdConfig};
let config = EtcdConfig {
endpoints: vec!["http://localhost:2379".to_string()],
username: None,
password: None,
ttl: 60, // Service TTL in seconds
..Default::default()
};
let registry = EtcdRegistry::new(config).await?;
// Watch for service changes
let mut watcher = registry.watch("user-service").await?;
while let Some(event) = watcher.next().await {
match event {
ServiceEvent::Added(service) => println!("Service added: {}", service.id),
ServiceEvent::Modified(service) => println!("Service modified: {}", service.id),
ServiceEvent::Removed(id) => println!("Service removed: {}", id),
}
}
use revoke_registry::discovery::DiscoveryClient;
let discovery = DiscoveryClient::new(registry);
// Get service instance with load balancing
let instance = discovery
.get_instance("user-service")
.with_load_balancer(LoadBalancer::RoundRobin)
.await?;
// Make request to the instance
let url = format!("http://{}:{}/api/users", instance.address, instance.port);
use revoke_registry::discovery::ServiceResolver;
let resolver = ServiceResolver::new(registry);
// Resolve service name to address
let endpoint = resolver.resolve("user-service").await?;
// Use with HTTP client
let client = reqwest::Client::new();
let response = client.get(&endpoint.url("/api/users")).send().await?;
use revoke_registry::health::{HealthChecker, HealthCheckType};
let checker = HealthChecker::new(registry);
// HTTP health check
checker.add_check(
service_id,
HealthCheckType::Http {
url: "http://localhost:8080/health".to_string(),
method: "GET".to_string(),
expected_status: 200,
timeout: Duration::from_secs(5),
}
).await?;
// TCP health check
checker.add_check(
service_id,
HealthCheckType::Tcp {
address: "localhost:8080".to_string(),
timeout: Duration::from_secs(5),
}
).await?;
// Script health check
checker.add_check(
service_id,
HealthCheckType::Script {
command: "/usr/local/bin/check-health.sh".to_string(),
args: vec![service_id.to_string()],
timeout: Duration::from_secs(10),
}
).await?;
use revoke_registry::health::PassiveHealthCheck;
let passive_check = PassiveHealthCheck::new()
.failure_threshold(5) // Mark unhealthy after 5 failures
.success_threshold(2) // Mark healthy after 2 successes
.timeout(Duration::from_secs(30));
// Report request outcome
passive_check.report_success(service_id).await;
passive_check.report_failure(service_id).await;
// Check health status
let is_healthy = passive_check.is_healthy(service_id).await;
let mut metadata = HashMap::new();
metadata.insert("region".to_string(), "us-west-2".to_string());
metadata.insert("zone".to_string(), "us-west-2a".to_string());
metadata.insert("version".to_string(), "1.2.3".to_string());
metadata.insert("commit".to_string(), "abc123".to_string());
let service = ServiceInfo {
metadata,
..Default::default()
};
// Query by metadata
let services = registry
.query_services()
.with_metadata("region", "us-west-2")
.with_metadata("version", "1.2.3")
.execute()
.await?;
use revoke_registry::groups::ServiceGroup;
// Create service group
let group = ServiceGroup::new("api-services")
.add_service("user-service")
.add_service("order-service")
.add_service("payment-service");
registry.register_group(group).await?;
// Get all services in group
let services = registry.get_group("api-services").await?;
use revoke_registry::resilience::RegistryCircuitBreaker;
let breaker = RegistryCircuitBreaker::new(registry)
.failure_threshold(5)
.recovery_timeout(Duration::from_secs(60));
// Discover with circuit breaker
match breaker.get_service("user-service").await {
Ok(services) => {
// Use services
}
Err(e) if e.is_circuit_open() => {
// Use fallback
}
Err(e) => {
// Handle other errors
}
}
use revoke_registry::multiregion::{MultiRegionRegistry, RegionConfig};
let regions = vec![
RegionConfig {
name: "us-west-2".to_string(),
endpoint: "http://consul-us-west-2:8500".to_string(),
priority: 1,
},
RegionConfig {
name: "us-east-1".to_string(),
endpoint: "http://consul-us-east-1:8500".to_string(),
priority: 2,
},
];
let registry = MultiRegionRegistry::new(regions).await?;
// Prefer local region
let services = registry
.get_service("user-service")
.prefer_region("us-west-2")
.await?;
use revoke_registry::cache::{CachedRegistry, CacheConfig};
let cache_config = CacheConfig {
ttl: Duration::from_secs(5),
max_size: 1000,
refresh_interval: Duration::from_secs(30),
};
let cached_registry = CachedRegistry::new(registry, cache_config);
// Cached lookups
let services = cached_registry.get_service("user-service").await?;
// Register multiple services
let services = vec![service1, service2, service3];
registry.register_batch(services).await?;
// Deregister multiple services
let ids = vec![id1, id2, id3];
registry.deregister_batch(ids).await?;
// Batch health updates
let updates = vec![
(id1, HealthStatus::Healthy),
(id2, HealthStatus::Unhealthy),
];
registry.update_health_batch(updates).await?;
use revoke_registry::metrics::RegistryMetrics;
let metrics = RegistryMetrics::new(®istry);
// Get registry statistics
let stats = metrics.get_stats().await?;
println!("Total services: {}", stats.total_services);
println!("Healthy services: {}", stats.healthy_services);
println!("Unhealthy services: {}", stats.unhealthy_services);
// Export Prometheus metrics
let prometheus_metrics = metrics.export_prometheus();
service-name-v1)See the examples directory:
basic_registration.rs - Simple service registrationhealth_checks.rs - Health check configurationservice_discovery.rs - Client-side discovery patternsmulti_region.rs - Multi-region deployment