# Discovery 手册

`rs-zero` 的 `discovery` feature 提供服务发现基础模型和 adapter 边界。

## 核心能力

- `ServiceInstance`：服务实例、endpoint、metadata、weight、health。
- discovery/registry trait：支持 discover、register、deregister 和 watch/refresh 语义。
- static、memory、DNS adapter。
- round-robin selector。
- etcd/Kubernetes adapter 分别通过 `discovery-etcd` 和 `discovery-kube` feature 开启；`discovery-etcd` 使用真实 etcd v3 client，`discovery-kube` 使用真实 Kubernetes EndpointSlice watcher。

## etcd registry

启用 `discovery-etcd` 后，`EtcdRegistry::connect` 会连接 etcd，并支持：

- `register`：写入 `/prefix/{service}/{id}`，value 为 `ServiceInstance` JSON。
- lease keep-alive：注册时创建 lease，后台续期，`deregister` 停止续期并 revoke lease。
- `discover`：按 service prefix 查询，只返回 healthy instance；空结果返回 `NoInstances`。
- `update_health`：更新已注册实例的 healthy 字段并保留 lease。
- `watch_service`：监听 service prefix 的 put/delete 事件；stream 失败后按 `BackoffConfig` 重连。

最小配置示例：

```rust
use rs_zero::discovery_etcd::{BackoffConfig, EtcdDiscoveryConfig, EtcdRegistry};
use std::time::Duration;

# async fn build_registry() -> Result<EtcdRegistry, Box<dyn std::error::Error>> {
let registry = EtcdRegistry::connect(EtcdDiscoveryConfig {
    endpoints: vec!["http://127.0.0.1:2379".to_string()],
    prefix: "/rs-zero".to_string(),
    lease_ttl: 30,
    reconnect_interval: Duration::from_secs(1),
    connect_timeout: Duration::from_secs(3),
    operation_timeout: Duration::from_secs(3),
    keep_alive_interval: Duration::from_secs(5),
    keep_alive_timeout: Duration::from_secs(3),
    watch_backoff: BackoffConfig::default(),
    auth: None,
})
.await?;
# Ok(registry)
# }
```

## Kubernetes discovery

启用 `discovery-kube` 后，`KubeDiscovery::connect` 会从 kubeconfig 或 in-cluster 环境创建 Kubernetes client，并监听指定 namespace 的 `discovery.k8s.io/v1 EndpointSlice`。

主路径支持：

- `connect` / `from_client`：启动真实 EndpointSlice watcher；`from_snapshot` 仅保留给 fixture 和离线测试。
- 初始化同步：处理 `Init` / `InitApply` / `InitDone`，首次同步完成前返回可诊断错误。
- 增删更新：处理 `Apply` / `Delete`，本地缓存按 EndpointSlice 贡献更新，删除后不会保留旧实例。
- 健康过滤：只返回 ready 且非 terminating endpoint。
- 配置：`context`、`namespace`、`service_name`、`label_selector`、`field_selector`、`port_name`、`watch_timeout`、`page_size`、`sync_timeout` 和 metadata。

最小配置示例：

```rust
use rs_zero::discovery_kube::{KubeDiscovery, KubeDiscoveryConfig};
use std::time::Duration;

# async fn build_discovery() -> Result<KubeDiscovery, Box<dyn std::error::Error>> {
let discovery = KubeDiscovery::connect(KubeDiscoveryConfig {
    context: Some("docker-desktop".to_string()),
    namespace: "default".to_string(),
    service_name: Some("api".to_string()),
    port_name: Some("http".to_string()),
    sync_timeout: Duration::from_secs(10),
    ..KubeDiscoveryConfig::default()
})
.await?;
# Ok(discovery)
# }
```

## 使用建议

- 本地开发优先使用 static 或 memory adapter。
- 生产注册中心接入前先确认 health、metadata、权重、lease TTL、keep-alive、timeout、Kubernetes RBAC 和 watch 重连策略。
- 外部服务测试默认 ignored，不应纳入默认 CI 主路径。

## 相关示例与测试

- `examples/discovery-hello/README.md`
- `examples/production-adapters/etcd-discovery.rs`
- `cargo test --test discovery_integration`
- `cargo test --no-default-features --features discovery-etcd --test discovery_etcd_codec`
- `RS_ZERO_ETCD_ENDPOINT=http://127.0.0.1:2379 cargo test --no-default-features --features discovery-etcd --test discovery_etcd_external -- --ignored`
- `cargo test --no-default-features --features discovery-kube --test discovery_kube_mapper`
- `RS_ZERO_KUBE_EXTERNAL=1 cargo test --no-default-features --features discovery-kube --test discovery_kube_external -- --ignored`
