@@ -11,17 +11,30 @@ import (
11
11
12
12
func main () {
13
13
14
+ // configure the concurrency flag
14
15
concurrency := 20
15
16
flag .IntVar (& concurrency , "c" , 20 , "Set the concurrency level" )
16
17
18
+ // parse the flags
17
19
flag .Parse ()
18
20
21
+ // jobs is a channel of strings. We'll send domains on the
22
+ // channel so that a bunch of workers can receive them and
23
+ // try to resolve them
19
24
jobs := make (chan string )
20
25
26
+ // A WaitGroup is useful if you have lots of goroutines
27
+ // and you want to know when they're all done.
21
28
var wg sync.WaitGroup
29
+
30
+ // spin up a whole bunch of workers
22
31
for i := 0 ; i < concurrency ; i ++ {
32
+ // tell the waitgroup about the new worker
23
33
wg .Add (1 )
24
34
35
+ // launch a goroutine that takes domains off the
36
+ // jobs channel, tries to resolve them and outputs
37
+ // them only if there was no error
25
38
go func () {
26
39
for domain := range jobs {
27
40
_ , err := net .ResolveIPAddr ("ip4" , domain )
@@ -30,19 +43,33 @@ func main() {
30
43
}
31
44
fmt .Println (domain )
32
45
}
46
+
47
+ // when the jobs channel is closed the loop
48
+ // above will stop; then we need to tell the
49
+ // waitgroup that the worker is done
33
50
wg .Done ()
34
51
}()
35
52
}
36
53
54
+ // open stdin as a scanner. That makes it super easy
55
+ // to deal with line-delimited input
37
56
sc := bufio .NewScanner (os .Stdin )
38
57
for sc .Scan () {
58
+ // send each line (a domain) on the jobs channel
39
59
jobs <- sc .Text ()
40
60
}
61
+
62
+ // as soon as we're done sending all the jobs we can
63
+ // close the jobs channel. If we don't the workers
64
+ // will never stop.
41
65
close (jobs )
42
66
67
+ // check there were no errors reading stdin (unlikely)
43
68
if err := sc .Err (); err != nil {
44
69
fmt .Fprintf (os .Stderr , "failed to read input: %s\n " , err )
45
70
}
71
+
72
+ // wait for the workers to finish doing their thing
46
73
wg .Wait ()
47
74
48
75
}
0 commit comments