面试题答案
一键面试设计思路
- Goroutine 管理:
- 使用一个全局的 Goroutine 管理器,通过一个 channel 来控制所有 Goroutine 的启动与停止。这样在扩容或缩容时,可以方便地统一管理这些 Goroutine。
- 为每个 Goroutine 分配一个唯一的标识符,便于跟踪和管理。
- 资源管理:
- 对于文件句柄和数据库连接等资源,采用资源池的方式进行管理。资源池可以限制资源的最大数量,避免资源过度消耗。
- 当一个 Goroutine 启动时,从资源池中获取资源;当 Goroutine 结束时,将资源归还到资源池。
- 动态扩容与缩容:
- 监听系统的扩容与缩容信号。例如,通过与容器编排工具(如 Kubernetes)集成,接收其发出的扩容或缩容事件。
- 在扩容时,根据需要启动新的 Goroutine,并为其分配资源。在缩容时,安全地停止指定数量的 Goroutine,并将其所占用的资源归还到资源池。
实现方案
- Goroutine 管理器实现:
package main
import (
"context"
"fmt"
"sync"
)
type GoroutineManager struct {
ctx context.Context
cancel context.CancelFunc
wg sync.WaitGroup
id int
}
func NewGoroutineManager() *GoroutineManager {
ctx, cancel := context.WithCancel(context.Background())
return &GoroutineManager{
ctx: ctx,
cancel: cancel,
}
}
func (gm *GoroutineManager) StartTask(task func(ctx context.Context, id int)) {
gm.id++
gm.wg.Add(1)
go func(id int) {
defer gm.wg.Done()
task(gm.ctx, id)
}(gm.id)
}
func (gm *GoroutineManager) Stop() {
gm.cancel()
gm.wg.Wait()
}
- 资源池实现:
package main
import (
"database/sql"
"fmt"
"sync"
_ "github.com/go - sql - driver/mysql"
)
type ResourcePool struct {
pool chan *sql.DB
maxSize int
}
func NewResourcePool(dsn string, maxSize int) (*ResourcePool, error) {
pool := make(chan *sql.DB, maxSize)
for i := 0; i < maxSize; i++ {
db, err := sql.Open("mysql", dsn)
if err != nil {
close(pool)
return nil, err
}
pool <- db
}
return &ResourcePool{
pool: pool,
maxSize: maxSize,
}, nil
}
func (rp *ResourcePool) GetResource() *sql.DB {
return <-rp.pool
}
func (rp *ResourcePool) ReturnResource(db *sql.DB) {
rp.pool <- db
}
func (rp *ResourcePool) Close() {
close(rp.pool)
for db := range rp.pool {
db.Close()
}
}
- 结合动态扩容与缩容:
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
)
func main() {
gm := NewGoroutineManager()
rp, err := NewResourcePool("user:password@tcp(127.0.0.1:3306)/test", 10)
if err != nil {
fmt.Println("Failed to create resource pool:", err)
return
}
defer rp.Close()
// 模拟启动任务
for i := 0; i < 5; i++ {
gm.StartTask(func(ctx context.Context, id int) {
db := rp.GetResource()
defer rp.ReturnResource(db)
for {
select {
case <-ctx.Done():
return
default:
// 执行任务
fmt.Printf("Goroutine %d is working with database connection\n", id)
}
}
})
}
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sigs
fmt.Println()
fmt.Println(sig)
gm.Stop()
os.Exit(0)
}()
fmt.Println("Press Ctrl+C to exit")
select {}
}
在这个实现中,GoroutineManager
用于管理 Goroutine 的生命周期,ResourcePool
用于管理数据库连接资源。通过监听系统信号,可以模拟动态缩容(接收到终止信号时停止 Goroutine),如果要实现动态扩容,可以通过修改启动任务的逻辑,根据扩容信号增加新的 Goroutine。对于文件句柄的管理,同样可以采用类似资源池的方式实现。