面试题答案
一键面试使用goroutine和channel处理大量HTTP请求提升吞吐量
- 使用goroutine处理请求:
- 在Go中,当接收到HTTP请求时,为每个请求启动一个新的goroutine来处理。例如,在标准库
net/http
的HandleFunc
中:
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { go func() { // 具体的请求处理逻辑 // 这里处理完成后向w.Write一些响应数据 _, err := w.Write([]byte("Response data")) if err!= nil { // 处理写入错误 } }() })
- 这样每个请求都在独立的goroutine中处理,从而实现并发处理,大大提高了系统同时处理多个请求的能力。
- 在Go中,当接收到HTTP请求时,为每个请求启动一个新的goroutine来处理。例如,在标准库
- 使用channel进行数据通信和控制:
- 请求分发:可以使用一个
channel
来作为请求的缓冲区。例如:
type Request struct { w http.ResponseWriter r *http.Request } requestChan := make(chan Request, 100) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { req := Request{w, r} requestChan <- req })
- 工作池模式:启动多个工作goroutine从
requestChan
中获取请求并处理。
const numWorkers = 10 for i := 0; i < numWorkers; i++ { go func() { for req := range requestChan { // 处理请求 _, err := req.w.Write([]byte("Response data")) if err!= nil { // 处理写入错误 } } }() }
- 这种方式通过控制工作goroutine的数量,可以避免系统资源过度消耗,同时利用
channel
的阻塞特性来协调请求的处理。
- 请求分发:可以使用一个
避免并发问题
- 资源竞争:
- 使用互斥锁(Mutex):如果在处理请求过程中需要访问共享资源,例如共享的数据库连接池或全局计数器。可以使用
sync.Mutex
来保护这些资源。例如:
var counter int var mu sync.Mutex http.HandleFunc("/count", func(w http.ResponseWriter, r *http.Request) { mu.Lock() counter++ mu.Unlock() // 返回计数器的值作为响应 str := fmt.Sprintf("Count: %d", counter) _, err := w.Write([]byte(str)) if err!= nil { // 处理写入错误 } })
- 使用读写锁(RWMutex):当共享资源的读操作远远多于写操作时,可以使用
sync.RWMutex
。例如,对于一个只读的配置信息共享变量:
var config struct { // 配置信息字段 } var rwmu sync.RWMutex http.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) { rwmu.RLock() // 读取config并返回响应 //... rwmu.RUnlock() })
- 使用互斥锁(Mutex):如果在处理请求过程中需要访问共享资源,例如共享的数据库连接池或全局计数器。可以使用
- 死锁:
- 避免循环依赖:在设计并发程序时,确保不同的goroutine之间的资源依赖关系不会形成循环。例如,A goroutine等待B goroutine释放资源,B goroutine又等待A goroutine释放资源,这就会导致死锁。
- 正确使用channel:确保在向channel发送数据时,有对应的接收操作,反之亦然。避免在一个goroutine中既发送又接收同一个channel,而没有适当的同步机制。例如:
ch := make(chan int) go func() { // 发送数据 ch <- 1 }() // 接收数据 data := <-ch
- 数据竞争导致的未定义行为:
- 除了使用锁,还可以通过设计数据结构和逻辑来减少共享资源的使用。例如,将共享资源复制到每个goroutine的本地,避免多个goroutine同时修改同一资源。如果必须共享,尽量将共享资源设计为只读的,或者使用不可变数据结构。