最近用到了 Go 從 Excel 導(dǎo)數(shù)據(jù)到服務(wù)器內(nèi)部 用的是 http 請(qǐng)求
但是發(fā)現(xiàn)一個(gè)問(wèn)題 從文件讀取之后 新開(kāi) Goroutine 會(huì)無(wú)限制新增
導(dǎo)致全部卡在初始化請(qǐng)求 于是乎就卡死了
問(wèn)題模擬
- 模擬代碼
func main() {
pool := sync.WaitGroup{}
for i := 0; i < 500; i++ {
pool.Add(1)
go func(i int) {
resp, err := http.Get("http://ip.3322.org")
if err != nil {
fmt.Println(i, err)
} else {
defer resp.Body.Close()
result, _ := ioutil.ReadAll(resp.Body)
fmt.Println(i, string(result))
}
pool.Done()
}(i)
}
pool.Wait()
}
- 數(shù)量小的情況下 沒(méi)有問(wèn)題 但是數(shù)量比較大的情況 就會(huì)發(fā)現(xiàn)程序直接卡死 一段時(shí)間之后報(bào)錯(cuò) 并且沒(méi)有發(fā)出任何請(qǐng)求
問(wèn)題解決
- 實(shí)際上看的出來(lái) 是應(yīng)為同時(shí)發(fā)起了太多的HTTP請(qǐng)求 導(dǎo)致系統(tǒng)卡死 數(shù)據(jù)沒(méi)有發(fā)送
- 想到我在Java中用Thread提交請(qǐng)求 我就考慮 可不可限制 Goroutine 的數(shù)量
- 使用強(qiáng)大的百度 果然找到了大佬已經(jīng)寫(xiě)好的協(xié)程池
- 代碼如下 我加上了注釋
package gopool
import (
"sync"
)
// Pool Goroutine Pool
type Pool struct {
queue chan int
wg *sync.WaitGroup
}
// New 新建一個(gè)協(xié)程池
func New(size int) *Pool {
if size <= 0 {
size = 1
}
return &Pool{
queue: make(chan int, size),
wg: &sync.WaitGroup{},
}
}
// Add 新增一個(gè)執(zhí)行
func (p *Pool) Add(delta int) {
// delta為正數(shù)就添加
for i := 0; i < delta; i++ {
p.queue <- 1
}
// delta為負(fù)數(shù)就減少
for i := 0; i > delta; i-- {
<-p.queue
}
p.wg.Add(delta)
}
// Done 執(zhí)行完成減一
func (p *Pool) Done() {
<-p.queue
p.wg.Done()
}
// Wait 等待Goroutine執(zhí)行完畢
func (p *Pool) Wait() {
p.wg.Wait()
}
- 然后修改剛才的測(cè)試方法
package main
import (
"io/ioutil"
"log"
"net/http"
"yumc.pw/cloud/lib/gopool"
)
func main() {
// 這里限制5個(gè)并發(fā)
pool := gopool.New(5)// sync.WaitGroup{}
for i := 0; i < 500; i++ {
pool.Add(1)
go func(i int) {
resp, err := http.Get("http://ip.3322.org")
if err != nil {
fmt.Println(i, err)
} else {
defer resp.Body.Close()
result, _ := ioutil.ReadAll(resp.Body)
fmt.Println(i, string(result))
}
pool.Done()
}(i)
}
pool.Wait()
}
- 完美解決