【Go语言学习系列53】高可用系统设计

📚 原创系列: “Go语言学习系列”

🔄 转载说明: 本文最初发布于"Gopher部落"微信公众号,经原作者授权转载。

🔗 关注原创: 欢迎扫描文末二维码,关注"Gopher部落"微信公众号获取第一手Go技术文章。

📑 Go语言学习系列导航

本文是【Go语言学习系列】的第53篇,当前位于第四阶段(专业篇)

🚀 第四阶段:专业篇
  1. 性能优化(一):编写高性能Go代码
  2. 性能优化(二):profiling深入
  3. 性能优化(三):并发调优
  4. 代码质量与最佳实践
  5. 设计模式在Go中的应用(一)
  6. 设计模式在Go中的应用(二)
  7. 云原生Go应用开发
  8. 分布式系统基础
  9. 高可用系统设计 👈 当前位置
  10. 安全编程实践
  11. Go汇编基础
  12. 第四阶段项目实战:高性能API网关

📚 查看完整Go语言学习系列导航

引言

在现代软件系统中,高可用性已经成为一项基本要求。无论是面向消费者的应用程序还是企业级系统,用户都期望服务能够24x7不间断运行。一个系统的停机可能导致收入损失、用户流失甚至声誉受损。因此,设计和实现高可用系统已成为软件工程师必备的技能。

本文将探讨高可用系统设计的核心原则和实践方法,包括如何度量可用性、分析故障模式、设计冗余策略以及使用Go语言实现各种高可用性模式。我们将从理论到实践,系统地介绍构建高可用Go服务所需的知识和技术。

高可用系统的核心概念

高可用系统(High Availability System)是指在约定的时间内,系统能够正常运行的时间占比很高的系统。简单来说,高可用系统就是一个能够持续提供服务、很少发生中断的系统。

2.1 可用性的定义

从技术角度看,可用性(Availability)通常定义为:

可用性 = 系统正常运行时间 / 系统总运行时间

例如,一个系统在一年中有99.9%的时间可以正常工作,则其可用性为"三个9"(99.9%)。这意味着该系统在一年中可能有大约8.76小时(365天 × 24小时 × 0.1%)的不可用时间。

2.2 高可用系统的核心特性

高可用系统通常具有以下核心特性:

  1. 无单点故障(No Single Point of Failure):系统中的任何组件故障都不会导致整个系统不可用。
  2. 可靠性(Reliability):系统在指定的时间内,在给定的环境条件下,执行预期功能的能力。
  3. 弹性(Resilience):系统在面对故障时能够优雅降级,并从故障中快速恢复的能力。
  4. 可扩展性(Scalability):系统处理不断增长的负载和数据量的能力。
  5. 可维护性(Maintainability):系统易于维护、更新和修复的能力。

2.3 高可用系统的挑战

设计和实现高可用系统面临着多方面的挑战:

  1. 分布式系统的复杂性:分布式环境带来的网络不可靠、时钟不同步等问题。
  2. 状态管理:在多个实例间保持一致的状态信息。
  3. 故障检测:准确及时地检测各种故障情况。
  4. 自动恢复:从故障中快速自动恢复,减少人工干预。
  5. 测试的困难性:全面测试各种故障场景和恢复机制的复杂性。

2.4 Go语言在高可用系统中的优势

Go语言作为一种现代化的编程语言,在构建高可用系统方面具有显著优势:

  1. 内置并发支持:goroutine和channel使得并发编程变得简单高效。
  2. 出色的网络编程能力:标准库提供了强大的网络编程支持。
  3. 垃圾回收:自动内存管理减少了内存泄漏和相关崩溃的风险。
  4. 快速编译和部署:加速开发和修复周期。
  5. 跨平台:支持多种平台,便于部署到不同环境。
  6. 丰富的生态系统:大量高质量的库和框架用于构建分布式和高可用系统。

下面是一个简单的Go程序,展示了如何使用goroutine和channel来处理并发请求:

package main

import (
    "fmt"
    "log"
    "net/http"
    "sync"
    "time"
)

