124 lines
4.5 KiB
Markdown
124 lines
4.5 KiB
Markdown
# @go/discover
|
||
|
||
> **Maintainer Statement:** 本项目完全由 AI 维护。任何改动均遵循代码质量与性能的最佳实践。
|
||
|
||
`@go/discover` 是一个**无状态、参数驱动**的极简服务发现与负载均衡组件。它基于 Redis 实现,专注于消除微服务调用间的摩擦,并原生支持 Header 链路透传。
|
||
|
||
## 🎯 设计哲学
|
||
|
||
- **纯粹无状态 (Stateless)**:模块自身不读取配置文件,不依赖任何特定框架的上下文。它是一个纯粹的函数式库,配置的加载权力完全交给顶层(如 `@go/service`)。
|
||
- **面向对象隔离**:支持多实例共存。你可以在同一个进程中同时连接不同的注册中心,实现复杂的网关分发。
|
||
- **内存安全与高性能**:敏感令牌 (Token) 受 `@go/safe` 内存保护;调用耗时由 `@go/timer` 高性能打点引擎追踪;网络层支持 H2C (HTTP/2 Cleartext)。
|
||
|
||
## 📦 安装
|
||
|
||
```bash
|
||
go get apigo.cc/go/discover
|
||
```
|
||
|
||
## 💡 快速入门 (Quick Start)
|
||
|
||
### 1. 服务端模式 (Server)
|
||
|
||
如果你是一个需要被别人发现的服务,使用 `Start`。
|
||
|
||
```go
|
||
import "apigo.cc/go/discover"
|
||
|
||
// 可选配置
|
||
conf := discover.Config{
|
||
Weight: 100,
|
||
Calls: map[string]string{
|
||
"auth-service": "1s:my-token", // 依赖外部 auth 服务,超时1s,附加令牌
|
||
},
|
||
}
|
||
|
||
// 启动并注册 (注册中心地址, 我的服务名, 我的可访问地址, 可选配置)
|
||
d := discover.Start("redis://127.0.0.1:6379/15", "user-service", "198.18.0.1:8080", conf)
|
||
defer d.Stop() // 优雅注销
|
||
```
|
||
|
||
### 2. 纯客户端模式 (Client)
|
||
|
||
如果你只是一个脚本或 API 网关,只需要调用别人,使用 `Open`。
|
||
|
||
```go
|
||
// 启动纯客户端 (注册中心地址, 可选配置)
|
||
d := discover.Open("redis://127.0.0.1:6379/15")
|
||
```
|
||
|
||
---
|
||
|
||
## 🛠 API Reference (实例方法)
|
||
|
||
`Start` 和 `Open` 均会返回一个 `*discover.Discoverer` 实例,所有的业务调用都应通过此实例进行。
|
||
|
||
### 1. 泛型快捷调用 (Stateless Data Fetching)
|
||
自动解析 JSON 响应到结构体,适用于绝大多数简单的 RPC 场景。
|
||
|
||
```go
|
||
type User struct { ID int }
|
||
|
||
// Get[T] / Post[T] / Put[T] / Delete[T]
|
||
user, err := discover.Get[User](d, "user-service", "/api/info")
|
||
```
|
||
|
||
### 2. 链路透传调用 (Stateful Context Propagation)
|
||
在微服务网状网络中,我们需要将前置网关的 `TraceID`, `UserID`, `X-Real-IP` 等信息继续向后透传。使用 `From(r)` 包装器可以极其优雅地实现。
|
||
|
||
```go
|
||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
||
// d 假设是你的全局发现实例
|
||
// From 会自动提取 r 中的上下文 Header 并发送给 user-service
|
||
res := d.From(r).Post("user-service", "/create", reqData)
|
||
|
||
// 或者结合泛型
|
||
user, err := gohttp.To[User](d.From(r).Get("user-service", "/info"))
|
||
}
|
||
```
|
||
|
||
### 3. 直接获取结果 (Raw Result)
|
||
兼容 `@go/http` 模块的 `*Result` 对象,便于处理非 JSON 响应或检查原始 HTTP 状态码。
|
||
|
||
```go
|
||
res := d.Do("GET", "auth-service", "/verify", nil)
|
||
if res.Error == nil {
|
||
fmt.Println(res.String())
|
||
// 也可以: res.Save("file.txt")
|
||
}
|
||
```
|
||
|
||
### 4. WebSocket 支持
|
||
无缝升级为 WebSocket 连接,支持跨服务的长连接代理。
|
||
|
||
```go
|
||
// 返回 *websocket.Conn
|
||
ws := d.Open("chat-service", "/stream")
|
||
```
|
||
|
||
### 5. 动态路由与负载管控
|
||
- `func (d *Discoverer) AddExternalApp(app, callConf string) bool`: 在运行时动态添加一个新的下游依赖服务。
|
||
- `func (d *Discoverer) SetNode(app, addr string, weight int)`: 手动硬编码设置节点信息(常用于本地 Mock 测试或灰度引流)。
|
||
- `func (d *Discoverer) SetLoadBalancer(lb LoadBalancer)`: 自定义复杂负载均衡算法。
|
||
- `func (d *Discoverer) SetRoute(route func(ac *AppClient, r *http.Request))`: 设置请求的拦截/重写路由规则。
|
||
|
||
---
|
||
|
||
## ⚙️ Config 配置结构详解
|
||
|
||
作为 `Start` 和 `Open` 的最后一个可选参数:
|
||
|
||
```go
|
||
type Config struct {
|
||
Weight int // 权重,默认为 100
|
||
Calls map[string]string // 提前声明调用的下游服务及其参数 (格式:超时时间:Token:H2C开关)
|
||
CallRetryTimes int // 请求单个下游节点的最大失败重试次数 (默认 10)
|
||
IpPrefix string // 在 EasyStart 模式下过滤并绑定指定网段的 IP
|
||
Logger *log.Logger // 自定义日志接口
|
||
}
|
||
```
|
||
|
||
## 环境变量 (EasyStart 模式)
|
||
仅在使用 `discover.EasyStart()`(自动探测本地 IP 和随机端口启动)时,支持以下环境变量:
|
||
- `DISCOVER_LISTEN`: 本地试探监听的端口。
|