MST

星途 面试题库

面试题:Go语言扇出模式在高并发场景下的优化策略

在高并发环境中使用Go语言的扇出模式时,可能会面临资源消耗过大、数据处理不均衡等问题。请提出至少三种优化策略,并结合代码说明如何在实际项目中应用这些策略来提高系统的性能和稳定性。
47.8万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

1. 限制并发数

  • 策略:通过使用 sync.WaitGroupchannel 来控制并发数,避免过多的 goroutine 同时运行导致资源耗尽。
  • 代码示例
package main

import (
    "fmt"
    "sync"
)

func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for j := range jobs {
        fmt.Printf("Worker %d started job %d\n", id, j)
        result := j * 2
        fmt.Printf("Worker %d finished job %d, result: %d\n", id, j, result)
        results <- result
    }
}

func main() {
    const numJobs = 10
    const maxWorkers = 3

    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)
    var wg sync.WaitGroup

    for i := 0; i < maxWorkers; i++ {
        wg.Add(1)
        go worker(i, jobs, results, &wg)
    }

    for j := 0; j < numJobs; j++ {
        jobs <- j
    }
    close(jobs)

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

    for r := range results {
        fmt.Printf("Result: %d\n", r)
    }
}

在上述代码中,maxWorkers 控制了同时运行的 goroutine 数量,避免了资源过度消耗。

2. 负载均衡

  • 策略:采用更智能的任务分配方式,例如轮询或加权轮询,确保每个 goroutine 处理的任务量相对均衡。
  • 代码示例
package main

import (
    "fmt"
    "sync"
)

func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for j := range jobs {
        fmt.Printf("Worker %d started job %d\n", id, j)
        result := j * 2
        fmt.Printf("Worker %d finished job %d, result: %d\n", id, j, result)
        results <- result
    }
}

func main() {
    const numJobs = 10
    const numWorkers = 3

    jobs := make([]chan int, numWorkers)
    results := make(chan int, numJobs)
    var wg sync.WaitGroup

    for i := 0; i < numWorkers; i++ {
        jobs[i] = make(chan int)
        wg.Add(1)
        go worker(i, jobs[i], results, &wg)
    }

    for j := 0; j < numJobs; j++ {
        jobs[j%numWorkers] <- j
    }

    for i := 0; i < numWorkers; i++ {
        close(jobs[i])
    }

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

    for r := range results {
        fmt.Printf("Result: %d\n", r)
    }
}

这里通过 jobs[j%numWorkers] <- j 实现了简单的轮询负载均衡,让每个 worker 处理的任务相对均衡。

3. 资源复用

  • 策略:对于一些昂贵的资源(如数据库连接、网络连接等),可以使用连接池来复用,减少资源创建和销毁的开销。
  • 代码示例
package main

import (
    "database/sql"
    "fmt"
    "sync"

    _ "github.com/go-sql-driver/mysql"
)

func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup, db *sql.DB) {
    defer wg.Done()
    for j := range jobs {
        fmt.Printf("Worker %d started job %d\n", id, j)
        // 复用数据库连接进行操作
        row := db.QueryRow("SELECT some_column FROM some_table WHERE id =?", j)
        var result int
        row.Scan(&result)
        fmt.Printf("Worker %d finished job %d, result: %d\n", id, j, result)
        results <- result
    }
}

func main() {
    const numJobs = 10
    const numWorkers = 3

    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)
    var wg sync.WaitGroup

    // 初始化数据库连接池
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/database_name")
    if err != nil {
        panic(err.Error())
    }
    defer db.Close()

    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go worker(i, jobs, results, &wg, db)
    }

    for j := 0; j < numJobs; j++ {
        jobs <- j
    }
    close(jobs)

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

    for r := range results {
        fmt.Printf("Result: %d\n", r)
    }
}

此代码示例中使用了数据库连接池,每个 worker 复用连接池中的数据库连接,减少了连接创建和销毁的开销,提高了性能和稳定性。