// RequestHandler 处理请求,带有超时和重试机制
func RequestHandler(w http.ResponseWriter, r *http.Request) {
   
   
    results := make(chan string, 3) // 缓冲通道,避免goroutine泄漏
    
    // 尝试从多个后端服务获取数据,取最快的响应
    var wg sync.WaitGroup
    
    for i := 1; i <= 3; i++ {
   
   
        wg.Add(1)
        go func(serviceID int) {
   
   
            defer wg.Done()
            
            // 模拟向后端服务发送请求,带有超时控制
            start := time.Now()
            
            // 创建一个context,带有1秒超时
            ctx, cancel := context.WithTimeout(r.Context(), 1*time.Second)
            defer cancel()
            
            // 模拟调用后端服务
            select {
   
   
            case <-time.After(time.Duration(serviceID*500) * time.Millisecond): // 模拟不同响应时间
                // 成功获取数据,将结果放入通道
                select {
   
   
                case results <- fmt.Sprintf("Data from service %d (took: %v)", serviceID, time.Since(start)):
                    // 成功发送到结果通道
                default:
                    // 结果通道已满,其他服务已经返回了更快的结果
                }
            case <-ctx.Done():
                // 请求超时
                log.Printf("Request to service %d timed out", serviceID)
            }
        }(i)
    }
    
    // 启动一个goroutine来关闭结果通道,当所有请求完成后
    go func() {
   
   
        wg.Wait()
        close(results)
    }()
    
    // 取得第一个可用结果
    select {
   
   
    case result, ok := <-results:
        if ok {
   
   
            fmt.Fprintf(w, "Success: %s\n", result)
        } else {
   
   
            // 所有请求都失败了
            w.WriteHeader(http.StatusServiceUnavailable)
            fmt.Fprintf(w, "All backend services failed to respond in time\n")
        }
    case <-time.After(2 * time.Second): // 整体超时控制
        w.WriteHeader(http.StatusGatewayTimeout)
        fmt.Fprintf(w, "Request timed out\n")
    }
}

func main() {
   
   
    http.HandleFunc("/api", RequestHandler)
    
    server := &http.Server{
   
   
        Addr:         ":8080",
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  120 * time.Second,
    }
    
    log.Println("Starting high availability server on :8080")
    if err := server.ListenAndServe(); err != nil {
   
   
        log.Fatalf("Server failed: %v", err)
    }
}

这个示例展示了高可用服务设计的几个关键原则:

  • 并行请求多个后端服务,采取最快的响应(提高可用性)
  • 对每个后端请求设置超时(避免慢服务拖慢整体响应)
  • 对整体请求设置超时(确保客户端快速得到响应)
  • 使用缓冲通道和goroutine优雅处理并发(避免资源泄漏)

在后续章节,我们将深入探讨高可用系统的各个方面,并提供更多实际的Go语言实现。

可用性度量与SLA

在高可用系统设计中,明确的可用性度量标准和服务级别协议(SLA)对于设定目标、评估系统性能以及管理用户期望至关重要。

3.1 可用性的度量方式

可用性通常以"几个9"来表示:

可用性级别 每年停机时间 每月停机时间 每周停机时间 每天停机时间
90% (“一个9”) 36.5天 72小时 16.8小时 2.4小时
99% (“两个9”) 3.65天 7.2小时 1.68小时 14.4分钟
99.9% (“三个9”) 8.76小时 43.8分钟 10.1分钟 1.44分钟
99.99% (“四个9”) 52.56分钟 4.38分钟 1.01分钟 8.64秒
99.999% (“五个9”) 5.26分钟 26.3秒 6.05秒 0.86秒
99.9999% (“六个9”) 31.5秒 2.63秒 0.605秒 0.086秒

3.2 SLA(服务级别协议)

SLA是服务提供商与客户间的正式承诺,规定了服务的性能和可用性标准。一个典型的SLA包含以下要素:

  1. 服务可用性目标:例如"99.95%的月度可用性"
  2. 服务性能指标:如响应时间、吞吐量等
  3. 故障定义:什么情况构成服务不可用
  4. 测量方法:如何计算和验证可用性
  5. 补偿措施:当未达到承诺指标时的赔偿政策

3.3 常见可用性指标

除了纯粹的时间百分比外,还有其他指标用于评估系统的可用性:

  1. MTBF(平均故障间隔时间):两次故障之间的平均时间,计算公式:

    MTBF = 总运行时间 / 故障次数
    
  2. MTTR(平均修复时间):从故障发生到恢复服务的平均时间,计算公式:

    MTTR = 总修复时间 / 故障次数
    
  3. 可用性计算:使用MTBF和MTTR计算可用性:

    可用性 = MTBF / (MTBF + MTTR)
    
  4. 错误预算:规定在特定时间段内允许的停机时间,帮助团队在可靠性和创新间取得平衡。

