From 9428bd1a52d9fa555f310007fd2dad9a9c8b5ce7 Mon Sep 17 00:00:00 2001 From: akaKAIN Date: Sat, 29 May 2021 13:39:18 +1000 Subject: [PATCH 1/3] fmt --- l1/cmd/circuit_breaker/server.go | 2 +- l1/cmd/crawler/main.go | 2 +- l1/cmd/signals/main.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/l1/cmd/circuit_breaker/server.go b/l1/cmd/circuit_breaker/server.go index 3f321f6..36ca41c 100644 --- a/l1/cmd/circuit_breaker/server.go +++ b/l1/cmd/circuit_breaker/server.go @@ -8,7 +8,7 @@ import ( const addr = "localhost:5301" -func main() { +func main() { http.HandleFunc("/pay", func(writer http.ResponseWriter, request *http.Request) { bb, err := ioutil.ReadAll(request.Body) if err != nil { diff --git a/l1/cmd/crawler/main.go b/l1/cmd/crawler/main.go index 8a7fd54..8616ccf 100755 --- a/l1/cmd/crawler/main.go +++ b/l1/cmd/crawler/main.go @@ -68,7 +68,7 @@ func main() { // ловим сигналы выключения func watchSignals(cancel context.CancelFunc) { - osSignalChan := make(chan os.Signal) + osSignalChan := make(chan os.Signal, 1) signal.Notify(osSignalChan, syscall.SIGINT, diff --git a/l1/cmd/signals/main.go b/l1/cmd/signals/main.go index 42d8e77..20096dc 100644 --- a/l1/cmd/signals/main.go +++ b/l1/cmd/signals/main.go @@ -10,10 +10,10 @@ import ( func main() { log.Print("start") - sigintChan := make(chan os.Signal) + sigintChan := make(chan os.Signal, 1) signal.Notify(sigintChan, syscall.SIGINT) - sigtermChan := make(chan os.Signal) + sigtermChan := make(chan os.Signal, 1) signal.Notify(sigtermChan, syscall.SIGTERM) select { From d038d8c2a3dbd34f393c3d6d6dcf565148880959 Mon Sep 17 00:00:00 2001 From: akaKAIN Date: Sun, 30 May 2021 16:45:13 +1000 Subject: [PATCH 2/3] Added signal listener for sigusr1. Added timeout for http.client --- l1/cmd/crawler/crawler.go | 6 ++++++ l1/cmd/crawler/main.go | 13 +++++++++++++ l1/cmd/crawler/ugly_parser.go | 14 +++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/l1/cmd/crawler/crawler.go b/l1/cmd/crawler/crawler.go index ba159be..3117068 100755 --- a/l1/cmd/crawler/crawler.go +++ b/l1/cmd/crawler/crawler.go @@ -26,6 +26,12 @@ func newCrawler(maxDepth int) *crawler { } } +func (c *crawler) dive() { + c.Lock() + c.maxDepth += 2 + c.Unlock() +} + // рекурсивно сканируем страницы func (c *crawler) run(ctx context.Context, url string, results chan<- crawlResult, depth int) { // просто для того, чтобы успевать следить за выводом программы, можно убрать :) diff --git a/l1/cmd/crawler/main.go b/l1/cmd/crawler/main.go index 8616ccf..96fdd3b 100755 --- a/l1/cmd/crawler/main.go +++ b/l1/cmd/crawler/main.go @@ -56,6 +56,9 @@ func main() { // запускаем горутину для чтения из каналов done := watchCrawler(ctx, results, errorsLimit, resultsLimit) + // запускаем горутину для прослушивания сигнала пользователя + go watchUserSignal(crawler) + // запуск основной логики // внутри есть рекурсивные запуски анализа в других горутинах crawler.run(ctx, url, results, 0) @@ -81,6 +84,16 @@ func watchSignals(cancel context.CancelFunc) { cancel() } +// ловим сигнал пользователя +func watchUserSignal(c *crawler) { + osUserChan := make(chan os.Signal, 1) + + signal.Notify(osUserChan, syscall.SIGUSR1) + <- osUserChan + log.Printf("got user signal to search deep") + c.dive() +} + func watchCrawler(ctx context.Context, results <-chan crawlResult, maxErrors, maxResults int) chan struct{} { readersDone := make(chan struct{}) diff --git a/l1/cmd/crawler/ugly_parser.go b/l1/cmd/crawler/ugly_parser.go index 4b16cf2..25be596 100755 --- a/l1/cmd/crawler/ugly_parser.go +++ b/l1/cmd/crawler/ugly_parser.go @@ -1,18 +1,30 @@ package main import ( + "bytes" "fmt" "golang.org/x/net/html" + "log" "net/http" + "time" ) // парсим страницу func parse(url string) (*html.Node, error) { // что здесь должно быть вместо http.Get? :) - r, err := http.Get(url) + client := http.Client{Timeout: 1 * time.Second} + log.Println("init") + request, err := http.NewRequest("GET", url, bytes.NewReader([]byte{})) + if err != nil { + return nil, fmt.Errorf("can't send request") + } + + r, err := client.Do(request) + log.Println("do") if err != nil { return nil, fmt.Errorf("can't get page") } + b, err := html.Parse(r.Body) if err != nil { return nil, fmt.Errorf("can't parse page") From 3a2e41d265f10d9649a44f34edc3ea5d6fc90e21 Mon Sep 17 00:00:00 2001 From: akaKAIN Date: Wed, 2 Jun 2021 06:35:54 +1000 Subject: [PATCH 3/3] corrected notes --- l1/cmd/crawler/crawler.go | 14 +++-- l1/cmd/crawler/ugly_parser.go | 100 +++++++++++++++++++--------------- 2 files changed, 65 insertions(+), 49 deletions(-) diff --git a/l1/cmd/crawler/crawler.go b/l1/cmd/crawler/crawler.go index 3117068..7e4421c 100755 --- a/l1/cmd/crawler/crawler.go +++ b/l1/cmd/crawler/crawler.go @@ -15,6 +15,7 @@ type crawlResult struct { type crawler struct { sync.Mutex + rwMutex sync.RWMutex visited map[string]string maxDepth int } @@ -27,9 +28,9 @@ func newCrawler(maxDepth int) *crawler { } func (c *crawler) dive() { - c.Lock() + c.rwMutex.RLock() + defer c.rwMutex.RUnlock() c.maxDepth += 2 - c.Unlock() } // рекурсивно сканируем страницы @@ -37,6 +38,9 @@ func (c *crawler) run(ctx context.Context, url string, results chan<- crawlResul // просто для того, чтобы успевать следить за выводом программы, можно убрать :) time.Sleep(2 * time.Second) + ctxParse, cancel := context.WithTimeout(ctx, 3 * time.Second) + defer cancel() + // проверяем что контекст исполнения актуален select { case <-ctx.Done(): @@ -48,7 +52,7 @@ func (c *crawler) run(ctx context.Context, url string, results chan<- crawlResul return } - page, err := parse(url) + page, err := parse(ctxParse, url) if err != nil { // ошибку отправляем в канал, а не обрабатываем на месте results <- crawlResult{ @@ -57,8 +61,8 @@ func (c *crawler) run(ctx context.Context, url string, results chan<- crawlResul return } - title := pageTitle(page) - links := pageLinks(nil, page) + title := pageTitle(ctxParse, page) + links := pageLinks(ctxParse, nil, page) // блокировка требуется, т.к. мы модифицируем мапку в несколько горутин c.Lock() diff --git a/l1/cmd/crawler/ugly_parser.go b/l1/cmd/crawler/ugly_parser.go index 25be596..a5518e4 100755 --- a/l1/cmd/crawler/ugly_parser.go +++ b/l1/cmd/crawler/ugly_parser.go @@ -2,71 +2,83 @@ package main import ( "bytes" + "context" "fmt" "golang.org/x/net/html" - "log" "net/http" "time" ) // парсим страницу -func parse(url string) (*html.Node, error) { - // что здесь должно быть вместо http.Get? :) - client := http.Client{Timeout: 1 * time.Second} - log.Println("init") - request, err := http.NewRequest("GET", url, bytes.NewReader([]byte{})) - if err != nil { - return nil, fmt.Errorf("can't send request") - } +func parse(ctx context.Context, url string) (*html.Node, error) { + select { + case <-ctx.Done(): + return nil, nil + default: + client := http.Client{Timeout: 1 * time.Second} + request, err := http.NewRequest("GET", url, bytes.NewReader([]byte{})) + if err != nil { + return nil, fmt.Errorf("can't send request") + } - r, err := client.Do(request) - log.Println("do") - if err != nil { - return nil, fmt.Errorf("can't get page") - } + r, err := client.Do(request) + if err != nil { + return nil, fmt.Errorf("can't get page") + } - b, err := html.Parse(r.Body) - if err != nil { - return nil, fmt.Errorf("can't parse page") + b, err := html.Parse(r.Body) + if err != nil { + return nil, fmt.Errorf("can't parse page") + } + return b, err } - return b, err } // ищем заголовок на странице -func pageTitle(n *html.Node) string { - var title string - if n.Type == html.ElementNode && n.Data == "title" { - return n.FirstChild.Data - } - for c := n.FirstChild; c != nil; c = c.NextSibling { - title = pageTitle(c) - if title != "" { - break +func pageTitle(ctx context.Context, n *html.Node) string { + select { + case <-ctx.Done(): + return "" + default: + var title string + if n.Type == html.ElementNode && n.Data == "title" { + return n.FirstChild.Data } + for c := n.FirstChild; c != nil; c = c.NextSibling { + title = pageTitle(ctx, c) + if title != "" { + break + } + } + return title } - return title } // ищем все ссылки на страницы. Используем мапку чтобы избежать дубликатов -func pageLinks(links map[string]struct{}, n *html.Node) map[string]struct{} { - if links == nil { - links = make(map[string]struct{}) - } +func pageLinks(ctx context.Context, links map[string]struct{}, n *html.Node) map[string]struct{} { + select { + case <-ctx.Done(): + return nil + default: + if links == nil { + links = make(map[string]struct{}) + } - if n.Type == html.ElementNode && n.Data == "a" { - for _, a := range n.Attr { - if a.Key != "href" { - continue - } + if n.Type == html.ElementNode && n.Data == "a" { + for _, a := range n.Attr { + if a.Key != "href" { + continue + } - // костылик для простоты - if _, ok := links[a.Val]; !ok && len(a.Val) > 2 && a.Val[:2] == "//" { - links["http://"+a.Val[2:]] = struct{}{} + // костылик для простоты + if _, ok := links[a.Val]; !ok && len(a.Val) > 2 && a.Val[:2] == "//" { + links["http://"+a.Val[2:]] = struct{}{} + } } } + for c := n.FirstChild; c != nil; c = c.NextSibling { + links = pageLinks(ctx, links, c) + } + return links } - for c := n.FirstChild; c != nil; c = c.NextSibling { - links = pageLinks(links, c) - } - return links }