简介
- 使用goroutine池管理,防止线程过多导致cpu、内存爆满
- 使用网络库,批量访问网络资源
- 实现线程阻塞,等待goroutine 全部执行完毕
demo
package main
import (
"fmt"
"github.com/panjf2000/ants/v2"
"golang.org/x/net/publicsuffix"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/cookiejar"
"strconv"
"sync"
"time"
)
type Session struct {
client *http.Client
}
func (s *Session) SetHeaders(req *http.Request, headers map[string]string) {
if headers != nil {
for key, value := range headers {
req.Header.Add(key, value)
}
}
}
func (s *Session) DefaultHeaders(req *http.Request) {
header := map[string]string{
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Connection": "keep-alive",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36",
}
s.SetHeaders(req, header)
}
func (s *Session) New() *http.Client {
tr := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 10 * time.Second,
ResponseHeaderTimeout: 30 * time.Second,
ExpectContinueTimeout: 10 * time.Second,
}
tr.DisableKeepAlives = false
options := cookiejar.Options{
PublicSuffixList: publicsuffix.List,
}
jar, err := cookiejar.New(&options)
if err != nil {
log.Fatal(err)
}
s.client = &http.Client{
Timeout: 15 * time.Second,
Jar: jar,
}
return nil
}
func (s *Session) Get(url string, headers map[string]string) (*http.Response, error) {
request, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println("http get error: ", err)
return nil, err
}
s.DefaultHeaders(request)
if headers != nil {
s.SetHeaders(request, headers)
}
response, err := s.client.Do(request)
if err != nil {
fmt.Println("http get error: ", err)
return nil, err
}
return response, nil
}
func (s *Session) GetDataStr(response *http.Response) (string, error) {
defer func() { _ = response.Body.Close() }()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal("read error", err)
return "", err
}
return string(body), nil
}
func reqData(s *Session, url string) {
response, err := s.Get(url, nil)
if err != nil {
return
}
buf, err := s.GetDataStr(response)
if err != nil {
log.Fatal(err)
}
fmt.Println(buf)
}
func loopData() {
var wg sync.WaitGroup
pool, _ := ants.NewPool(10000)
defer pool.Release()
s := &Session{}
s.New()
for i := 0; i < 100000; i++ {
wg.Add(1)
url := "http://127.0.0.1:8080/app04/async4?num=" + strconv.Itoa(i)
err := pool.Submit(func() {
defer wg.Done()
reqData(s, url)
})
if err != nil {
log.Fatal(err)
}
}
wg.Wait()
n := pool.Running()
fmt.Printf("running goroutines: %d\n", n)
fmt.Printf("finish all tasks.\n")
}
func main() {
startTime := time.Now()
fmt.Println("start time: ",startTime.Format("2006-01-02 15:04"))
loopData()
endTime := time.Now()
fmt.Println("end time: ",endTime.Format("2006-01-02 15:04"))
fmt.Println(endTime.Sub(startTime).Seconds())
}