3.4 Go语言中的可用性监控

使用Go语言可以构建可用性监控系统,以下是一个简单的示例,展示如何创建服务健康检查和可用性统计:

package main

import (
    "fmt"
    "log"
    "net/http"
    "sync"
    "time"
)

// ServiceMonitor 监控服务的可用性
type ServiceMonitor struct {
   
   
    serviceName     string
    checkInterval   time.Duration
    url             string
    timeout         time.Duration
    totalChecks     int64
    successfulChecks int64
    startTime       time.Time
    downtime        time.Duration
    lastDownTime    time.Time
    isDown          bool
    mu              sync.Mutex
}

// NewServiceMonitor 创建一个新的服务监控器
func NewServiceMonitor(name string, url string, interval time.Duration, timeout time.Duration) *ServiceMonitor {
   
   
    return &ServiceMonitor{
   
   
        serviceName:   name,
        url:           url,
        checkInterval: interval,
        timeout:       timeout,
        startTime:     time.Now(),
    }
}

// Start 开始监控服务
func (sm *ServiceMonitor) Start() {
   
   
    ticker := time.NewTicker(sm.checkInterval)
    go func() {
   
   
        for range ticker.C {
   
   
            sm.checkService()
        }
    }()
}

// checkService 执行一次服务检查
func (sm *ServiceMonitor) checkService() {
   
   
    sm.mu.Lock()
    sm.totalChecks++
    sm.mu.Unlock()
    
    client := http.Client{
   
   
        Timeout: sm.timeout,
    }
    
    start := time.Now()
    resp, err := client.Get(sm.url)
    
    sm.mu.Lock()
    defer sm.mu.Unlock()
    
    if err != nil || resp.StatusCode >= 500 {
   
   
        // 服务不可用
        if !sm.isDown {
   
   
            sm.isDown = true
            sm.lastDownTime = start
            log.Printf("Service %s is DOWN!", sm.serviceName)
        }
    } else {
   
   
        // 服务可用
        sm.successfulChecks++
        if resp.Body != nil {
   
   
            resp.Body.Close()
        }
        
        if sm.isDown {
   
   
            // 服务刚恢复
            downDuration := time.Since(sm.lastDownTime)
            sm.downtime += downDuration
            sm.isDown = false
            log.Printf("Service %s is back UP after %v", sm.serviceName, downDuration)
        }
    }
}

// GetAvailability 计算服务可用性百分比
func (sm *ServiceMonitor) GetAvailability() float64 {
   
   
    sm.mu.Lock()
    defer sm.mu.Unlock()
    
    totalTime := time.Since(sm.startTime)
    availableTime := totalTime - sm.downtime
    if sm.isDown {
   
   
        // 如果当前不可用,计算额外的停机时间
        availableTime -= time.Since(sm.lastDownTime)
    }
    
    return float64(availableTime) / float64(totalTime) * 100
}

// GetStats 获取监控统计信息
func (sm *ServiceMonitor) GetStats() map[string]interface{
   
   } {
   
   
    sm.mu.Lock()
    defer sm.mu.Unlock()
    
    totalTime := time.Since(sm.startTime)
    currentDowntime := sm.downtime
    if sm.isDown {
   
   
        currentDowntime += time.Since(sm.lastDownTime)
    }
    
    availability := float64(totalTime-currentDowntime) / float64(totalTime) * 100
    
    return map[string]interface{
   
   }{
   
   
        "service_name":      sm.serviceName,
        "uptime":            totalTime.String(),
        "total_checks":      sm.totalChecks,
        "successful_checks": sm.successfulChecks,
        "availability_pct":  fmt.Sprintf("%.4f%%", availability),
        "downtime":          currentDowntime.String(),
        "is_currently_down": sm.isDown,
    }
}

