From a3cf983b39998475d788ca9cba443d9057c6f490 Mon Sep 17 00:00:00 2001 From: what Date: Fri, 8 May 2026 11:37:34 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=B0=86=20WithAffinity=20?= =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8D=E4=B8=BA=20WithSticky=EF=BC=8C?= =?UTF-8?q?=E6=96=87=E4=BB=B6=20affinity.go=20=E2=86=92=20sticky.go?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++++----- affinity.go | 26 -------------------------- example/main.go | 6 +++--- sticky.go | 26 ++++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 34 deletions(-) delete mode 100644 affinity.go create mode 100644 sticky.go diff --git a/README.md b/README.md index b32bb04..b8cc3d6 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ def slow_compute(n: int) -> int: ## Session 亲和路由 -默认情况下,每次 `Invoke` 通过轮询分配 worker 进程。当多次调用需要共享同一 Python 进程的状态时,可以使用 Session 或 WithAffinity 将调用固定到同一进程。 +默认情况下,每次 `Invoke` 通过轮询分配 worker 进程。当多次调用需要共享同一 Python 进程的状态时,可以使用 Session 或 WithSticky 将调用固定到同一进程。 ### NewSession @@ -223,13 +223,13 @@ def step(session_id: str, delta: int) -> int: > `NewSession` 不拥有底层 pool 的生命周期,调用 `session.Close()` 是空操作,只需关闭原始 pool。 -### WithAffinity +### WithSticky -`WithAffinity` 将亲和键写入 ctx,相同 key 通过哈希稳定路由到同一 worker,无需持有 session 对象: +`WithSticky` 将亲和键写入 ctx,相同 key 通过哈希稳定路由到同一 worker,无需持有 session 对象: ```go // 每次调用前附加 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_b", ...) ``` @@ -262,7 +262,7 @@ gobridge.Invoke(ctx, sessB, "increment", 99) // worker 0: counter = 99(独立 ### 两种方式对比 -| | `NewSession` | `WithAffinity` | +| | `NewSession` | `WithSticky` | |---|---|---| | 路由方式 | 创建时轮询确定 worker | 按 key 哈希确定 worker | | 适用场景 | 显式会话管理 | 按自然键(用户 ID 等)路由 | diff --git a/affinity.go b/affinity.go deleted file mode 100644 index 91b4558..0000000 --- a/affinity.go +++ /dev/null @@ -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 -} diff --git a/example/main.go b/example/main.go index facdf10..08e9f66 100644 --- a/example/main.go +++ b/example/main.go @@ -235,13 +235,13 @@ func demoSession(ctx context.Context, script string) { r, _ = gobridge.Invoke[string](ctx, sessA, "global_get") fmt.Println("sessA get:", r) // worker 1 counter = 15(不受 sessB 影响) - // ── WithAffinity:相同 key 跨调用始终路由同一 worker ──────────────────── + // ── WithSticky:相同 key 跨调用始终路由同一 worker ──────────────────── fmt.Println() for i := range 4 { - affinityCtx := gobridge.WithAffinity(ctx, "sticky-key") + affinityCtx := gobridge.WithSticky(ctx, "sticky-key") msg, _ := gobridge.Invoke[string](affinityCtx, pool, "session_init", 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 ───────────────────────────── diff --git a/sticky.go b/sticky.go new file mode 100644 index 0000000..73d88db --- /dev/null +++ b/sticky.go @@ -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 +}