2013-06-03 12 views
5

Ho scritto un po 'di codice che gli URL contemporaneamente sondaggio ogni 30 minuti:compito polling pianificata in Go

func (obj * MyObj) Poll() { 
    for ;; { 
     for _, url := range obj.UrlList { 
      //Download the current contents of the URL and do something with it 
     } 
     time.Sleep(30 * time.Minute) 
} 

//Start the routine in another function 
go obj.Poll() 

Come potrei quindi aggiungere a obj.UrlList in altre parti del codice e assicurare che la prossima volta gli URL vengono interrogati che l'UrlList nella goroutine del sondaggio è stato anch'esso aggiornato e come tale eseguirà anche il polling del nuovo URL?

Capisco che la memoria è condivisa attraverso la comunicazione piuttosto che viceversa in Go e ho studiato i canali, ma non sono sicuro di come implementarli in questo esempio.

+0

È possibile trovare [questo talk] (http://blog.golang.org/2013/05/advanced-go-concurrency-patterns.html) interessante. – nemo

risposta

8

Ecco un modello testato, ma sicuro per andare a prendere periodicamente alcuni URL con la possibilità di aggiungere dinamicamente nuovi URL alla lista di URL in modo sicuro. Dovrebbe essere ovvio al lettore cosa sarebbe necessario se si volesse rimuovere anche un URL.

type harvester struct { 
    ticker *time.Ticker // periodic ticker 
    add chan string // new URL channel 
    urls []string  // current URLs 
} 

func newHarvester() *harvester { 
    rv := &harvester{ 
     ticker: time.NewTicker(time.Minute * 30), 
     add: make(chan string), 
    } 
    go rv.run() 
    return rv 
} 

func (h *harvester) run() { 
    for { 
     select { 
     case <-h.ticker.C: 
      // When the ticker fires, it's time to harvest 
      for _, u := range h.urls { 
       harvest(u) 
      } 
     case u := <-h.add: 
      // At any time (other than when we're harvesting), 
      // we can process a request to add a new URL 
      h.urls = append(h.urls, u) 
     } 
    } 
} 

func (h *harvester) AddURL(u string) { 
    // Adding a new URL is as simple as tossing it onto a channel. 
    h.add <- u 
} 
+0

Questo ha funzionato in modo assolutamente brillante anche se è necessario cambiare h.ticker.C in <-h.ticker.C altrimenti si ottiene un errore 'valore non utilizzato'. –

+0

Fantastico, sono contento che questo abbia funzionato e che sia stato abbastanza chiaro da poter aggirare il bug (che ho appena corretto in base alle specifiche). – Dustin

1
// Type with queue through a channel. 
type MyType struct { 
    queue chan []*net.URL 
} 

func (t *MyType) poll() { 
    for urls := range t.queue { 
     ... 
     time.Sleep(30 * time.Minute) 
    } 
} 

// Create instance with buffered queue. 
t := MyType{make(chan []*net.URL, 25)} 

go t.Poll() 
+0

Presumibilmente, il sondaggio verrà comunque attivato anche se t.queue non viene aggiornato in un intervallo di trenta minuti? –

+2

In effetti, time.Sleep() non è necessario. Un intervallo su un canale si bloccherà fino a quando i valori saranno disponibili, o uscirà una volta chiuso il canale. Avrei usato un semplice chan * net.URL, anche se, invece di una slice, altrimenti avresti bisogno di un ciclo for interno (per superare la fetta ricevuta dal canale). – mna

+0

Sì, è un trucco veloce. ;) Dorme per 30 minuti tra ogni sondaggio e i blocchi con una coda vuota. Una soluzione reale userebbe selezionare internamente e un valore predefinito se la coda è vuota. Il sonno dovrebbe essere fatto in quel ramo, in modo che la ricezione del canale funzioni finché i dati sono disponibili e altrimenti l'intero ciclo fa un pisolino. – Mue

2

Se avete bisogno di polling a intervalli periodici regolari, si non uso time.Sleep ma una time.Ticker (o un parente come time.After) dovrebbe. Il motivo è che il sonno è solo un sonno e non tiene conto della deriva dovuta al vero lavoro che hai fatto nel tuo ciclo. Viceversa, un Ticker ha una goroutine separata e un canale, che insieme sono in grado di inviarti eventi regolari e quindi far sì che qualcosa di utile accada.

Ecco un esempio simile al tuo. Inserisco un jitter casuale per illustrare il vantaggio dell'uso di un Ticker.

package main 

import (
    "fmt" 
    "time" 
    "math/rand" 
) 

func Poll() { 
    r := rand.New(rand.NewSource(99)) 
    c := time.Tick(10 * time.Second) 
    for _ = range c { 
     //Download the current contents of the URL and do something with it 
     fmt.Printf("Grab at %s\n", time.Now()) 
     // add a bit of jitter 
     jitter := time.Duration(r.Int31n(5000)) * time.Millisecond 
     time.Sleep(jitter) 
    } 
} 

func main() { 
    //go obj.Poll() 
    Poll() 
} 

Quando ho eseguito questo, ho trovato che ha mantenuto un rigoroso ciclo di 10 secondi nonostante il jitter.