func main() {
   
   
    // 创建多个服务监控器
    monitors := []*ServiceMonitor{
   
   
        NewServiceMonitor("API Gateway", "https://api.example.com/health", 30*time.Second, 5*time.Second),
        NewServiceMonitor("Auth Service", "https://auth.example.com/health", 30*time.Second, 5*time.Second),
        NewServiceMonitor("Database", "https://db.example.com/health", 60*time.Second, 10*time.Second),
    }
    
    // 启动所有监控器
    for _, monitor := range monitors {
   
   
        monitor.Start()
    }
    
    // 提供监控数据API
    http.HandleFunc("/availability", func(w http.ResponseWriter, r *http.Request) {
   
   
        w.Header().Set("Content-Type", "application/json")
        fmt.Fprintf(w, "{\n")
        
        for i, monitor := range monitors {
   
   
            stats := monitor.GetStats()
            fmt.Fprintf(w, "  \"%s\": {\n", stats["service_name"])
            fmt.Fprintf(w, "    \"availability\": \"%s\",\n", stats["availability_pct"])
            fmt.Fprintf(w, "    \"uptime\": \"%s\",\n", stats["uptime"])
            fmt.Fprintf(w, "    \"downtime\": \"%s\",\n", stats["downtime"])
            fmt.Fprintf(w, "    \"is_down\": %v,\n", stats["is_currently_down"])
            fmt.Fprintf(w, "    \"total_checks\": %d,\n", stats["total_checks"])
            fmt.Fprintf(w, "    \"successful_checks\": %d\n", stats["successful_checks"])
            if i < len(monitors)-1 {
   
   
                fmt.Fprintf(w, "  },\n")
            } else {
   
   
                fmt.Fprintf(w, "  }\n")
            }
        }
        
        fmt.Fprintf(w, "}\n")
    })
    
    log.Println("Starting availability monitoring server on :8081")
    if err := http.ListenAndServe(":8081", nil); err != nil {
   
   
        log.Fatalf("Server failed: %v", err)
    }
}

这个示例展示了如何:

  • 持续监控多个服务的健康状态
  • 计算服务的可用性百分比
  • 跟踪停机时间和可用时间
  • 通过API提供可用性指标

3.5 制定合适的SLA级别

制定SLA时应考虑以下因素:

  1. 业务需求和影响:服务对业务的重要性和不可用的影响
  2. 技术可行性:从技术角度是否能够实现该可用性目标
  3. 成本与收益平衡:更高的可用性通常需要更高的投入
  4. 依赖服务的SLA:系统可用性会受到所依赖服务的SLA限制
  5. 历史性能数据:基于历史数据设定切实可行的目标

故障模式与故障域

建设高可用系统首先需要理解各种可能的故障模式,然后通过故障域隔离来限制故障的影响范围。

4.1 常见故障模式分析

故障模式是系统可能发生故障的各种方式。理解这些模式有助于我们设计更健壮的系统。以下是常见的故障模式:

4.1.1 硬件故障
  • 服务器故障:物理机器的CPU、内存或主板故障
  • 存储故障:硬盘故障、存储系统崩溃
  • 网络故障:网络设备故障、网络连接中断、网络延迟增高
  • 电源故障:电源中断、电源不稳定
4.1.2 软件故障
  • 程序崩溃:未处理的异常、内存泄漏
  • 死锁:多个进程或线程相互等待资源
  • 资源耗尽:内存耗尽、连接池耗尽、文件描述符用尽
  • 性能退化:请求响应时间逐渐增加
  • 版本兼容性问题:新版本与旧版本之间的接口不兼容
4.1.3 数据故障
  • 数据损坏:存储的数据被意外修改
  • 数据丢失:存储系统故障导致数据永久丢失
  • 数据不一致:分布式系统中数据副本之间的不一致
  • 数据库锁争用:多个事务争用同一资源导致性能下降
4.1.4 操作故障
  • 错误配置:错误的系统配置导致故障
  • 计划外维护:非预期的系统维护
  • 流量突增:突发的高流量超出系统处理能力
  • 安全事件:恶意攻击导致系统不可用

4.2 故障域隔离

故障域是指一个故障可能影响的范围。通过合理设计故障域隔离,可以将故障的影响限制在最小范围内。

4.2.1 常见的故障域层次
  1. 主机级故障域:单个服务器或虚拟机
  2. 机架级故障域:同一机架上的多台服务器
  3. 数据中心级故障域:同一数据中心的所有资源
  4. 区域级故障域:同一地理区域的多个数据中心
  5. 云提供商级故障域:依赖同一云服务提供商的所有资源
  6. 应用级故障域:应用程序内部的不同组件或服务
4.2.2 故障域隔离策略
  1. 地理分布:将系统部署在不同地理位置的多个数据中心
  2. 多云策略:使用多个云服务提供商
  3. 服务隔离:将系统拆分为独立的微服务
  4. 分库分表:将数据库拆分为多个独立的实例
  5. 流量分区:不同类型的流量路由到不同的服务实例

4.3 故障注入测试

故障注入是一种主动验证系统弹性的方法,通过人为制造故障来测试系统的容错能力。Go语言提供了丰富的工具来实现故障注入:

