# Batata Client
A Rust client library for [Nacos](https://nacos.io/) service discovery and configuration management.
[](https://crates.io/crates/batata-client)
[](https://docs.rs/batata-client)
[](LICENSE)
## Features
- **Configuration Management**
- Get, publish, and remove configurations
- Listen for configuration changes with callbacks
- Search configurations with pagination
- Local caching with MD5 validation
- **Service Discovery**
- Register, deregister, and update service instances
- Query service instances (all or healthy only)
- Subscribe to service change notifications
- Automatic heartbeat for ephemeral instances
- **Authentication**
- Username/Password authentication
- AccessKey/SecretKey authentication (Alibaba Cloud style)
- Automatic token refresh
- **Enterprise Features**
- TLS/SSL support
- Multiple server addresses with load balancing
- Automatic failover and retry
- Local file cache for disaster recovery
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
batata-client = "0.0.1"
tokio = { version = "1", features = ["full"] }
```
## Quick Start
### Basic Usage
```rust
use batata_client::{BatataClient, Result};
#[tokio::main]
async fn main() -> Result<()> {
// Create client
let client = BatataClient::builder()
.server_addr("localhost:8848")
.namespace("public")
.build()
.await?;
// Configuration management
let config_service = client.config_service();
// Get configuration
let content = config_service.get_config("my-config", "DEFAULT_GROUP").await?;
println!("Config: {}", content);
// Publish configuration
config_service.publish_config("my-config", "DEFAULT_GROUP", "key=value").await?;
// Service discovery
let naming_service = client.naming_service();
// Register instance
naming_service.register_instance_simple("my-service", "127.0.0.1", 8080).await?;
// Get healthy instances
let instances = naming_service.select_instances("my-service", "DEFAULT_GROUP", true).await?;
// Shutdown
client.shutdown().await;
Ok(())
}
```
### With Authentication
```rust
use batata_client::BatataClient;
// Username/Password authentication
let client = BatataClient::builder()
.server_addr("localhost:8848")
.username_password("nacos", "nacos")
.build()
.await?;
// AccessKey/SecretKey authentication
let client = BatataClient::builder()
.server_addr("localhost:8848")
.access_key("your-access-key", "your-secret-key")
.build()
.await?;
```
### With TLS
```rust
use batata_client::{BatataClient, TlsConfig};
let client = BatataClient::builder()
.server_addr("localhost:8848")
.tls_config(
TlsConfig::new()
.with_ca_cert("/path/to/ca.pem")
.with_client_cert("/path/to/cert.pem", "/path/to/key.pem")
)
.build()
.await?;
```
### Listen for Configuration Changes
```rust
use batata_client::{BatataClient, ConfigChangeEvent};
let client = BatataClient::builder()
.server_addr("localhost:8848")
.build()
.await?;
let config_service = client.config_service();
// Add listener with callback
event.old_content.unwrap_or_default(),
event.new_content);
});
// Start config service to enable listening
client.start_config_service().await?;
```
### Subscribe to Service Changes
```rust
use batata_client::{BatataClient, ServiceChangeEvent};
let client = BatataClient::builder()
.server_addr("localhost:8848")
.build()
.await?;
let naming_service = client.naming_service();
// Subscribe with callback
naming_service.subscribe_callback("my-service", "DEFAULT_GROUP", |event: ServiceChangeEvent| {
println!("Service instances changed: {:?}", event.instances);
}).await?;
```
### Search Configurations
```rust
let config_service = client.config_service();
// Search with pagination
let (total, items) = config_service
.search_config("test*", "DEFAULT_GROUP", 1, 10)
.await?;
println!("Found {} configs", total);
for item in items {
println!("- {}: {}", item.data_id, item.group);
}
```
### Update Service Instance
```rust
use batata_client::Instance;
let naming_service = client.naming_service();
// Update instance weight and status
let instance = Instance::new("127.0.0.1", 8080)
.with_weight(2.0)
.with_enabled(false)
.with_metadata("version", "2.0.0");
naming_service.update_instance("my-service", "DEFAULT_GROUP", instance).await?;
```
### Local Cache for Failover
```rust
let client = BatataClient::builder()
.server_addr("localhost:8848")
.cache_dir("/tmp/nacos-cache")
.build()
.await?;
```
## Configuration Options
| `server_addr` | Nacos server address | `localhost:8848` |
| `server_addrs` | Multiple server addresses | - |
| `namespace` | Namespace ID | `public` |
| `app_name` | Application name | - |
| `timeout_ms` | Request timeout in milliseconds | `3000` |
| `retry_times` | Number of retry attempts | `3` |
| `username_password` | Username and password for auth | - |
| `access_key` | Access key and secret key for auth | - |
| `tls` | Enable TLS | `false` |
| `tls_config` | TLS configuration | - |
| `cache_dir` | Local cache directory | - |
## Examples
Run the examples:
```bash
# Configuration example
cargo run --example config_example
# Naming example
cargo run --example naming_example
```
## License
Apache License 2.0