2016-01-07 19 views
5

Sto provando a valutare il limite delle funzioni che chiamo ponendole attraverso una coda per l'accesso in seguito. Sotto ho una sezione di richieste che ho creato e la funzione requestHandler elabora ogni richiesta ad una determinata velocità.Vai: passaggio delle funzioni attraverso i canali

Voglio che accetti tutti i tipi di funzioni con diversi tipi di parametri, da qui il tipo di interfaccia {}.

Come potrei passare le funzioni attraverso un canale e chiamarle con successo?

type request struct { 
    function interface{} 
    channel chan interface{} 
} 

var requestQueue []request 

func pushQueue(f interface{}, ch chan interface{}) { 
    req := request{ 
     f, 
     ch, 
    } 

    //push 
    requestQueue = append(requestQueue, req) 
} 

func requestHandler() { 
    for { 
     if len(requestQueue) > 0 { 
      //pop 
      req := requestQueue[len(requestQueue)-1] 
      requestQueue = requestQueue[:len(requestQueue)-1] 

      req.channel <- req.function 
     } 

     <-time.After(1200 * time.Millisecond) 
    } 
} 

Ecco un esempio di quello che sto cercando di realizzare (GetLeagueEntries (string, string) e GetSummonerName (int, int) sono funzioni):

ch := make(chan interface{}) 
    pushQueue(l.GetLeagueEntries, ch) 
    pushQueue(l.GetSummonerName, ch) 

    leagues, _ := <-ch(string1, string2) 
    summoners, _ := <-ch(int1, int2) 
+1

Perché si vuole limitare tasso delle chiamate di funzione? la limitazione dell'accesso alla risorsa reale limitata (come netwrok, disco, ecc.) potrebbe essere più facile da implementare. Dai anche un'occhiata a https://godoc.org/golang.org/x/time/rate – kostya

+0

Perché queste chiamate di funzione sono chiamate di librerie esterne legate a un'API. Devo limitare le mie funzioni. – tyuo9980

risposta

0

Va bene, qui è la codez: https://play.golang.org/p/XZvb_4BaJF

Si noti che non è perfetto. Hai una coda che viene eseguita ogni secondo. Se la coda è vuota e viene aggiunto un nuovo elemento, il nuovo elemento può attendere quasi un secondo prima di essere eseguito.

Ma questo dovrebbe farti molto vicino a ciò che è necessario in ogni caso :)

