We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.

This lesson's interactive features are locked, please to keep using them

Concurrency

Why write a web crawler with Go and not use concurrency? Let's speed this bad boy up with some goroutines!

To coordinate the goroutines, we'll use:

  • A Mutex to protect the shared pages map.
  • A buffered channel of empty structs to limit concurrent requests. Each request sends a token when it starts and receives one when it finishes. Sends block when the buffer is full.
  • A WaitGroup to keep main from exiting until all requests finish.

Assignment

  1. type config struct {
        pages              map[string]PageData
        baseURL            *url.URL
        mu                 *sync.Mutex
        concurrencyControl chan struct{}
        wg                 *sync.WaitGroup
    }
    
  2. func (cfg *config) crawlPage(rawCurrentURL string)
    
    • Fetch each normalized URL at most once, even when multiple goroutines discover it.
    • Store the result of extractPageData for each successfully fetched page, using the current page's URL, and crawl its OutgoingLinks.
    • Keep access to the shared pages map safe with the mutex.

When you're satisfied with the results, you can move on.

Tips

I created this method to call as a helper inside of crawlPage:

func (cfg *config) addPageVisit(normalizedURL string) (shouldVisit bool)
  • Make sure you're not crawling the same page multiple times. That's why my addPageVisit method returns a boolean: to indicate whether we should crawl the page.
  • I defer a wg.Done() and a receive from the (<-cfg.concurrencyControl) channel. This ensures that the wg is decremented and the channel is emptied even when the goroutine errors and returns early.
  • Make sure you wg.Add before you spawn a goroutine, but that you send into the channel after but still at the start of a new goroutine.
  • Because all the recursive calls share a wait group, you should only need to wg.Wait() once in the main function.