在Golang开发中,同步调用HTTP接口时,程序会等待当前请求返回后再执行后续逻辑,当需要处理多个耗时接口调用时,这种方式会大幅降低执行效率。使用异步非阻塞的方式发起HTTP请求,可以让多个请求并行执行,减少整体等待时间。

核心实现原理
Golang实现异步HTTP请求的核心依赖两个特性:goroutine和channel。goroutine是Golang的轻量级线程,可以并发执行任务,每个HTTP请求可以放到独立的goroutine中执行,实现非阻塞效果。channel则用于在不同goroutine之间传递数据,把每个请求的返回结果收集到主goroutine中统一处理。
基础实现示例
下面是一个简单的异步HTTP请求示例,同时发起三个接口请求,等待所有请求完成后再处理结果:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"sync"
"time"
)
// 定义请求结果结构体
type RequestResult struct {
Url string
Body string
Err error
CostTime time.Duration
}
func asyncHttpRequest(url string, resultChan chan<- RequestResult, wg *sync.WaitGroup) {
// 函数执行完成后减少等待组计数
defer wg.Done()
startTime := time.Now()
// 发起HTTP GET请求
resp, err := http.Get(url)
if err != nil {
resultChan <- RequestResult{Url: url, Err: err, CostTime: time.Since(startTime)}
return
}
defer resp.Body.Close()
// 读取响应内容
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
resultChan <- RequestResult{Url: url, Err: err, CostTime: time.Since(startTime)}
return
}
// 把结果发送到channel
resultChan <- RequestResult{
Url: url,
Body: string(body),
Err: nil,
CostTime: time.Since(startTime),
}
}
func main() {
// 需要请求的接口地址列表
urls := []string{
"http://127.0.0.1:8080/api/user",
"http://127.0.0.1:8080/api/order",
"http://127.0.0.1:8080/api/product",
}
// 创建结果channel,缓冲大小等于请求数量
resultChan := make(chan RequestResult, len(urls))
// 创建等待组,用于等待所有goroutine执行完成
var wg sync.WaitGroup
// 遍历地址发起异步请求
for _, url := range urls {
wg.Add(1)
go asyncHttpRequest(url, resultChan, &wg)
}
// 等待所有goroutine执行完成,然后关闭channel
go func() {
wg.Wait()
close(resultChan)
}()
// 从channel中读取所有结果
for result := range resultChan {
if result.Err != nil {
fmt.Printf("请求 %s 失败,耗时 %v,错误:%vn", result.Url, result.CostTime, result.Err)
} else {
fmt.Printf("请求 %s 成功,耗时 %v,响应长度:%dn", result.Url, result.CostTime, len(result.Body))
}
}
}
关键注意事项
超时控制
上面的示例中使用的http.Get没有设置超时时间,如果接口响应缓慢会导致goroutine长时间阻塞,建议自定义http.Client设置超时时间:
// 创建带超时的HTTP客户端,超时时间设置为5秒
client := &http.Client{
Timeout: 5 * time.Second,
}
// 使用自定义客户端发起请求
resp, err := client.Get(url)
错误处理
异步请求中每个goroutine的错误需要单独捕获,不能直接panic,否则会导致整个程序崩溃。上面示例中把错误放到了RequestResult结构体中,主goroutine可以统一处理所有错误。
资源释放
每个HTTP响应体的Body都需要手动关闭,否则会造成资源泄漏,所以示例中使用了defer resp.Body.Close()确保响应体被正确释放。
适用场景
异步非阻塞HTTP请求适合以下场景:
- 需要同时调用多个无依赖关系的第三方接口
- 接口响应时间较长,需要减少整体等待时间
- 批量处理接口调用任务,提升程序吞吐量
如果接口之间存在依赖关系,比如第二个接口的参数依赖第一个接口的返回结果,就不适合使用这种完全并行的异步方式,需要根据依赖关系调整执行逻辑。