From 90b4eb1b15a808c384c850cb4eb8d60f55e045a1 Mon Sep 17 00:00:00 2001 From: Nibolg1994 Date: Sun, 28 Mar 2021 22:40:35 +0300 Subject: [PATCH 1/3] lesson 1 --- l1/cmd/crawler/crawler.go | 48 +++++++++++++++++++++++++++------------ l1/cmd/crawler/main.go | 31 ++++++++++++++++--------- l1/go.mod | 2 +- 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/l1/cmd/crawler/crawler.go b/l1/cmd/crawler/crawler.go index cd875e4..5bb2230 100644 --- a/l1/cmd/crawler/crawler.go +++ b/l1/cmd/crawler/crawler.go @@ -42,23 +42,44 @@ func (c *crawler) run(ctx context.Context, url string, results chan<- crawlResul return } - page, err := parse(url) - if err != nil { - // ошибку отправляем в канал, а не обрабатываем на месте + var ( + secondCtx, cancel = context.WithTimeout(ctx, time.Second*2) + parsingResult = make(chan struct{}) + title string + links map[string]struct{} + ) + + defer cancel() + go func(parsingResult chan struct{}) { + defer func() { parsingResult <- struct{}{} }() + page, err := parse(url) + if err != nil { + // ошибку отправляем в канал, а не обрабатываем на месте + results <- crawlResult{ + err: errors.Wrapf(err, "parse page %s", url), + } + return + } + + title = pageTitle(page) + links = pageLinks(nil, page) + + // блокировка требуется, т.к. мы модифицируем мапку в несколько горутин + c.Lock() + c.visited[url] = title + c.Unlock() + }(parsingResult) + + select { + case <-secondCtx.Done(): + cancel() results <- crawlResult{ - err: errors.Wrapf(err, "parse page %s", url), + err: errors.New("timeout error " + url), } return + case <-parsingResult: } - title := pageTitle(page) - links := pageLinks(nil, page) - - // блокировка требуется, т.к. мы модифицируем мапку в несколько горутин - c.Lock() - c.visited[url] = title - c.Unlock() - // отправляем результат в канал, не обрабатывая на месте results <- crawlResult{ err: nil, @@ -71,8 +92,7 @@ func (c *crawler) run(ctx context.Context, url string, results chan<- crawlResul if c.checkVisited(link) { continue } - - go c.run(ctx, link, results, depth) + go c.run(ctx, link, results, depth+1) } } } diff --git a/l1/cmd/crawler/main.go b/l1/cmd/crawler/main.go index 8a7fd54..bad2393 100644 --- a/l1/cmd/crawler/main.go +++ b/l1/cmd/crawler/main.go @@ -45,10 +45,9 @@ func main() { started := time.Now() ctx, cancel := context.WithCancel(context.Background()) - go watchSignals(cancel) - defer cancel() - crawler := newCrawler(depthLimit) + go watchSignals(cancel, crawler) + defer cancel() // создаём канал для результатов results := make(chan crawlResult) @@ -67,18 +66,27 @@ func main() { } // ловим сигналы выключения -func watchSignals(cancel context.CancelFunc) { - osSignalChan := make(chan os.Signal) +func watchSignals(cancel context.CancelFunc, crawler *crawler) { + osSignalTerminatedChan := make(chan os.Signal) + osSignalUserChan := make(chan os.Signal) - signal.Notify(osSignalChan, + signal.Notify(osSignalTerminatedChan, syscall.SIGINT, syscall.SIGTERM) - sig := <-osSignalChan - log.Printf("got signal %q", sig.String()) - - // если сигнал получен, отменяем контекст работы - cancel() + signal.Notify(osSignalUserChan, syscall.SIGINT) + for { + select { + case sig := <-osSignalTerminatedChan: + log.Printf("got signal %q", sig.String()) + // если сигнал получен, отменяем контекст работы + cancel() + return + case <-osSignalUserChan: + crawler.maxDepth += 10 + log.Printf("got signal %d", crawler.maxDepth) + } + } } func watchCrawler(ctx context.Context, results <-chan crawlResult, maxErrors, maxResults int) chan struct{} { @@ -93,6 +101,7 @@ func watchCrawler(ctx context.Context, results <-chan crawlResult, maxErrors, ma case result := <-results: if result.err != nil { + log.Println(result.err.Error()) maxErrors-- if maxErrors <= 0 { log.Println("max errors exceeded") diff --git a/l1/go.mod b/l1/go.mod index 4814124..33032a6 100644 --- a/l1/go.mod +++ b/l1/go.mod @@ -1,4 +1,4 @@ -module github.com/kilchik/gb/lesson1 +module github.com/Nibolg1994/gb/lesson1 go 1.15 From dcca62df3524aa424f73f11cb4420ee0194133c8 Mon Sep 17 00:00:00 2001 From: Nibolg1994 Date: Sun, 28 Mar 2021 22:47:12 +0300 Subject: [PATCH 2/3] fix signal --- l1/cmd/crawler/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1/cmd/crawler/main.go b/l1/cmd/crawler/main.go index bad2393..44eeb7b 100644 --- a/l1/cmd/crawler/main.go +++ b/l1/cmd/crawler/main.go @@ -74,7 +74,7 @@ func watchSignals(cancel context.CancelFunc, crawler *crawler) { syscall.SIGINT, syscall.SIGTERM) - signal.Notify(osSignalUserChan, syscall.SIGINT) + signal.Notify(osSignalUserChan, syscall.SIGUSR1) for { select { case sig := <-osSignalTerminatedChan: From 33ad0f6ab78e52d76c3007d294b304f908e5e078 Mon Sep 17 00:00:00 2001 From: Nibolg1994 Date: Sat, 3 Apr 2021 14:12:14 +0300 Subject: [PATCH 3/3] fixes lesson 1 --- l1/cmd/crawler/crawler.go | 67 ++++++++++++++++----------------------- l1/cmd/crawler/main.go | 5 ++- 2 files changed, 30 insertions(+), 42 deletions(-) diff --git a/l1/cmd/crawler/crawler.go b/l1/cmd/crawler/crawler.go index 5bb2230..c8f91d5 100644 --- a/l1/cmd/crawler/crawler.go +++ b/l1/cmd/crawler/crawler.go @@ -14,9 +14,10 @@ type crawlResult struct { } type crawler struct { - sync.Mutex - visited map[string]string - maxDepth int + mutexVisited sync.Mutex + mutexMaxDepth sync.RWMutex + visited map[string]string + maxDepth int } func newCrawler(maxDepth int) *crawler { @@ -26,6 +27,12 @@ func newCrawler(maxDepth int) *crawler { } } +func (c *crawler) increaseMaxDepth(maxDepth int) { + c.mutexMaxDepth.Lock() + c.maxDepth += maxDepth + c.mutexMaxDepth.Unlock() +} + // рекурсивно сканируем страницы func (c *crawler) run(ctx context.Context, url string, results chan<- crawlResult, depth int) { // просто для того, чтобы успевать следить за выводом программы, можно убрать :) @@ -37,49 +44,30 @@ func (c *crawler) run(ctx context.Context, url string, results chan<- crawlResul return default: + c.mutexMaxDepth.RLock() // проверка глубины if depth >= c.maxDepth { return } + c.mutexMaxDepth.RUnlock() - var ( - secondCtx, cancel = context.WithTimeout(ctx, time.Second*2) - parsingResult = make(chan struct{}) - title string - links map[string]struct{} - ) - - defer cancel() - go func(parsingResult chan struct{}) { - defer func() { parsingResult <- struct{}{} }() - page, err := parse(url) - if err != nil { - // ошибку отправляем в канал, а не обрабатываем на месте - results <- crawlResult{ - err: errors.Wrapf(err, "parse page %s", url), - } - return - } - - title = pageTitle(page) - links = pageLinks(nil, page) - - // блокировка требуется, т.к. мы модифицируем мапку в несколько горутин - c.Lock() - c.visited[url] = title - c.Unlock() - }(parsingResult) - - select { - case <-secondCtx.Done(): - cancel() + page, err := parse(url) + if err != nil { + // ошибку отправляем в канал, а не обрабатываем на месте results <- crawlResult{ - err: errors.New("timeout error " + url), + err: errors.Wrapf(err, "parse page %s", url), } return - case <-parsingResult: } + title := pageTitle(page) + links := pageLinks(nil, page) + + // блокировка требуется, т.к. мы модифицируем мапку в несколько горутин + c.mutexVisited.Lock() + c.visited[url] = title + c.mutexVisited.Unlock() + // отправляем результат в канал, не обрабатывая на месте results <- crawlResult{ err: nil, @@ -92,14 +80,15 @@ func (c *crawler) run(ctx context.Context, url string, results chan<- crawlResul if c.checkVisited(link) { continue } - go c.run(ctx, link, results, depth+1) + + go c.run(ctx, link, results, depth) } } } func (c *crawler) checkVisited(url string) bool { - c.Lock() - defer c.Unlock() + c.mutexVisited.Lock() + defer c.mutexVisited.Unlock() _, ok := c.visited[url] return ok diff --git a/l1/cmd/crawler/main.go b/l1/cmd/crawler/main.go index 44eeb7b..afea680 100644 --- a/l1/cmd/crawler/main.go +++ b/l1/cmd/crawler/main.go @@ -43,8 +43,7 @@ func init() { func main() { started := time.Now() - - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) crawler := newCrawler(depthLimit) go watchSignals(cancel, crawler) defer cancel() @@ -83,7 +82,7 @@ func watchSignals(cancel context.CancelFunc, crawler *crawler) { cancel() return case <-osSignalUserChan: - crawler.maxDepth += 10 + crawler.increaseMaxDepth(10) log.Printf("got signal %d", crawler.maxDepth) } }