refactor: 将 WithAffinity 重命名为 WithSticky,文件 affinity.go → sticky.go
This commit is contained in:
10
README.md
10
README.md
@@ -188,7 +188,7 @@ def slow_compute(n: int) -> int:
|
|||||||
|
|
||||||
## Session 亲和路由
|
## Session 亲和路由
|
||||||
|
|
||||||
默认情况下,每次 `Invoke` 通过轮询分配 worker 进程。当多次调用需要共享同一 Python 进程的状态时,可以使用 Session 或 WithAffinity 将调用固定到同一进程。
|
默认情况下,每次 `Invoke` 通过轮询分配 worker 进程。当多次调用需要共享同一 Python 进程的状态时,可以使用 Session 或 WithSticky 将调用固定到同一进程。
|
||||||
|
|
||||||
### NewSession
|
### NewSession
|
||||||
|
|
||||||
@@ -223,13 +223,13 @@ def step(session_id: str, delta: int) -> int:
|
|||||||
|
|
||||||
> `NewSession` 不拥有底层 pool 的生命周期,调用 `session.Close()` 是空操作,只需关闭原始 pool。
|
> `NewSession` 不拥有底层 pool 的生命周期,调用 `session.Close()` 是空操作,只需关闭原始 pool。
|
||||||
|
|
||||||
### WithAffinity
|
### WithSticky
|
||||||
|
|
||||||
`WithAffinity` 将亲和键写入 ctx,相同 key 通过哈希稳定路由到同一 worker,无需持有 session 对象:
|
`WithSticky` 将亲和键写入 ctx,相同 key 通过哈希稳定路由到同一 worker,无需持有 session 对象:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// 每次调用前附加 key,相同 key 始终走同一 worker
|
// 每次调用前附加 key,相同 key 始终走同一 worker
|
||||||
ctx = gobridge.WithAffinity(ctx, "user-42")
|
ctx = gobridge.WithSticky(ctx, "user-42")
|
||||||
gobridge.Invoke(ctx, pool, "method_a", ...)
|
gobridge.Invoke(ctx, pool, "method_a", ...)
|
||||||
gobridge.Invoke(ctx, pool, "method_b", ...)
|
gobridge.Invoke(ctx, pool, "method_b", ...)
|
||||||
```
|
```
|
||||||
@@ -262,7 +262,7 @@ gobridge.Invoke(ctx, sessB, "increment", 99) // worker 0: counter = 99(独立
|
|||||||
|
|
||||||
### 两种方式对比
|
### 两种方式对比
|
||||||
|
|
||||||
| | `NewSession` | `WithAffinity` |
|
| | `NewSession` | `WithSticky` |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 路由方式 | 创建时轮询确定 worker | 按 key 哈希确定 worker |
|
| 路由方式 | 创建时轮询确定 worker | 按 key 哈希确定 worker |
|
||||||
| 适用场景 | 显式会话管理 | 按自然键(用户 ID 等)路由 |
|
| 适用场景 | 显式会话管理 | 按自然键(用户 ID 等)路由 |
|
||||||
|
|||||||
26
affinity.go
26
affinity.go
@@ -1,26 +0,0 @@
|
|||||||
package gobridge
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"hash/fnv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type affinityKey struct{} // 用户提供的字符串 key,acquire 时 hash 到 worker 下标
|
|
||||||
|
|
||||||
// WithAffinity 将亲和键写入 ctx,相同 key 始终路由到同一 worker 进程。
|
|
||||||
//
|
|
||||||
// ctx = gobridge.WithAffinity(ctx, "user-42")
|
|
||||||
// gobridge.Invoke(ctx, pool, "method", ...) // 同 key 始终走同一进程
|
|
||||||
func WithAffinity(ctx context.Context, key string) context.Context {
|
|
||||||
return context.WithValue(ctx, affinityKey{}, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// workerIndexFor 根据 ctx 计算应使用的 worker 下标,无亲和键时返回 -1(交由轮询)。
|
|
||||||
func workerIndexFor(ctx context.Context, n int) int {
|
|
||||||
if key, ok := ctx.Value(affinityKey{}).(string); ok && key != "" {
|
|
||||||
h := fnv.New32a()
|
|
||||||
h.Write([]byte(key))
|
|
||||||
return int(h.Sum32()) % n
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
@@ -235,13 +235,13 @@ func demoSession(ctx context.Context, script string) {
|
|||||||
r, _ = gobridge.Invoke[string](ctx, sessA, "global_get")
|
r, _ = gobridge.Invoke[string](ctx, sessA, "global_get")
|
||||||
fmt.Println("sessA get:", r) // worker 1 counter = 15(不受 sessB 影响)
|
fmt.Println("sessA get:", r) // worker 1 counter = 15(不受 sessB 影响)
|
||||||
|
|
||||||
// ── WithAffinity:相同 key 跨调用始终路由同一 worker ────────────────────
|
// ── WithSticky:相同 key 跨调用始终路由同一 worker ────────────────────
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
for i := range 4 {
|
for i := range 4 {
|
||||||
affinityCtx := gobridge.WithAffinity(ctx, "sticky-key")
|
affinityCtx := gobridge.WithSticky(ctx, "sticky-key")
|
||||||
msg, _ := gobridge.Invoke[string](affinityCtx, pool, "session_init",
|
msg, _ := gobridge.Invoke[string](affinityCtx, pool, "session_init",
|
||||||
fmt.Sprintf("aff-%d", i), i)
|
fmt.Sprintf("aff-%d", i), i)
|
||||||
fmt.Printf("WithAffinity(sticky-key) #%d → %s\n", i, msg)
|
fmt.Printf("WithSticky(sticky-key) #%d → %s\n", i, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 对照组:不带亲和,轮询分配给两个 worker ─────────────────────────────
|
// ── 对照组:不带亲和,轮询分配给两个 worker ─────────────────────────────
|
||||||
|
|||||||
26
sticky.go
Normal file
26
sticky.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package gobridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"hash/fnv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stickyKey struct{} // 用户提供的字符串 key,acquire 时 hash 到 worker 下标
|
||||||
|
|
||||||
|
// WithSticky 将粘性键写入 ctx,相同 key 始终路由到同一 worker 进程。
|
||||||
|
//
|
||||||
|
// ctx = gobridge.WithSticky(ctx, "user-42")
|
||||||
|
// gobridge.Invoke(ctx, pool, "method", ...) // 同 key 始终走同一进程
|
||||||
|
func WithSticky(ctx context.Context, key string) context.Context {
|
||||||
|
return context.WithValue(ctx, stickyKey{}, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// workerIndexFor 根据 ctx 计算应使用的 worker 下标,无粘性键时返回 -1(交由轮询)。
|
||||||
|
func workerIndexFor(ctx context.Context, n int) int {
|
||||||
|
if key, ok := ctx.Value(stickyKey{}).(string); ok && key != "" {
|
||||||
|
h := fnv.New32a()
|
||||||
|
h.Write([]byte(key))
|
||||||
|
return int(h.Sum32()) % n
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user