Last active
July 26, 2018 10:13
-
-
Save grench/7a169384f48ea44d3e013df49ffbfe13 to your computer and use it in GitHub Desktop.
Решение к заданию
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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