Files
backend/internal/pkg/circuitbreaker/circuitbreaker.go
lan 9a1851f023
Some checks failed
Build Backend / build (push) Successful in 1m56s
Build Backend / build-docker (push) Has been cancelled
refactor: cleanup unused code and simplify internal packages
This commit performs a significant cleanup of the codebase by removing unused functions, methods, and entire files across various internal modules. This reduces technical debt and simplifies the project structure.

Key changes include:
- **cache**: Removed unused cache key generators and metrics snapshots.
- **dto**: Removed redundant converter functions and segment creation helpers.
- **middleware**: Deleted the unused `logger.go` middleware and simplified `ratelimit.go` and `casbin.go`.
- **model**: Removed unused ID helpers, database closing functions, and batch decryption logic.
- **pkg**: Cleaned up unused utility functions in `circuitbreaker`, `crypto`, `cursor`, `hook`, and `utils`.
- **service**: Deleted `account_cleanup_service.go` and removed unused helper functions in `log_cleanup_service.go` and `sensitive_service.go`.
- **repository**: Removed unused private loading methods in `comment_repo.go`.
2026-05-14 02:24:30 +08:00

128 lines
2.3 KiB
Go

package circuitbreaker
import (
"sync"
"time"
"go.uber.org/zap"
)
type State int
const (
StateClosed State = iota
StateOpen
StateHalfOpen
)
type Config struct {
FailureThreshold int // 连续失败次数阈值,超过则开启熔断
SuccessThreshold int // 半开状态下连续成功次数,恢复到关闭
Timeout time.Duration // 熔断开启后等待多久进入半开
Name string // 熔断器名称,用于日志
}
type Breaker struct {
cfg Config
mu sync.Mutex
state State
fails int
wins int
openAt time.Time
}
func New(cfg Config) *Breaker {
return &Breaker{cfg: cfg}
}
func (b *Breaker) Allow() bool {
b.mu.Lock()
defer b.mu.Unlock()
switch b.state {
case StateClosed:
return true
case StateOpen:
if time.Since(b.openAt) > b.cfg.Timeout {
b.state = StateHalfOpen
b.wins = 0
return true
}
return false
case StateHalfOpen:
return true
default:
return true
}
}
func (b *Breaker) RecordSuccess() {
b.mu.Lock()
defer b.mu.Unlock()
b.fails = 0
if b.state == StateHalfOpen {
b.wins++
if b.wins >= b.cfg.SuccessThreshold {
b.state = StateClosed
b.wins = 0
zap.L().Info("circuit breaker closed",
zap.String("breaker", b.cfg.Name),
)
}
}
}
func (b *Breaker) RecordFailure() {
b.mu.Lock()
defer b.mu.Unlock()
b.fails++
if b.state == StateHalfOpen {
b.state = StateOpen
b.openAt = time.Now()
b.wins = 0
zap.L().Warn("circuit breaker re-opened from half-open",
zap.String("breaker", b.cfg.Name),
)
return
}
if b.fails >= b.cfg.FailureThreshold {
b.state = StateOpen
b.openAt = time.Now()
zap.L().Warn("circuit breaker opened",
zap.String("breaker", b.cfg.Name),
zap.Int("consecutive_failures", b.fails),
)
}
}
func (b *Breaker) State() State {
b.mu.Lock()
defer b.mu.Unlock()
return b.state
}
func (b *Breaker) Execute(fn func() error) error {
if !b.Allow() {
zap.L().Debug("circuit breaker rejected request",
zap.String("breaker", b.cfg.Name),
)
return &OpenError{Name: b.cfg.Name}
}
if err := fn(); err != nil {
b.RecordFailure()
return err
}
b.RecordSuccess()
return nil
}
type OpenError struct {
Name string
}
func (e *OpenError) Error() string {
return "circuit breaker '" + e.Name + "' is open"
}