From 80f04c92ecd16719a74591b6be8a8b3a9a854fc0 Mon Sep 17 00:00:00 2001 From: luorijun Date: Wed, 13 May 2026 18:07:44 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AF=B7=E6=B1=82=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=B6=88=E6=81=AF=E4=B8=8A=E6=8A=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 63 ++++++---------------------------------------- web/middlewares.go | 18 ++++++------- 2 files changed, 16 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 2477dae..b9b325b 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,13 @@ ## TODO -### 接口并发问题 - -#### 找可用网关 & 找可用端点 -*(注:找端点依赖于网关状态,二者先后执行,但在并发控制上统一处理)* - -**下线并发问题** -* 提供指定网关的可重入读写锁A。 -* 整个提取期间锁定A直到提取结束。 -* 网关可以并发下线,但是不允许在锁A清空前进行删除或修改。 - -#### 找可用节点 - -##### 找本地 -**并发筛选问题** -多个提取请求筛选到同一个节点,只有一个请求可以占用该节点,其他请求会直接失败。 - -**处理方案:** -* 提供占用锁,持续到提交配置后。 -* 筛选出节点后,锁定该节点,如果锁定失败说明该节点已被占用(包括云端节点)。 -* 如果被占用则等待占用结束后重新筛选(如果在结束前就重新筛选,可能还是会筛选到同样的节点,再次导致失败)。 -* 提取时可能要求多个节点,因此锁定节点时,需要一个 lua 脚本同步锁定同一批节点。 - -##### 找云端 -**重复筛选问题** -云端接口不会自动过滤已连接的节点,有可能筛选到已经连接甚至配置的节点。 - -**处理(缓解)方案:** -* 优先筛选今日未分配的节点,如果没有可用节点,再分配已用节点,这个方案暂时缓解问题。 - -#### 整理配置信息 - -* 该环节**不会有并发问题**。 - -#### 开通通道 -*(注:包含以下三个独立操作,主要关注其执行的原子性与最终一致性保证)* - -##### 提交异步关闭任务 -**后续失败问题:** -* 不考虑回滚,执行时需要考虑后续数据不全的情况。 -* 提交需要 `proxy` 和 `batch` 参数,端口取用是独占的,因此在归还端口前,一定不会有其他连接使用端口。任务信息中需要包含足够的信息以在没有数据库信息时解除配置。 -* 解除连接与归还端口全部成功才算成功。 - -##### 提交配置到云端 -* 该环节**不会有并发问题**。 - -##### 保存到数据库 -**一致性处理:** -* 保存失败后,只会存在孤立占用。异步任务会自动重试,能够实现最终一致性。 - ---- - -- 并发扣减问题 -- 代理选择通过查 redis 实现 - -- 重新考虑取用流程设计,是否可以分离端口归还与通道断开。端口归还后通道即使没有断开,未来也会被其他请求占用,能够实现最终一致性 +提取代理: +- 网关修改问题 + - 在提取流程中如果网关被修改,有可能导致数据不一致 + - 提供读写锁,在提取流程中获取读锁,在修改网关时获取写锁 +- 端口悬空问题 + - 在提取流程中如果发生异常,可能导致端口被占用但通道信息没有被写入数据库,配置以及端口将无法解除 + - 提交删除任务时,带上配置信息,无需再查数据库且接口总是会被正确清理 + - 异常情况下,将只清理网关端口,而无法解除节点连接,这个问题需要额外处理 --- diff --git a/web/middlewares.go b/web/middlewares.go index 6bc5eaf..1724424 100644 --- a/web/middlewares.go +++ b/web/middlewares.go @@ -22,6 +22,9 @@ func ApplyMiddlewares(app *fiber.App) { EnableStackTrace: true, })) + // metric + app.Use(otelfiber.Middleware()) + // logger app.Use(logger.New(logger.Config{ Next: func(c *fiber.Ctx) bool { @@ -29,8 +32,7 @@ func ApplyMiddlewares(app *fiber.App) { }, })) - // metric - app.Use(otelfiber.Middleware()) + // 补充 otel span attr app.Use(func(c *fiber.Ctx) error { err := c.Next() @@ -39,16 +41,12 @@ func ApplyMiddlewares(app *fiber.App) { return err } - status := c.Response().StatusCode() - body := []byte{} - if status < 200 || status >= 300 { - body = c.Response().Body() - if len(body) > 1024 { - body = body[:1024] - } + str := "" + if err != nil { + str = err.Error() } - span.SetAttributes(attribute.String("http.response.error", string(body))) + span.SetAttributes(attribute.String("http.response.error", str)) return err })