Home | History | Annotate | Download | only in codewalk
      1 // Copyright 2010 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package main
      6 
      7 import (
      8 	"log"
      9 	"net/http"
     10 	"time"
     11 )
     12 
     13 const (
     14 	numPollers     = 2                // number of Poller goroutines to launch
     15 	pollInterval   = 60 * time.Second // how often to poll each URL
     16 	statusInterval = 10 * time.Second // how often to log status to stdout
     17 	errTimeout     = 10 * time.Second // back-off timeout on error
     18 )
     19 
     20 var urls = []string{
     21 	"http://www.google.com/",
     22 	"http://golang.org/",
     23 	"http://blog.golang.org/",
     24 }
     25 
     26 // State represents the last-known state of a URL.
     27 type State struct {
     28 	url    string
     29 	status string
     30 }
     31 
     32 // StateMonitor maintains a map that stores the state of the URLs being
     33 // polled, and prints the current state every updateInterval nanoseconds.
     34 // It returns a chan State to which resource state should be sent.
     35 func StateMonitor(updateInterval time.Duration) chan<- State {
     36 	updates := make(chan State)
     37 	urlStatus := make(map[string]string)
     38 	ticker := time.NewTicker(updateInterval)
     39 	go func() {
     40 		for {
     41 			select {
     42 			case <-ticker.C:
     43 				logState(urlStatus)
     44 			case s := <-updates:
     45 				urlStatus[s.url] = s.status
     46 			}
     47 		}
     48 	}()
     49 	return updates
     50 }
     51 
     52 // logState prints a state map.
     53 func logState(s map[string]string) {
     54 	log.Println("Current state:")
     55 	for k, v := range s {
     56 		log.Printf(" %s %s", k, v)
     57 	}
     58 }
     59 
     60 // Resource represents an HTTP URL to be polled by this program.
     61 type Resource struct {
     62 	url      string
     63 	errCount int
     64 }
     65 
     66 // Poll executes an HTTP HEAD request for url
     67 // and returns the HTTP status string or an error string.
     68 func (r *Resource) Poll() string {
     69 	resp, err := http.Head(r.url)
     70 	if err != nil {
     71 		log.Println("Error", r.url, err)
     72 		r.errCount++
     73 		return err.Error()
     74 	}
     75 	r.errCount = 0
     76 	return resp.Status
     77 }
     78 
     79 // Sleep sleeps for an appropriate interval (dependent on error state)
     80 // before sending the Resource to done.
     81 func (r *Resource) Sleep(done chan<- *Resource) {
     82 	time.Sleep(pollInterval + errTimeout*time.Duration(r.errCount))
     83 	done <- r
     84 }
     85 
     86 func Poller(in <-chan *Resource, out chan<- *Resource, status chan<- State) {
     87 	for r := range in {
     88 		s := r.Poll()
     89 		status <- State{r.url, s}
     90 		out <- r
     91 	}
     92 }
     93 
     94 func main() {
     95 	// Create our input and output channels.
     96 	pending, complete := make(chan *Resource), make(chan *Resource)
     97 
     98 	// Launch the StateMonitor.
     99 	status := StateMonitor(statusInterval)
    100 
    101 	// Launch some Poller goroutines.
    102 	for i := 0; i < numPollers; i++ {
    103 		go Poller(pending, complete, status)
    104 	}
    105 
    106 	// Send some Resources to the pending queue.
    107 	go func() {
    108 		for _, url := range urls {
    109 			pending <- &Resource{url: url}
    110 		}
    111 	}()
    112 
    113 	for r := range complete {
    114 		go r.Sleep(pending)
    115 	}
    116 }
    117