Skip to content

refactor(circuitBreaker): 重构熔断器初始化逻辑和探活机制 #112

Merged
Penryn merged 3 commits intozjutjh:mainfrom
BURIBURI-ZAEMON1:main
Mar 2, 2026
Merged

refactor(circuitBreaker): 重构熔断器初始化逻辑和探活机制 #112
Penryn merged 3 commits intozjutjh:mainfrom
BURIBURI-ZAEMON1:main

Conversation

@BURIBURI-ZAEMON1
Copy link
Contributor

No description provided.

将init函数重命名为Init并显式调用,优化探活逻辑为并发执行,提高效率
Copy link
Collaborator

@cbluebird cbluebird left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

该 PR 旨在重构熔断器(circuitBreaker)的初始化流程,并调整探活(liveness probe)机制,使探活在启动时与定时周期内执行,并尝试并发探测待恢复的 API。

Changes:

  • main 中启动探活协程,并在探活开始前调用 circuitBreaker.Init()
  • LiveNessProbe 增加并发探测逻辑 probeTogether(),并在 Start() 中复用
  • 调整 circuitBreaker.Init():初始化时将 API 放入 Probe 而不是直接加入 LB

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
main.go 启动探活 goroutine,并新增一次 circuitBreaker.Init() 调用
app/utils/circuitBreaker/liveNessProbe.go 新增并发探活 probeTogether(),ticker 到点后调用
app/utils/circuitBreaker/circuitBreaker.go Init 时改为把 API 加入 Probe 并初始化快照,不再直接 seed 到 LB

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Probe.Add(api, funnelApi.ZF)

snapShot.Store(oauthKey, oauthSnap)
snapShot.Store(zfKey, zfSnap)
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Init() no longer adds configured APIs into the load balancer (previously via lb.Add(...)), so CB.LB starts empty and GetApi() will return NoApiAvailable until the liveness probe succeeds and calls CB.LB.Add. If this behavior change is intentional, consider blocking startup (or delaying server listen) until at least one API is marked healthy, or seed LB with all configured APIs and let probing remove unhealthy ones to avoid initial request failures.

Suggested change
snapShot.Store(zfKey, zfSnap)
snapShot.Store(zfKey, zfSnap)
lb.Add(api, funnelApi.Oauth)
lb.Add(api, funnelApi.ZF)

Copilot uses AI. Check for mistakes.
Comment on lines 55 to 66
go func() {
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
zap.L().Fatal("Server Error Occurred", zap.Error(err))
}
}()

wg.Add(1)
go func() {
circuitBreaker.Init()
defer wg.Done()
circuitBreaker.Probe.Start(ctx)
}()
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the new behavior where CB.LB starts empty until probes add healthy APIs, the server begins accepting requests (ListenAndServe) before Probe.Start(ctx) has had a chance to populate the load balancer. This creates a startup window where requests may consistently fail with NoApiAvailable. Consider starting the probe (and waiting for at least one healthy API or a timeout) before calling ListenAndServe, or keep LB seeded at init time.

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +67
func (l *LiveNessProbe) probeTogether() {
var wg sync.WaitGroup
for apiKey, loginType := range l.ApiMap {
wg.Add(1)
go func(apikey string, lt funnelApi.LoginType) {
defer wg.Done()
api := strings.TrimSuffix(apikey, string(lt))
if err := liveNess(l.User, api, lt); err == nil {
CB.LB.Add(api, lt)
CB.Success(api, lt)
l.Remove(apikey)
}
}(apiKey, loginType)
}
wg.Wait()
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probeTogether() iterates over l.ApiMap without holding the mutex while goroutines concurrently call l.Remove() (which deletes from the same map). This can trigger fatal error: concurrent map iteration and map write at runtime. Fix by taking the lock and copying keys/values to a slice (or using a sync.Map) before launching goroutines, and only mutate ApiMap under the lock (e.g., remove keys after probing via a channel processed by a single goroutine).

Copilot uses AI. Check for mistakes.
Comment on lines 61 to 66
wg.Add(1)
go func() {
circuitBreaker.Init()
defer wg.Done()
circuitBreaker.Probe.Start(ctx)
}()
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

circuitBreaker.Init() is now called twice (once during startup and again inside the probe goroutine). Since Init() reassigns the global CB and Probe, this can reset circuit-breaker state while the server is already running and may race with request handling. Initialize once (ideally before starting any goroutines) and only start Probe.Start(ctx) here.

Copilot uses AI. Check for mistakes.
@Penryn Penryn merged commit 227d2aa into zjutjh:main Mar 2, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants