MST

星途 面试题库

面试题:复杂Go并发服务场景下的优雅关闭策略优化

在一个复杂的Go微服务系统中,存在多个相互依赖的并发服务模块,每个模块都有自己的goroutine池和资源管理逻辑。现在需要设计一个全局的优雅关闭策略,以确保在系统关闭时,各个模块按照正确的顺序关闭,避免数据丢失和资源泄漏。请详细说明你的设计思路,包括涉及到的Go语言特性、数据结构和同步机制,并给出一个概念性的代码框架。
43.1万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. Go语言特性
    • 使用 context.Context 来控制goroutine的生命周期。context 可以传递取消信号,各个模块的goroutine可以监听这个信号,在接收到取消信号时进行清理工作。
    • 利用 sync.WaitGroup 来等待所有goroutine完成清理工作。
  2. 数据结构
    • 可以使用一个 map 来存储各个模块的关闭函数。例如 map[string]func() error,其中键为模块名称,值为对应的关闭函数。这样可以方便地按照一定顺序调用各个模块的关闭函数。
  3. 同步机制
    • 对于每个模块的goroutine池,在关闭时需要保证所有正在运行的goroutine都能安全结束。可以使用 sync.Mutex 来保护对goroutine池状态的修改,同时结合 context.Context 来通知goroutine停止工作。
    • 使用 sync.WaitGroup 等待所有模块的关闭函数执行完毕,确保整个系统安全关闭。

概念性代码框架

package main

import (
    "context"
    "fmt"
    "sync"
)

// 定义模块关闭函数类型
type ShutdownFunc func() error

// 存储各个模块的关闭函数
var shutdownFuncs = make(map[string]ShutdownFunc)

// 注册模块关闭函数
func RegisterShutdownFunc(moduleName string, f ShutdownFunc) {
    shutdownFuncs[moduleName] = f
}

// 全局优雅关闭函数
func GracefulShutdown(ctx context.Context) error {
    var wg sync.WaitGroup
    var errChan = make(chan error, len(shutdownFuncs))

    // 按照一定顺序调用各个模块的关闭函数
    for moduleName, shutdownFunc := range shutdownFuncs {
        wg.Add(1)
        go func(name string, f ShutdownFunc) {
            defer wg.Done()
            err := f()
            if err != nil {
                errChan <- fmt.Errorf("module %s shutdown error: %v", name, err)
            }
        }(moduleName, shutdownFunc)
    }

    go func() {
        wg.Wait()
        close(errChan)
    }()

    // 等待所有关闭函数执行完毕,或者上下文取消
    for i := 0; i < len(shutdownFuncs); i++ {
        select {
        case err := <-errChan:
            if err != nil {
                return err
            }
        case <-ctx.Done():
            return ctx.Err()
        }
    }
    return nil
}

// 示例模块关闭函数
func Module1Shutdown() error {
    // 清理Module1的资源
    fmt.Println("Module1 is shutting down")
    return nil
}

func Module2Shutdown() error {
    // 清理Module2的资源
    fmt.Println("Module2 is shutting down")
    return nil
}

func main() {
    // 注册模块关闭函数
    RegisterShutdownFunc("Module1", Module1Shutdown)
    RegisterShutdownFunc("Module2", Module2Shutdown)

    ctx, cancel := context.WithCancel(context.Background())

    // 模拟系统接收到关闭信号
    go func() {
        // 模拟一些业务逻辑
        fmt.Println("System is running")
        // 这里假设一段时间后接收到关闭信号
        cancel()
    }()

    err := GracefulShutdown(ctx)
    if err != nil {
        fmt.Printf("Shutdown error: %v\n", err)
    } else {
        fmt.Println("System shutdown gracefully")
    }
}