Go語言實現(xiàn)多協(xié)程并發(fā)下載網(wǎng)頁內(nèi)容的完整代碼
一、實戰(zhàn)背景
在互聯(lián)網(wǎng)項目中,我們常需要批量獲取多個網(wǎng)頁的內(nèi)容,例如:
- 爬蟲程序抓取網(wǎng)頁 HTML
- 數(shù)據(jù)聚合服務(wù)請求多個 API
- 批量檢測多個 URL 的可用性
如果逐個請求(串行),效率將非常低下。Go 天生支持高并發(fā),我們可以用 Goroutine 實現(xiàn) 多協(xié)程并發(fā)下載網(wǎng)頁內(nèi)容,顯著提高吞吐能力。
二、實戰(zhàn)目標(biāo)
我們將構(gòu)建一個小型并發(fā)網(wǎng)頁下載器,具備以下能力:
- 輸入一組網(wǎng)址列表
- 使用 Goroutine 并發(fā)請求多個網(wǎng)頁
- 使用 Channel 收集下載結(jié)果
- 打印成功/失敗狀態(tài)與網(wǎng)頁內(nèi)容摘要
- 支持 WaitGroup 等待所有任務(wù)完成
三、完整代碼實現(xiàn)
package main
import (
"fmt"
"io"
"net/http"
"strings"
"sync"
"time"
)
type Result struct {
URL string
Status string
Length int
Error error
}
// 下載網(wǎng)頁內(nèi)容并寫入結(jié)果通道
func fetchURL(url string, wg *sync.WaitGroup, resultCh chan<- Result) {
defer wg.Done()
client := http.Client{
Timeout: 5 * time.Second,
}
resp, err := client.Get(url)
if err != nil {
resultCh <- Result{URL: url, Status: "請求失敗", Error: err}
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
resultCh <- Result{URL: url, Status: "讀取失敗", Error: err}
return
}
resultCh <- Result{
URL: url,
Status: resp.Status,
Length: len(body),
}
}
func main() {
urls := []string{
"https://example.com",
"https://httpbin.org/get",
"https://golang.org",
"https://nonexistent.example.com", // 故意的錯誤URL
}
var wg sync.WaitGroup
resultCh := make(chan Result, len(urls))
// 啟動多個下載協(xié)程
for _, url := range urls {
wg.Add(1)
go fetchURL(url, &wg, resultCh)
}
// 等待所有任務(wù)完成后關(guān)閉通道
go func() {
wg.Wait()
close(resultCh)
}()
// 讀取結(jié)果
for res := range resultCh {
if res.Error != nil {
fmt.Printf("[失敗] %s:%v\n", res.URL, res.Error)
} else {
snippet := fmt.Sprintf("%d 字節(jié)", res.Length)
if res.Length > 0 {
snippet = fmt.Sprintf("%s 內(nèi)容預(yù)覽:%s", snippet, strings.TrimSpace(string([]byte(res.URL)[:min(50, res.Length)])))
}
fmt.Printf("[成功] %s:%s\n", res.URL, snippet)
}
}
fmt.Println("所有網(wǎng)頁請求已完成。")
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
四、輸出示例
[成功] https://example.com:1256 字節(jié) 內(nèi)容預(yù)覽:https://example.com [成功] https://httpbin.org/get:349 字節(jié) 內(nèi)容預(yù)覽:https://httpbin.org/get [成功] https://golang.org:3578 字節(jié) 內(nèi)容預(yù)覽:https://golang.org [失敗] https://nonexistent.example.com:Get "https://nonexistent.example.com": dial tcp: ... 所有網(wǎng)頁請求已完成。
五、重點知識點講解
1. 使用 Goroutine 啟動并發(fā)請求
go fetchURL(url, &wg, resultCh)
每個網(wǎng)頁請求都是一個輕量級的線程(協(xié)程),同時運行,最大化資源利用。
2. 使用 sync.WaitGroup 等待所有任務(wù)完成
WaitGroup 是 Goroutine 的最佳搭檔,確保主線程不會提前退出。
wg.Add(1) defer wg.Done()
3. 使用帶緩沖的 Channel 收集結(jié)果
resultCh := make(chan Result, len(urls))
避免協(xié)程阻塞,收集所有結(jié)果后統(tǒng)一處理。
4. 設(shè)置請求超時
使用 http.Client{ Timeout: ... } 可防止因某個 URL 卡住導(dǎo)致整體阻塞。
5. 防止通道未關(guān)閉阻塞
一定要在所有任務(wù)完成后關(guān)閉結(jié)果通道:
go func() {
wg.Wait()
close(resultCh)
}()
六、可擴(kuò)展方向
這個簡單的并發(fā)網(wǎng)頁下載器可以繼續(xù)擴(kuò)展為:
| 功能方向 | 實現(xiàn)建議 |
|---|---|
| 限制最大并發(fā)數(shù) | 使用帶緩沖的 chan struct{} 控制令牌 |
| 下載網(wǎng)頁保存文件 | 使用 os.Create 寫入 HTML 文件 |
| 支持重試機(jī)制 | 封裝帶重試的請求邏輯 |
| 使用 context 控制取消或超時 | 實現(xiàn)更復(fù)雜的任務(wù)調(diào)度系統(tǒng) |
| 支持代理 | 設(shè)置 Transport.Proxy 實現(xiàn) |
七、小結(jié)
通過本篇案例你掌握了:
- 使用 Goroutine 啟動并發(fā)任務(wù)
- 使用 Channel 匯總?cè)蝿?wù)結(jié)果
- 使用 WaitGroup 管理協(xié)程生命周期
- 網(wǎng)絡(luò)請求的錯誤處理與超時機(jī)制
這為你實現(xiàn)一個功能完善的高并發(fā)爬蟲、網(wǎng)頁檢測器或 API 批量處理工具奠定了基礎(chǔ)。
以上就是Go語言實現(xiàn)多協(xié)程并發(fā)下載網(wǎng)頁內(nèi)容的完整代碼的詳細(xì)內(nèi)容,更多關(guān)于Go多協(xié)程并發(fā)下載網(wǎng)頁內(nèi)容的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Golang WebView跨平臺的桌面應(yīng)用庫的使用
Golang WebView是一個強(qiáng)大的桌面應(yīng)用庫,本文介紹了Golang WebView的特點和使用方法,并列舉示例詳細(xì)的介紹了其在實際項目中的應(yīng)用,具有一定的參考價值,感興趣的可以了解一下2024-03-03
go語言中的json與map相互轉(zhuǎn)換實現(xiàn)
本文主要介紹了go語言中的json與map相互轉(zhuǎn)換實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
go中Excelize處理excel表實現(xiàn)帶數(shù)據(jù)校驗的文件導(dǎo)出
本文主要介紹了go中Excelize處理excel表實現(xiàn)帶數(shù)據(jù)校驗的文件導(dǎo)出,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
解決vscode中g(shù)olang插件依賴安裝失敗問題
這篇文章主要介紹了解決vscode中g(shù)olang插件依賴安裝失敗問題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-08-08
go的defer和閉包示例說明(非內(nèi)部實現(xiàn))
這篇文章主要為大家介紹了go的defer和閉包示例說明(非內(nèi)部實現(xiàn)),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08

