面试题答案
一键面试设计思路
- 复用连接:
- Go的
http.Client
默认已经支持连接池,通过设置合适的Transport
来优化连接复用。http.Transport
结构体中的MaxIdleConns
和MaxIdleConnsPerHost
等字段可以控制连接池的行为。MaxIdleConns
设置了整个连接池的最大空闲连接数,MaxIdleConnsPerHost
设置了每个主机的最大空闲连接数。
- Go的
- 处理请求队列:
- 可以使用Go的通道(channel)来实现请求队列。将需要发送的请求封装成结构体,然后通过通道发送给处理请求的协程。这样可以解耦请求的生成和处理过程。
- 控制并发数:
- 使用信号量(
sync.Semaphore
)或带缓冲的通道来控制并发数。例如,使用带缓冲的通道,通道的缓冲大小就代表了允许的最大并发数。当一个请求要处理时,先尝试向通道发送一个信号(占用一个并发槽位),处理完成后再从通道接收(释放一个并发槽位)。
- 使用信号量(
关键代码示例
package main
import (
"fmt"
"net/http"
"sync"
)
// RequestItem 封装请求
type RequestItem struct {
url string
// 可以添加其他请求相关的字段,如请求方法、请求头、请求体等
}
func main() {
// 初始化请求队列
requestQueue := make(chan RequestItem, 100)
// 控制并发数的通道,这里设置最大并发数为10
semaphore := make(chan struct{}, 10)
var wg sync.WaitGroup
// 创建http.Client并设置连接池
transport := &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
}
client := &http.Client{Transport: transport}
// 启动处理请求的协程
for i := 0; i < cap(semaphore); i++ {
wg.Add(1)
go func() {
defer wg.Done()
for item := range requestQueue {
semaphore <- struct{}{}
resp, err := client.Get(item.url)
if err != nil {
fmt.Printf("请求 %s 出错: %v\n", item.url, err)
} else {
defer resp.Body.Close()
fmt.Printf("请求 %s 成功\n", item.url)
}
<-semaphore
}
}()
}
// 模拟添加请求到队列
requests := []string{
"http://example.com/api1",
"http://example.com/api2",
// 更多API端点
}
for _, url := range requests {
requestQueue <- RequestItem{url: url}
}
close(requestQueue)
wg.Wait()
}
在上述代码中:
- 连接复用:通过
http.Transport
的MaxIdleConns
和MaxIdleConnsPerHost
设置连接池参数,实现连接复用。 - 请求队列:使用
chan RequestItem
作为请求队列,将请求封装在RequestItem
结构体中发送到队列。 - 控制并发数:使用
make(chan struct{}, 10)
创建了一个带缓冲的通道semaphore
,缓冲大小为10,以此来控制最大并发数为10。每个请求处理前先向通道发送信号,处理完成后再从通道接收信号,确保并发数不超过设定值。