2016-01-07 9 views
5

私は呼び出した関数を、後でアクセスするためにキューに入れてレートを制限しようとしています。以下は、私が作成したリクエストのスライスを示しています。また、requestHandler関数は、各リクエストを一定の割合で処理します。Go:チャンネルを介して関数を渡す

異なる種類のパラメータを持つすべての種類の関数を受け入れるようにしたいので、interface {}型を使用します。

チャネルを介して関数を渡して正常に呼び出すことはできますか?ここで

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) 
    } 
} 

は私が(GetLeagueEntries(文字列、文字列)とGetSummonerName(int型、int型)が機能している)を達成しようとしているものの例である:

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

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

は、なぜあなたは、関数呼び出しの速度を制限したいですか?実際の制限されたリソース(ネットワーク、ディスクなど)へのレート制限アクセスは実装するのが簡単かもしれません。また、https://godoc.org/golang.org/x/time/rate – kostya

+0

を見てください。これらの関数呼び出しは、APIに結びついた外部ライブラリ呼び出しなのでです。私は自分の機能を制限する必要があります。 – tyuo9980

答えて

0

さてさて、ここcodezは次のようになります。それは完璧ではないことをhttps://play.golang.org/p/XZvb_4BaJF

注意してください。毎秒実行されるキューがあります。キューが空で新しいアイテムが追加された場合、新しいアイテムは実行されるまでに約1秒待つことができます。

しかし、これはあなたがとにかく必要なものにあなたが非常に近い取得する必要:)

このコードは、3セクションに分割することができます。

  1. 私は、サーバーを呼び出すレート制限されたキューキュータ、(私は物事に名前をつけることに恐ろしいです) - サーバは機能について何も知らない。それは、1秒に1回、キュー内の最も古い関数をポップし、それを呼び出す無限のゴルーチンを開始することです。私が上で話した問題は、コードのこのセクションにあります。あなたが望むなら、あなたがそれを修正するのを助けることができます。
  2. ボタンクリック機能 - これは、サーバーを使用して各ボタンクリックがどのように3つの差分関数(関数呼び出しを多かれ少なかれ行うことができる)を呼び出すことができ、それらがお互いに1秒ごとに離れていることを示しています。これらの機能のいずれかにタイムアウトを追加することもできます(レイテンシを偽るため)、1秒ごとに呼び出されます。これは、すべての関数呼び出しをできるだけ速くしたいので、チャネルを必要とする唯一の場所です(最初の関数が5秒かかり、2番目の関数を呼び出すために1秒間待つだけです)。あなたがすべて終わった時を知る必要があるので、完了してください。
  3. ボタンをクリックしてシミュレーション(メインFUNC) - これは単なる予想通り3回のボタンのクリックがうまくいくことを示しています。また、3人のユーザーがボタンを同時にクリックするのをシミュレートするためにそれらをゴルーチンに入れることもできます。

    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

私はそれが簡単に思っているだろう何らかのセマフォやワーカープールを使用するそうすれば、あなたは何でもできる人の数が限られています。複数のワーカープールを持つことも可能です。

同時/非同期でこれらの呼び出しが必要ですか?そうでなければ、あなたは設定可能な睡眠(厄介なハック心)を持つために呼び出すことができます。

機能ではなく、ワーカー・プールまたはセマフォを試用してください。

+0

主な問題は、私のAPIコールのレートを制限する方法を見つけることですが、これらの同時コールを遅らせる方法を見つけ出すことは困難です。 – tyuo9980

2

まず、私はとしてそれを記述します。

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

そして、サーバーにレート制限を置きます。レート制限ライブラリの1つ。

しかし、それは要求を統一するためにインターフェースを使用することが可能であり、そして(http.HandleFuncのように)クロージャを可能にするために、FUNCタイプを使用:もちろん

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 
}) 

、これはいくつかの同期を必要とします。

関連する問題