package main

import (
    "context"
    "fmt"
    "log"
    "math/rand"
    "net/http"
    "sync/atomic"
    "time"
)

// FaultInjector 可以注入各种类型的故障
type FaultInjector struct {
   
   
    // 模拟延迟的概率(0-100)
    delayProbability int32
    // 模拟错误的概率(0-100)
    errorProbability int32
    // 模拟延迟的持续时间范围(毫秒)
    delayRangeMs int
    // 是否启用混沌模式(随机注入故障)
    chaosEnabled int32
}

// NewFaultInjector 创建新的故障注入器
func NewFaultInjector() *FaultInjector {
   
   
    return &FaultInjector{
   
   
        delayProbability: 0,
        errorProbability: 0,
        delayRangeMs:     500,
        chaosEnabled:     0,
    }
}

// SetDelayProbability 设置延迟概率
func (fi *FaultInjector) SetDelayProbability(probability int32) {
   
   
    atomic.StoreInt32(&fi.delayProbability, probability)
}

// SetErrorProbability 设置错误概率
func (fi *FaultInjector) SetErrorProbability(probability int32) {
   
   
    atomic.StoreInt32(&fi.errorProbability, probability)
}

// SetDelayRange 设置延迟时间范围
func (fi *FaultInjector) SetDelayRange(maxDelayMs int) {
   
   
    fi.delayRangeMs = maxDelayMs
}

// EnableChaos 启用或禁用混沌模式
func (fi *FaultInjector) EnableChaos(enabled bool) {
   
   
    var val int32 = 0
    if enabled {
   
   
        val = 1
    }
    atomic.StoreInt32(&fi.chaosEnabled, val)
}

// IsChaosEnabled 检查混沌模式是否启用
func (fi *FaultInjector) IsChaosEnabled() bool {
   
   
    return atomic.LoadInt32(&fi.chaosEnabled) == 1
}

// MaybeInjectFault 根据概率可能注入故障
func (fi *FaultInjector) MaybeInjectFault(ctx context.Context) error {
   
   
    // 不在混沌模式下,直接返回
    if !fi.IsChaosEnabled() {
   
   
        return nil
    }
    
    // 可能注入延迟
    delayProb := atomic.LoadInt32(&fi.delayProbability)
    if rand.Intn(100) < int(delayProb) {
   
   
        delay := time.Duration(rand.Intn(fi.delayRangeMs)) * time.Millisecond
        log.Printf("故障注入: 增加 %v 延迟", delay)
        
        select {
   
   
        case <-time.After(delay):
            // 延迟完成
        case <-ctx.Done():
            // 请求已被取消
            return ctx.Err()
        }
    }
    
    // 可能注入错误
    errorProb := atomic.LoadInt32(&fi.errorProbability)
    if rand.Intn(100) < int(errorProb) {
   
   
        log.Println("故障注入: 返回服务器错误")
        return fmt.Errorf("注入的服务错误")
    }
    
    return nil
}

// 创建一个全局故障注入器
var faultInjector = NewFaultInjector()

// 中间件:注入故障
func faultInjectionMiddleware(next http.Handler) http.Handler {
   
   
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   
   
        err := faultInjector.MaybeInjectFault(r.Context())
        if err != nil {
   
   
            w.WriteHeader(http.StatusInternalServerError)
            fmt.Fprintf(w, "Service Error: %v", err)
            return
        }
        next.ServeHTTP(w, r)
    })
}

// 实际的服务处理函数
func serviceHandler(w http.ResponseWriter, r *http.Request) {
   
   
    fmt.Fprintf(w, "Service is working normally!\n")
}

// 控制故障注入配置的API
func adminHandler(w http.ResponseWriter, r *http.Request) {
   
   
    if r.Method != http.MethodPost {
   
   
        w.WriteHeader(http.StatusMethodNotAllowed)
        return
    }
    
    action := r.URL.Query().Get("action")
    
    switch action {
   
   
    case "enable_chaos":
        faultInjector.EnableChaos(true)
        fmt.Fprintf(w, "Chaos mode enabled\n")
    case "disable_chaos":
        faultInjector.EnableChaos(false)
        fmt.Fprintf(w, "Chaos mode disabled\n")
    case "set_delay":
        probability := r.URL.Query().Get("probability")
        var prob int32
        fmt.Sscanf(probability, "%d", &prob)
        faultInjector.SetDelayProbability(prob)
        fmt.Fprintf(w, "Delay probability set to %d%%\n", prob)
    case "set_error":
        probability := r.URL.Query().Get("probability")
        var prob int32
        fmt.Sscanf(probability, "%d", &prob)
        faultInjector.SetErrorProbability(prob)
        fmt.Fprintf(w, "Error probability set to %d%%\n", prob)
    default:
        w.WriteHeader(http.StatusBadRequest)
        fmt.Fprintf(w, "Unknown action: %s\n", action)
    }
}

