MatrixDeity

Untitled

Apr 23rd, 2023 (edited)
1,747
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Go 2.97 KB | None | 0 0
  1. package main
  2.  
  3. import (
  4.     "context"
  5.     "fmt"
  6.     "net/http"
  7.     "os"
  8.     "sync"
  9.     "time"
  10. )
  11.  
  12. const (
  13.     bufferSize int = 128
  14. )
  15.  
  16. type Requester struct {
  17.     client    *http.Client
  18.     poolSize  int
  19.     buffer    []string
  20.     bufferIdx int
  21.     attempts  int
  22. }
  23.  
  24. type RequesterConfig struct {
  25.     PoolSize int
  26.     Timeout  time.Duration
  27.     Attempts int
  28. }
  29.  
  30. func New(config RequesterConfig) *Requester {
  31.     requester := &Requester{
  32.         client: &http.Client{
  33.             Timeout: config.Timeout,
  34.         },
  35.         poolSize:  config.PoolSize,
  36.         buffer:    make([]string, bufferSize),
  37.         bufferIdx: 0,
  38.         attempts:  config.Attempts,
  39.     }
  40.  
  41.     return requester
  42. }
  43.  
  44. func (r *Requester) Request(ctx context.Context, urls []string, filename string) {
  45.     outChan := make(chan string, len(urls))
  46.     defer close(outChan)
  47.  
  48.     wg := r.asyncGet(ctx, urls, outChan)
  49.  
  50.     os.Remove(filename)
  51.     for i := 0; i < len(urls); i++ {
  52.         if r.bufferIdx == bufferSize {
  53.             r.dumpToFile(filename)
  54.         }
  55.  
  56.         select {
  57.         case resp := <-outChan:
  58.             r.buffer[r.bufferIdx] = resp
  59.             r.bufferIdx++
  60.         case <-ctx.Done():
  61.             break
  62.         }
  63.     }
  64.  
  65.     wg.Wait()
  66.  
  67.     r.dumpToFile(filename)
  68. }
  69.  
  70. func (r *Requester) asyncGet(ctx context.Context, urls []string, outChan chan<- string) *sync.WaitGroup {
  71.     inChan := make(chan string)
  72.  
  73.     go func() {
  74.         defer close(inChan)
  75.  
  76.         for _, url := range urls {
  77.             select {
  78.             case inChan <- url:
  79.             case <-ctx.Done():
  80.                 return
  81.             }
  82.         }
  83.     }()
  84.  
  85.     wg := &sync.WaitGroup{}
  86.  
  87.     for i := 0; i < r.poolSize; i++ {
  88.         wg.Add(1)
  89.         go r.worker(ctx, inChan, outChan, wg)
  90.     }
  91.  
  92.     return wg
  93. }
  94.  
  95. func (r *Requester) worker(ctx context.Context, inChan <-chan string, outChan chan<- string, wg *sync.WaitGroup) {
  96.     defer wg.Done()
  97.  
  98.     for {
  99.         select {
  100.         case url, ok := <-inChan:
  101.             if !ok {
  102.                 return
  103.             }
  104.  
  105.             for i := 1; i <= r.attempts; i++ {
  106.                 request, err := http.NewRequestWithContext(ctx, "GET", url, nil)
  107.                 if err != nil {
  108.                     panic(err)
  109.                 }
  110.  
  111.                 response, err := r.client.Do(request)
  112.                 if err == nil {
  113.                     defer response.Body.Close()
  114.                     outChan <- fmt.Sprintf("%s %d\n", url, response.StatusCode)
  115.  
  116.                     if response.StatusCode < 500 {
  117.                         break
  118.                     }
  119.                 } else if err == context.Canceled {
  120.                     break
  121.                 }
  122.             }
  123.         case <-ctx.Done():
  124.             return
  125.         }
  126.     }
  127. }
  128.  
  129. func (r *Requester) dumpToFile(filename string) {
  130.     outfile, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
  131.     if err != nil {
  132.         panic(err)
  133.     }
  134.  
  135.     defer outfile.Close()
  136.  
  137.     for i := 0; i < r.bufferIdx; i++ {
  138.         outfile.WriteString(r.buffer[i])
  139.     }
  140.  
  141.     r.bufferIdx = 0
  142. }
  143.  
  144. func main() {
  145.     sites := []string{
  146.         "https://www.avito.ru/",
  147.         "https://www.ozon.ru/",
  148.         "https://vk.com/",
  149.         "https://yandex.ru/",
  150.         "https://www.google.com/",
  151.         "https://mail.ru/",
  152.         "https://ok.ru/",
  153.     }
  154.  
  155.     requester := New(RequesterConfig{
  156.         PoolSize: 2,
  157.         Timeout:  2 * time.Second,
  158.         Attempts: 3,
  159.     })
  160.  
  161.     ctx, cancel := context.WithTimeout(context.Background(), 2000*time.Millisecond)
  162.     defer cancel()
  163.     requester.Request(ctx, sites, "./out.txt")
  164. }
  165.  
Advertisement
Add Comment
Please, Sign In to add comment