25. Terminal project: a concurrent URL health-checker (worker pool)
Goal: Week 2's mini-project, built out in full — a worker pool with a bounded concurrency limit and an overall deadline.
package main
import (
"bufio"
"context"
"fmt"
"net/http"
"os"
"sync"
"time"
)
type Result struct {
URL string
Status int
Latency time.Duration
Err error
}
func checkOne(ctx context.Context, client *http.Client, url string) Result {
start := time.Now()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return Result{URL: url, Err: err}
}
resp, err := client.Do(req)
if err != nil {
return Result{URL: url, Err: err, Latency: time.Since(start)}
}
defer resp.Body.Close()
return Result{URL: url, Status: resp.StatusCode, Latency: time.Since(start)}
}
func worker(ctx context.Context, client *http.Client, urls <-chan string, results chan<- Result, wg *sync.WaitGroup) {
defer wg.Done()
for url := range urls {
select {
case <-ctx.Done():
return
default:
results <- checkOne(ctx, client, url)
}
}
}
func main() {
concurrency := 10
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
urls := make(chan string)
results := make(chan Result)
client := &http.Client{Timeout: 5 * time.Second}
var wg sync.WaitGroup
for i := 0; i < concurrency; i++ {
wg.Add(1)
go worker(ctx, client, urls, results, &wg)
}
go func() {
defer close(urls)
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
select {
case urls <- scanner.Text():
case <-ctx.Done():
return
}
}
}()
go func() {
wg.Wait()
close(results)
}()
for r := range results {
if r.Err != nil {
fmt.Printf("%-40s ERROR %v\n", r.URL, r.Err)
continue
}
fmt.Printf("%-40s %d %v\n", r.URL, r.Status, r.Latency.Round(time.Millisecond))
}
}
printf "https://go.dev\nhttps://golang.org\nhttp://localhost:9999\n" | go run .
The pipeline is: stdin → urls channel → N workers → results channel →
main prints as results arrive, unbuffered so results stream out
immediately rather than batching at the end. The whole batch is bounded
by one shared context.WithTimeout.