Questo codice può essere suddiviso in 3 sezioni:

  1. L'esecutore tasso di coda limitata, che io chiamo il server (Sono orribile nel nominare le cose) - Il server non sa nulla delle funzioni. Tutto ciò che fa è iniziare una goroutine infinita che apre la funzione più vecchia in coda, una volta al secondo, e la chiama. Il problema di cui ho parlato sopra è in questa sezione del codice BTW e potrei aiutarti a risolverlo se vuoi.
  2. La funzionalità Click del pulsante - Questo mostra come ogni clic del pulsante può chiamare 3 funzioni diff (è possibile ovviamente effettuare più/meno chiamate di funzione) utilizzando il server e assicurarsi che siano ogni 1 secondo l'uno dall'altro. È anche possibile aggiungere un timeout a una qualsiasi delle funzioni (per simulare una latenza) e verrebbero comunque richiamati a distanza di 1 secondo.Questo è l'unico posto in cui hai bisogno di canali perché vuoi fare tutte le chiamate di funzione il più velocemente possibile (se la prima funzione impiega 5 secondi, vuoi solo aspettare 1 secondo per chiamare la seconda funzione) e poi aspettare che finisci quindi devi sapere quando sono finiti.
  3. La simulazione del pulsante (la funzione principale): mostra solo che i clic sui 3 pulsanti funzionano come previsto. Puoi anche metterli in una goroutine per simulare 3 utenti facendo clic sul pulsante allo stesso tempo e funzionerebbe ancora.

    package main 
    
    import (
        "fmt" 
        "sync" 
        "time" 
    ) 
    
    const (
        requestFreq = time.Second 
    ) 
    
    type (
        // A single request 
        request func() 
    
        // The server that will hold a queue of requests and make them once a requestFreq 
        server struct { 
         // This will tick once per requestFreq 
         ticker  *time.Ticker 
    
         requests []request 
         // Mutex for working with the request slice 
         sync.RWMutex 
        } 
    ) 
    
    var (
        createServerOnce sync.Once 
        s *server 
    ) 
    
    func main() { 
        // Multiple button clicks: 
        ButtonClick() 
        ButtonClick() 
        ButtonClick() 
    
        fmt.Println("Done!") 
    } 
    
    
    
    
    
    
    // BUTTON LOGIC: 
    
    // Calls 3 functions and returns 3 diff values. 
    // Each function is called at least 1 second appart. 
    func ButtonClick() (val1 int, val2 string, val3 bool) { 
        iCh := make(chan int) 
        sCh := make(chan string) 
        bCh := make(chan bool) 
    
        go func(){ 
         Server().AppendRequest(func() { 
          t := time.Now() 
          fmt.Println("Calling func1 (time: " + t.Format("15:04:05") + ")") 
          // do some stuff 
          iCh <- 1 
         }) 
        }() 
        go func(){ 
         Server().AppendRequest(func() { 
          t := time.Now() 
          fmt.Println("Calling func2 (time: " + t.Format("15:04:05") + ")") 
          // do some stuff 
          sCh <- "Yo" 
         }) 
        }() 
        go func(){ 
         Server().AppendRequest(func() { 
          t := time.Now() 
          fmt.Println("Calling func3 (time: " + t.Format("15:04:05") + ")") 
          // do some stuff 
          bCh <- true 
         }) 
        }() 
    
        // Wait for all 3 calls to come back 
        for count := 0; count < 3; count++ { 
         select { 
         case val1 = <-iCh: 
         case val2 = <-sCh: 
         case val3 = <-bCh: 
         } 
        } 
    
        return 
    } 
    
    
    
    
    
    // SERVER LOGIC 
    
    // Factory function that will only create a single server 
    func Server() *server { 
        // Only one server for the entire application 
        createServerOnce.Do(func() { 
         s = &server{ticker: time.NewTicker(requestFreq), requests: []request{}} 
    
         // Start a thread to make requests. 
         go s.makeRequests() 
        }) 
        return s 
    } 
    func (s *server) makeRequests() { 
        if s == nil || s.ticker == nil { 
         return 
        } 
    
        // This will keep going once per each requestFreq 
        for _ = range s.ticker.C { 
    
         var r request 
    
         // You can't just access s.requests because you are in a goroutine 
         // here while someone could be adding new requests outside of the 
         // goroutine so you have to use locks. 
         s.Lock() 
         if len(s.requests) > 0 { 
          // We have a lock here, which blocks all other operations 
          // so just shift the first request out, save it and give 
          // the lock back before doing any work. 
          r = s.requests[0] 
          s.requests = s.requests[1:] 
         } 
         s.Unlock() 
    
         if r != nil { 
          // make the request! 
          r() 
         } 
        } 
    } 
    func (s *server) AppendRequest(r request) { 
        if s == nil { 
         return 
        } 
        s.Lock() 
        s.requests = append(s.requests, r) 
        s.Unlock() 
    } 
    
0

avrei pensato più facile utilizzare una sorta di semaforo o pool di lavoratori. In questo modo hai un numero limitato di lavoratori che possono fare qualsiasi cosa. Sarebbe anche possibile avere più pool di worker.

Avete bisogno di una di queste chiamate per essere simultanee/asincrone? In caso contrario, e possono essere chiamati in ordine si potrebbe avere il sonno configurabile (una brutta mente hack).

Prova un pool di lavoratori o semaforo piuttosto che un chan di funzioni.

+0

Il problema principale è trovare un modo per limitare il limite delle chiamate a api ma sto riscontrando problemi nel trovare un modo per ritardare queste chiamate simultanee. – tyuo9980

2

In primo luogo, vorrei scrivere come:

leagues := server.GetLeagueEntries() 
summoners := server.GetSummoners() 

E, mettere il fattore limitante nel server. Con una delle librerie che limitano la velocità.

Tuttavia, è possibile utilizzare un'interfaccia di unificare le richieste, e utilizzare un tipo FUNC per consentire chiusure (come in http.HandleFunc):

type Command interface { 
    Execute(server *Server) 
} 

type CommandFunc func(server *Server) 
func (fn CommandFunc) Execute(server *Server) { fn(server) } 

type GetLeagueEntries struct { Leagues []League } 

func (entries *GetLeagueEntries) Execute(server *Server) { 
    // ... 
} 

func GetSummonerName(id int, result *string) CommandFunc { 
    return CommandFunc(func(server *Server){ 
     *result = "hello" 
    }) 
} 

get := GetLeagueEnties{} 
requests <- &get 

requests <- CommandFunc(func(server *Server){ 
    // ... handle struff here 
}) 

Naturalmente, questo bisogno di qualche sincronizzazione.