Skip to content

Instantly share code, notes, and snippets.

@grench
Last active July 26, 2018 10:13
Show Gist options
  • Save grench/7a169384f48ea44d3e013df49ffbfe13 to your computer and use it in GitHub Desktop.
Save grench/7a169384f48ea44d3e013df49ffbfe13 to your computer and use it in GitHub Desktop.
Решение к заданию
package main
import (
"fmt"
"net/http"
"time"
"errors"
"log"
"io/ioutil"
"runtime/debug"
"strings"
"sync"
"sync/atomic"
"runtime"
"bufio"
"os"
)
func main() {
// Для подсчета всех упоминаний слова "Go" использую атомарные операции
// Как вариант, можно использовать мьютексы
var total uint64 = 0
// создадим один клиент *http.Client
// Если создавать новый клиент в каждом запросе, то при нагрузке будет течь память и кол-во файловых дескрипторов.
httpClient := createHttpClient()
// создадим канал для обработки в 5 горутин одновременно
semaphore := make(chan struct{}, 5)
// используем WaitGroup, чтобы программа не прекратилась раньше выполнения всех горутин
var wg sync.WaitGroup
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
url := scanner.Text()
wg.Add(1)
semaphore <- struct{}{} // acquire semaphore
go func(url string) {
body, statusCode := makeRequestGet(url, httpClient)
if statusCode == 200 {
count := strings.Count(string(body[:]), "Go")
// атомарно увеличим счетчик, чтобы не было DATA RACE
atomic.AddUint64(&total, uint64(count))
runtime.Gosched()
fmt.Println("Count for", url, count)
} else {
fmt.Println(url, "статус ответа:", statusCode)
}
<-semaphore // release semaphore
wg.Done()
}(url)
}
wg.Wait()
totalFinal := atomic.LoadUint64(&total)
fmt.Println("Total:", totalFinal)
}
// получим указатель на *http.Client
// зададим количества соединений с хостами. Иначе будет тратиться время на создание новых соединений.
// зададим таймауты для соединений.
func createHttpClient() *http.Client {
defaultRoundTripper := http.DefaultTransport
defaultTransportPointer, ok := defaultRoundTripper.(*http.Transport)
if !ok {
log.Panic(errors.New("defaultRoundTripper not an *http.Transport"))
}
defaultTransport := *defaultTransportPointer
// максимальное кол-во соединений, которое может находится в пуле свободных соединений для ВСЕХ хостов
defaultTransport.MaxIdleConns = 200
defaultTransport.MaxIdleConnsPerHost = 100
defaultTransport.TLSHandshakeTimeout = 5 * time.Second
// максимальное время, которое незанятые соединения могут храниться в пуле до своего закрытия
defaultTransport.IdleConnTimeout = 60 * time.Second
client := &http.Client {
Transport: &defaultTransport,
}
return client
}
// функция для выполнения GET запросов
func makeRequestGet(urlPath string, httpClient *http.Client) ([]byte, int) {
var res []byte
var statusCode int
req, err := http.NewRequest("GET", urlPath, nil)
PrintError(err, "")
if err == nil {
resp, err := httpClient.Do(req)
PrintError(err, "Ошибка при выполнении запроса: " + urlPath)
statusCode = resp.StatusCode
if err == nil {
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
PrintError(err, "Ошибка при получении body запроса: " + urlPath)
if err == nil {
res = body
}
}
}
return res, statusCode
}
// функция для вывода сообщений об ошибках в лог
func PrintError(err error, msg string) {
if err != nil {
if len(msg) > 0 {
msg = "MSG: " + msg
}
stack := debug.Stack()
log.Printf("%s%s\n%s", err, msg, stack)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment