refactor(discover): 对齐 Redis 类型化命令(by AI)
This commit is contained in:
parent
a1bc49adcc
commit
9c64bc2f80
@ -1,5 +1,10 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## [v1.5.8] - 2026-07-19
|
||||||
|
- **Redis API 对齐**: 服务注册、心跳、注销和节点拉取统一改用 `redis v1.5.11` 类型化命令,减少通用结果转换。
|
||||||
|
- **依赖升级**: 对齐 `cast`、`config`、`crypto`、`encoding`、`file`、`http`、`id`、`log`、`rand`、`safe`、`shell` 的当前 patch 版本。
|
||||||
|
- **文档与测试**: 补齐全局配置和负载均衡 API 说明,新增默认负载均衡器 Benchmark。
|
||||||
|
|
||||||
## [v1.5.7] - 2026-07-18
|
## [v1.5.7] - 2026-07-18
|
||||||
- **依赖校验修复**: 升级 `redis` 至 `v1.5.11`,并将间接依赖 `redigo` 固定为稳定版 `v1.9.3`,移除撤回版本。
|
- **依赖校验修复**: 升级 `redis` 至 `v1.5.11`,并将间接依赖 `redigo` 固定为稳定版 `v1.9.3`,移除撤回版本。
|
||||||
|
|
||||||
|
|||||||
22
Discover.go
22
Discover.go
@ -96,8 +96,8 @@ func Start(addr string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 注册节点
|
// 注册节点
|
||||||
if serverRedisPool.Do("HSET", Config.App, addr, Config.Weight).Error == nil {
|
if serverRedisPool.HSET(Config.App, addr, Config.Weight) {
|
||||||
serverRedisPool.Do("SETEX", Config.App+"_"+addr, 10, "1")
|
serverRedisPool.SETEX(Config.App+"_"+addr, 10, "1")
|
||||||
logInfo("registered")
|
logInfo("registered")
|
||||||
serverRedisPool.PUBLISH("CH_"+Config.App, fmt.Sprintf("%s %d", addr, Config.Weight))
|
serverRedisPool.PUBLISH("CH_"+Config.App, fmt.Sprintf("%s %d", addr, Config.Weight))
|
||||||
daemonRunning = true
|
daemonRunning = true
|
||||||
@ -132,14 +132,14 @@ func daemon() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if isServer && serverRedisPool != nil {
|
if isServer && serverRedisPool != nil {
|
||||||
if !serverRedisPool.Do("HEXISTS", Config.App, myAddr).Bool() {
|
if !serverRedisPool.HEXISTS(Config.App, myAddr) {
|
||||||
logInfo("lost app registered info, re-registering")
|
logInfo("lost app registered info, re-registering")
|
||||||
if serverRedisPool.Do("HSET", Config.App, myAddr, Config.Weight).Error == nil {
|
if serverRedisPool.HSET(Config.App, myAddr, Config.Weight) {
|
||||||
serverRedisPool.Do("SETEX", Config.App+"_"+myAddr, 10, "1")
|
serverRedisPool.SETEX(Config.App+"_"+myAddr, 10, "1")
|
||||||
serverRedisPool.PUBLISH("CH_"+Config.App, fmt.Sprintf("%s %d", myAddr, Config.Weight))
|
serverRedisPool.PUBLISH("CH_"+Config.App, fmt.Sprintf("%s %d", myAddr, Config.Weight))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
serverRedisPool.Do("SETEX", Config.App+"_"+myAddr, 10, "1")
|
serverRedisPool.SETEX(Config.App+"_"+myAddr, 10, "1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -201,8 +201,8 @@ func Stop() {
|
|||||||
if isServer {
|
if isServer {
|
||||||
daemonRunning = false
|
daemonRunning = false
|
||||||
if serverRedisPool != nil {
|
if serverRedisPool != nil {
|
||||||
serverRedisPool.Do("HDEL", Config.App, myAddr)
|
serverRedisPool.HDEL(Config.App, myAddr)
|
||||||
serverRedisPool.Do("DEL", Config.App+"_"+myAddr)
|
serverRedisPool.DEL(Config.App + "_" + myAddr)
|
||||||
serverRedisPool.PUBLISH("CH_"+Config.App, fmt.Sprintf("%s %d", myAddr, 0))
|
serverRedisPool.PUBLISH("CH_"+Config.App, fmt.Sprintf("%s %d", myAddr, 0))
|
||||||
}
|
}
|
||||||
isServer = false
|
isServer = false
|
||||||
@ -360,12 +360,12 @@ func fetchApp(app string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
results := pool.Do("HGETALL", app).ResultMap()
|
results := pool.HGETALL(app)
|
||||||
|
|
||||||
// 检查存活
|
// 检查存活
|
||||||
for addr := range results {
|
for addr := range results {
|
||||||
if !pool.Do("EXISTS", app+"_"+addr).Bool() {
|
if !pool.EXISTS(app + "_" + addr) {
|
||||||
pool.Do("HDEL", app, addr)
|
pool.HDEL(app, addr)
|
||||||
delete(results, addr)
|
delete(results, addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,9 +8,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"apigo.cc/go/discover"
|
"apigo.cc/go/discover"
|
||||||
"apigo.cc/go/redis"
|
"apigo.cc/go/redis"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDiscover(t *testing.T) {
|
func TestDiscover(t *testing.T) {
|
||||||
@ -138,3 +138,19 @@ func TestEasyStart(t *testing.T) {
|
|||||||
fmt.Printf("EasyStart: %s:%d\n", ip, port)
|
fmt.Printf("EasyStart: %s:%d\n", ip, port)
|
||||||
discover.Stop()
|
discover.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkDefaultLoadBalancer(b *testing.B) {
|
||||||
|
loadBalancer := &discover.DefaultLoadBalancer{}
|
||||||
|
appClient := &discover.AppClient{}
|
||||||
|
nodes := []*discover.NodeInfo{
|
||||||
|
{Addr: "127.0.0.1:18001", Weight: 100},
|
||||||
|
{Addr: "127.0.0.1:18002", Weight: 200},
|
||||||
|
{Addr: "127.0.0.1:18003", Weight: 300},
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
for b.Loop() {
|
||||||
|
loadBalancer.Next(appClient, nodes, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
10
README.md
10
README.md
@ -21,6 +21,13 @@ discover:
|
|||||||
|
|
||||||
## API 指南
|
## API 指南
|
||||||
|
|
||||||
|
### 全局配置
|
||||||
|
- `Config.Registry / App / Weight`: 注册中心、当前应用名与节点权重。
|
||||||
|
- `Config.Calls`: 被调用应用及调用配置。
|
||||||
|
- `Config.CallRetryTimes`: 单次调用的最大节点尝试次数。
|
||||||
|
- `Config.IpPrefix`: `EasyStart` 选择监听地址时使用的 IP 前缀。
|
||||||
|
- `RelayHeaders`: 跨服务调用时自动透传的 Header 列表。
|
||||||
|
|
||||||
### 初始化与启动
|
### 初始化与启动
|
||||||
- `Start(addr string) bool`: 启动服务发现,指定当前节点的外部访问地址。
|
- `Start(addr string) bool`: 启动服务发现,指定当前节点的外部访问地址。
|
||||||
- `EasyStart() (string, int)`: 自动监听可用端口并启动服务发现。返回 IP 和端口。
|
- `EasyStart() (string, int)`: 自动监听可用端口并启动服务发现。返回 IP 和端口。
|
||||||
@ -42,6 +49,9 @@ discover:
|
|||||||
|
|
||||||
### 负载均衡与路由
|
### 负载均衡与路由
|
||||||
- `SetLoadBalancer(lb LoadBalancer)`: 自定义全局负载均衡策略。
|
- `SetLoadBalancer(lb LoadBalancer)`: 自定义全局负载均衡策略。
|
||||||
|
- `LoadBalancer.Next`: 从可用节点中选择本次调用节点。
|
||||||
|
- `LoadBalancer.Response`: 请求完成后更新节点状态或统计信息。
|
||||||
|
- `DefaultLoadBalancer`: 内置的权重得分负载均衡实现。
|
||||||
- `SetRoute(route func(ac *AppClient, r *http.Request))`: 设置全局路由拦截规则。
|
- `SetRoute(route func(ac *AppClient, r *http.Request))`: 设置全局路由拦截规则。
|
||||||
- `Caller.DoWithNode / ManualDoWithNode`: 指定或返回实际调用节点。
|
- `Caller.DoWithNode / ManualDoWithNode`: 指定或返回实际调用节点。
|
||||||
|
|
||||||
|
|||||||
4
TEST.md
4
TEST.md
@ -5,6 +5,7 @@
|
|||||||
2. **实时同步**: 验证通过 Redis PUBLISH 更新节点信息后,客户端能实时感知并更新本地节点列表。
|
2. **实时同步**: 验证通过 Redis PUBLISH 更新节点信息后,客户端能实时感知并更新本地节点列表。
|
||||||
3. **故障剔除**: 验证当节点调用持续失败时,能自动从本地列表中剔除。
|
3. **故障剔除**: 验证当节点调用持续失败时,能自动从本地列表中剔除。
|
||||||
4. **环境变量配置**: 验证 `EasyStart` 结合环境变量的启动流程。
|
4. **环境变量配置**: 验证 `EasyStart` 结合环境变量的启动流程。
|
||||||
|
5. **负载均衡性能**: 验证默认负载均衡器在多节点集合上的选择性能与内存分配。
|
||||||
|
|
||||||
## 测试结果
|
## 测试结果
|
||||||
- **Unit Tests**: `go test -v ./...`
|
- **Unit Tests**: `go test -v ./...`
|
||||||
@ -12,6 +13,7 @@
|
|||||||
- `TestEasyStart`: PASS
|
- `TestEasyStart`: PASS
|
||||||
- **Module Graph**: `go list -m all` 通过,不再出现 checksum mismatch。
|
- **Module Graph**: `go list -m all` 通过,不再出现 checksum mismatch。
|
||||||
- **v1.5.7 Module Graph**: 使用 `redis v1.5.11` 与 `redigo v1.9.3`,不再包含撤回版本。
|
- **v1.5.7 Module Graph**: 使用 `redis v1.5.11` 与 `redigo v1.9.3`,不再包含撤回版本。
|
||||||
|
- **v1.5.8 Module Graph**: `apigo.cc/go/*` 基础依赖已对齐模块索引中的当前 patch 版本。
|
||||||
|
|
||||||
## Benchmark
|
## Benchmark
|
||||||
- 待补充(Discover 主要性能开销在负载均衡算法选择,单次选择耗时极低)。
|
- `BenchmarkDefaultLoadBalancer-16`: `32.35 ns/op`,`0 B/op`,`0 allocs/op`(Apple M3 Max)。
|
||||||
|
|||||||
22
go.mod
22
go.mod
@ -3,23 +3,23 @@ module apigo.cc/go/discover
|
|||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
apigo.cc/go/cast v1.5.4
|
apigo.cc/go/cast v1.5.5
|
||||||
apigo.cc/go/config v1.5.3
|
apigo.cc/go/config v1.5.4
|
||||||
apigo.cc/go/http v1.5.3
|
apigo.cc/go/http v1.5.4
|
||||||
apigo.cc/go/id v1.5.6
|
apigo.cc/go/id v1.5.7
|
||||||
apigo.cc/go/log v1.5.8
|
apigo.cc/go/log v1.5.9
|
||||||
apigo.cc/go/redis v1.5.11
|
apigo.cc/go/redis v1.5.11
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
apigo.cc/go/crypto v1.5.4 // indirect
|
apigo.cc/go/crypto v1.5.5 // indirect
|
||||||
apigo.cc/go/encoding v1.5.5 // indirect
|
apigo.cc/go/encoding v1.5.6 // indirect
|
||||||
apigo.cc/go/file v1.5.5 // indirect
|
apigo.cc/go/file v1.5.6 // indirect
|
||||||
apigo.cc/go/jsmod v1.5.3 // indirect
|
apigo.cc/go/jsmod v1.5.3 // indirect
|
||||||
apigo.cc/go/rand v1.5.3 // indirect
|
apigo.cc/go/rand v1.5.4 // indirect
|
||||||
apigo.cc/go/safe v1.5.2 // indirect
|
apigo.cc/go/safe v1.5.3 // indirect
|
||||||
apigo.cc/go/shell v1.5.4 // indirect
|
apigo.cc/go/shell v1.5.5 // indirect
|
||||||
github.com/gomodule/redigo v1.9.3 // indirect
|
github.com/gomodule/redigo v1.9.3 // indirect
|
||||||
golang.org/x/crypto v0.52.0 // indirect
|
golang.org/x/crypto v0.52.0 // indirect
|
||||||
golang.org/x/net v0.54.0 // indirect
|
golang.org/x/net v0.54.0 // indirect
|
||||||
|
|||||||
44
go.sum
44
go.sum
@ -1,29 +1,29 @@
|
|||||||
apigo.cc/go/cast v1.5.4 h1:OH5Vkrc1K19wuazf9VecjwwMwKDFgLQkI3UvwB4umcs=
|
apigo.cc/go/cast v1.5.5 h1:DMbfK3uPhPjRaXutj3StIZIkfjFIATSXfuAOeNOd4Fw=
|
||||||
apigo.cc/go/cast v1.5.4/go.mod h1:GMjjrYn93tWat1U409G7h1jR3ejfLLI7r0efBo9Sbd4=
|
apigo.cc/go/cast v1.5.5/go.mod h1:GMjjrYn93tWat1U409G7h1jR3ejfLLI7r0efBo9Sbd4=
|
||||||
apigo.cc/go/config v1.5.3 h1:peq1FM2xO+vzPHJf8Dwg3DXm8PtFQMfTFKQj6fpoG7A=
|
apigo.cc/go/config v1.5.4 h1:1c/OarGwbz3+6ikurE+a6LJLjtzXvGvbxw3HV/Nk54M=
|
||||||
apigo.cc/go/config v1.5.3/go.mod h1:ZiOAjWa1mQIzszaJZN+kO6YU4GXreng+NxkcK/TAkqQ=
|
apigo.cc/go/config v1.5.4/go.mod h1:oN+D2F8ETIyqKp+Yu8R4PRQlUoqR44o35jAHwLrrAq0=
|
||||||
apigo.cc/go/crypto v1.5.4 h1:7rFcF9Mns+7BIB13lystvLVsAeYapKsrD26KOrQq2Zk=
|
apigo.cc/go/crypto v1.5.5 h1:YQHumieqviNGMhwoxDtMuUdGWAQiGrkVhz9hZRHOhWs=
|
||||||
apigo.cc/go/crypto v1.5.4/go.mod h1:PheYKHEXmoEFI1AK5PpY1borQWcRlkkSaWncT3cWbhE=
|
apigo.cc/go/crypto v1.5.5/go.mod h1:z/FXt0HE7fSMJKF2MF7jY2fSjlHUYYaSffETJ7ZzJmE=
|
||||||
apigo.cc/go/encoding v1.5.5 h1:kduNLWQgtcQqHYobOuu1djbgg8LedkGOe8f18ZMfqzs=
|
apigo.cc/go/encoding v1.5.6 h1:v02swVfbFGidD4QcX2ktuHHbCjdSbOB85fhzAXay+7M=
|
||||||
apigo.cc/go/encoding v1.5.5/go.mod h1:Big9q1Zwy4071dXtnrQ3SJDzfa/G7/A60KE/5+M//P8=
|
apigo.cc/go/encoding v1.5.6/go.mod h1:Big9q1Zwy4071dXtnrQ3SJDzfa/G7/A60KE/5+M//P8=
|
||||||
apigo.cc/go/file v1.5.5 h1:/+HmDumLu6Qk2KuQL63M9lpgzHTDL+QJ8dStOl7e9gs=
|
apigo.cc/go/file v1.5.6 h1:Y7w3Tyu4e16VuED7rF2pzba+dzGE+hjDnBlIPVHIfzA=
|
||||||
apigo.cc/go/file v1.5.5/go.mod h1:xRVNhctvqOKeBemmcRW/BQfgkc3B+vT/UZVdSc7duUo=
|
apigo.cc/go/file v1.5.6/go.mod h1:9sdW4ylSOA0HWc8Yt8qdnmMf6nn5SUEmjoPKyXpYXIQ=
|
||||||
apigo.cc/go/http v1.5.3 h1:nvJh9bqPPcPRv6p8WEw7bJAd0UC+r2zvQA8/QioVLTQ=
|
apigo.cc/go/http v1.5.4 h1:Tm95WMsXyazFnaaPopdMXv1k9TiaPenLxDQ40WCrsEQ=
|
||||||
apigo.cc/go/http v1.5.3/go.mod h1:cFrPK61y9f1PrsNSJscZT/QVOgkT15o9OP7O8cuMb8Q=
|
apigo.cc/go/http v1.5.4/go.mod h1:cFrPK61y9f1PrsNSJscZT/QVOgkT15o9OP7O8cuMb8Q=
|
||||||
apigo.cc/go/id v1.5.6 h1:Z3PPp8H8FgNHMwHerxtT2GgstueDv4aCDnUUCoQZoFQ=
|
apigo.cc/go/id v1.5.7 h1:Y5Sx6sQBCAdYMCQPTjODZyGMMd1+WRWCy2dWHVq11XQ=
|
||||||
apigo.cc/go/id v1.5.6/go.mod h1:HcJK691qfBPzvQ/lt8bo/incKUFtG5vENX/rDEgzJh0=
|
apigo.cc/go/id v1.5.7/go.mod h1:fugudFBqfVNakfm91zZzuzU0P4PULzo6sylB8hRMqxA=
|
||||||
apigo.cc/go/jsmod v1.5.3 h1:S3W317bH0QV2NMeRO1E0v6ySIBOfMWYv/NuQJbvqKWU=
|
apigo.cc/go/jsmod v1.5.3 h1:S3W317bH0QV2NMeRO1E0v6ySIBOfMWYv/NuQJbvqKWU=
|
||||||
apigo.cc/go/jsmod v1.5.3/go.mod h1:bmyeZtOAP/j5am+YRnaiM89smysK24K7ebk0koFtsSw=
|
apigo.cc/go/jsmod v1.5.3/go.mod h1:bmyeZtOAP/j5am+YRnaiM89smysK24K7ebk0koFtsSw=
|
||||||
apigo.cc/go/log v1.5.8 h1:/IYtGPWhRjT3OayylDIphkWZIQbpLjqVeSnFEiD3Dy0=
|
apigo.cc/go/log v1.5.9 h1:g8JehZrpVJyesesv+JFig5szEELsO4UjShsjlEihCfc=
|
||||||
apigo.cc/go/log v1.5.8/go.mod h1:HfFPANMYxJx197SSTXB21Pgxcz/gGqPP8nlSErgd5WE=
|
apigo.cc/go/log v1.5.9/go.mod h1:b4f/UB5Kk7oiFtkXvG4GVilmHGygWgxELRAdEZrIyfs=
|
||||||
apigo.cc/go/rand v1.5.3 h1:O4bPIwyaOWEBCr0nL9A4G4qG48AqiGTCzfPeckm3Ius=
|
apigo.cc/go/rand v1.5.4 h1:eessFBsKQuoOYdzrStldOGw9f4HqbeO87X95bI4jIBQ=
|
||||||
apigo.cc/go/rand v1.5.3/go.mod h1:q1BTFkY/cXE229dDD5Q22lF7T0DoKPV6xAu+6bCrDH4=
|
apigo.cc/go/rand v1.5.4/go.mod h1:q1BTFkY/cXE229dDD5Q22lF7T0DoKPV6xAu+6bCrDH4=
|
||||||
apigo.cc/go/redis v1.5.11 h1:r/kiY3EUD+3YwTd+zMZaRD7eOQZEIvSJwAVnzG96zoQ=
|
apigo.cc/go/redis v1.5.11 h1:r/kiY3EUD+3YwTd+zMZaRD7eOQZEIvSJwAVnzG96zoQ=
|
||||||
apigo.cc/go/redis v1.5.11/go.mod h1:sCzxIWN+nNIRvR5ePaHsP19hjR52NPfXsq7L1c6A5ec=
|
apigo.cc/go/redis v1.5.11/go.mod h1:sCzxIWN+nNIRvR5ePaHsP19hjR52NPfXsq7L1c6A5ec=
|
||||||
apigo.cc/go/safe v1.5.2 h1:EnuEOW/SGwf/5A0nw9LnqfKJE071+TIc6ez8HI9R9Lg=
|
apigo.cc/go/safe v1.5.3 h1:9p/BmdlVWLbekpKByZIFC09Qn8Wdhik2eINiwunBxPs=
|
||||||
apigo.cc/go/safe v1.5.2/go.mod h1:2GqCCLLGex4OAhdET3iBWm1R+LIYtmTrvHP8W0iESSw=
|
apigo.cc/go/safe v1.5.3/go.mod h1:Ay8kEPL76DeXH4ifsVTc/3/sfGHlWLQjAp4vi7GA9AI=
|
||||||
apigo.cc/go/shell v1.5.4 h1:Kn6lP6I6d9U0hbyUjpKKFdFZ8RPo4vi4V6AYW8YFzrc=
|
apigo.cc/go/shell v1.5.5 h1:sf1QZiL7IHkoOLdrYX1qT4JWIlYZIAAYGJJXorjscB4=
|
||||||
apigo.cc/go/shell v1.5.4/go.mod h1:FdZWUrcXHGJXo725oSyHqAeFoX0E9yY3PDhrz9hujgY=
|
apigo.cc/go/shell v1.5.5/go.mod h1:FdZWUrcXHGJXo725oSyHqAeFoX0E9yY3PDhrz9hujgY=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gomodule/redigo v1.9.3 h1:dNPSXeXv6HCq2jdyWfjgmhBdqnR6PRO3m/G05nvpPC8=
|
github.com/gomodule/redigo v1.9.3 h1:dNPSXeXv6HCq2jdyWfjgmhBdqnR6PRO3m/G05nvpPC8=
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user