func main() {
   
   
    // 初始化随机数生成器
    rand.Seed(time.Now().UnixNano())
    
    // 设置路由
    mux := http.NewServeMux()
    
    // 正常服务路由
    service := http.HandlerFunc(serviceHandler)
    mux.Handle("/api", faultInjectionMiddleware(service))
    
    // 管理路由
    mux.HandleFunc("/admin/fault-injection", adminHandler)
    
    // 提供故障注入状态API
    mux.HandleFunc("/admin/status", func(w http.ResponseWriter, r *http.Request) {
   
   
        w.Header().Set("Content-Type", "application/json")
        fmt.Fprintf(w, `{
            "chaos_enabled": %v,
            "delay_probability": %d,
            "error_probability": %d,
            "delay_range_ms": %d
        }`,
            faultInjector.IsChaosEnabled(),
            atomic.LoadInt32(&faultInjector.delayProbability),
            atomic.LoadInt32(&faultInjector.errorProbability),
            faultInjector.delayRangeMs)
    })
    
    // 启动服务器
    log.Println("Starting fault injection server on :8082")
    if err := http.ListenAndServe(":8082", mux); err != nil {
   
   
        log.Fatalf("Server failed: %v", err)
    }
}

使用以上代码,你可以:

  1. 模拟服务延迟:curl -X POST "http://localhost:8082/admin/fault-injection?action=set_delay&probability=30"
  2. 模拟服务错误:curl -X POST "http://localhost:8082/admin/fault-injection?action=set_error&probability=20"
  3. 启用混沌模式:curl -X POST "http://localhost:8082/admin/fault-injection?action=enable_chaos"
  4. 查看当前状态:curl http://localhost:8082/admin/status

4.4 Go语言中的故障处理模式

4.4.1 超时控制

使用 context 包进行超时控制是 Go 中最基本的故障处理方式:

func callServiceWithTimeout(url string) ([]byte, error) {
   
   
    // 创建一个带有超时的上下文
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()
    
    // 创建请求
    req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
    if err != nil {
   
   
        return nil, err
    }
    
    // 发送请求
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
   
   
        return nil, err
    }
    defer resp.Body.Close()
    
    // 读取响应
    return ioutil.ReadAll(resp.Body)
}
4.4.2 断路器模式

使用断路器模式可以防止一个故障服务影响整个系统:

type CircuitBreaker struct {
   
   
    mu                  sync.Mutex
    failureThreshold    int
    successThreshold    int
    resetTimeout        time.Duration
    failureCount        int
    successCount        int
    lastStateChangeTime time.Time
    state               string // "closed", "open", "half-open"
}

func NewCircuitBreaker(failureThreshold, successThreshold int, resetTimeout time.Duration) *CircuitBreaker {
   
   
    return &CircuitBreaker{
   
   
        failureThreshold:    failureThreshold,
        successThreshold:    successThreshold,
        resetTimeout:        resetTimeout,
        state:               "closed",
        lastStateChangeTime: time.Now(),
    }
}

func (cb *CircuitBreaker) Execute(operation func() (interface{
   
   }, error)) (interface{
   
   }, error) {
   
   
    cb.mu.Lock()
    state := cb.state
    
    // 如果断路器是打开的,检查是否已经过了重置时间
    if state == "open" && time.Since(cb.lastStateChangeTime) > cb.resetTimeout {
   
   
        cb.setState("half-open")
        state = "half-open"
    }
    cb.mu.Unlock()
    
    // 如果断路器是打开的,快速失败
    if state == "open" {
   
   
        return nil, fmt.Errorf("circuit breaker is open")
    }
    
    // 执行操作
    result, err := operation()
    
    cb.mu.Lock()
    defer cb.mu.Unlock()
    
    // 根据结果更新断路器状态
    if err != nil {
   
   
        cb.onFailure()
    } else {
   
   
        cb.onSuccess()
    }
    
    return result, err
}

func 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gopher部落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值