2016-04-26 4 views
4

私は比較的新しいので、同時にREST APIから情報を引き出す最良の方法を見つけようとしています。その目的は、APIへの複数の同時呼び出しを行い、それぞれの呼び出しが異なるタイプのデータを返すことです。複数のタイプのゴルーチンとチャンネル

私は現在持っている:

s := NewClient() 
c1 := make(chan map[string]Service) 
c2 := make(chan map[string]ServicePlan) 
c3 := make(chan map[string]ServiceInstance) 
c4 := make(chan map[string]ServiceBinding) 
c5 := make(chan map[string]Organization) 
c6 := make(chan map[string]Space) 

go func() { 
    c1 <- GetServices(s) 
}() 

go func() { 
    c2 <- GetServicePlans(s) 
}() 

go func() { 
    c3 <- GetServiceInstances(s) 
}() 

go func() { 
    c4 <- GetServiceBindings(s) 
}() 

go func() { 
    c5 <- GetOrganizations(s) 
}() 

go func() { 
    c6 <- GetSpaces(s) 
}() 

services := <- c1 
servicePlans := <- c2 
serviceInstances := <- c3 
serviceBindings := <- c4 
orgs := <- c5 
spaces := <- c6 
// stitch all the data together later 

が、これを書くためのより良い方法があった場合、私は思っていました。

EDIT:それはまだ醜いですが、一つにチャンネル数を削減:

c := make(chan interface{}) 

var (
    services  map[string]Service 
    servicePlans  map[string]ServicePlan 
    serviceInstances map[string]ServiceInstance 
    serviceBindings map[string]ServiceBinding 
    orgs   map[string]Organization 
    spaces  map[string]Space 
) 

go func() { 
    c <- GetServices(s) 
}() 

go func() { 
    c <- GetServicePlans(s) 
}() 

go func() { 
    c <- GetServiceInstances(s) 
}() 

go func() { 
    c <- GetServiceBindings(s) 
}() 

go func() { 
    c <- GetOrganizations(s) 
}() 

go func() { 
    c <- GetSpaces(s) 
}() 

for i := 0; i < 6; i++ { 
    v := <-c 
    switch v := v.(type) { 
    case map[string]Service: 
     services = v 
    case map[string]ServicePlan: 
     servicePlans = v 
    case map[string]ServiceInstance: 
     serviceInstances = v 
    case map[string]ServiceBinding: 
     serviceBindings = v 
    case map[string]Organization: 
     orgs = v 
    case map[string]Space: 
     spaces = v 
    } 
} 

私はまだ実際にこれを行う方法のようなので、私はループニーズことをハードコードする必要はありませんでしょう6回走る私は実際に実行するための関数のリストを作成し、それを繰り返すことを繰り返しましたgo func呼び出しがすべての関数が異なる戻り値の型を持っているので、私はすべての型の不一致のエラーを持っていますし、func(api) interface{}ランタイムパニックが発生するだけです。

+0

あなたはチャンネルを使い過ぎているようです。リクエストがすでにコンカレントで、それぞれが1つのタイプの結果を返す場合、結果を受け取るためにさらに多くのゴルーチンとチャンネルが必要なのはなぜですか? – JimB

+0

それはトリックですが、それぞれが異なるタイプの結果を返します –

+0

クロージャで値を割り当てることができます:http://play.golang.org/p/Sak5QGCPZi、または以下に示唆したようにして汎用インターフェース{ }任意の型を受け取ります。 – JimB

答えて

5

私はこれを見たとき、私たちはこのように種類ごとに一つのチャネルを作成、完了に割り当てをconflatingことができると思います。

割り当てごとに1つのクロージャを作成し、完了を管理する1つのチャネルを作成する方が簡単かもしれません。

例:

s := NewClient() 
c := make(chan bool) 
// I don't really know the types here 
var services services 
var servicePlans servicePlans 
var serviceInstances serviceInstances 
var serviceBindings serviceInstances 
var orgs orgs 
var spaces spaces 

go func() { 
    service = GetServices(s) 
    c <- true 
}() 

go func() { 
    servicePlans = GetServicePlans(s) 
    c <- true 
}() 

go func() { 
    serviceInstances = GetServiceInstances(s) 
    c <- true 
}() 

go func() { 
    serviceBindings = GetServiceBindings(s) 
    c <- true 
}() 

go func() { 
    orgs = GetOrganizations(s) 
    c <- true 
}() 

go func() { 
    spaces = GetSpaces(s) 
    c <- true 
}() 

for i = 0; i < 6; i++ { 
    <-c 
} 
// stitch all the data together later 

囲碁著者は、このユースケースを予想し、同期を提供しています。これを少し明確にするWaitGroup sync.WaitGroup Docsこれは、チャネル同期を置き換えるファンシーなアトミック操作です。

例:

s := NewClient() 
// again, not sure of the types here 
var services services 
var servicePlans servicePlans 
var serviceInstances serviceInstances 
var serviceBindings serviceInstances 
var orgs orgs 
var spaces spaces 

var wg sync.WaitGroup 
wg.Add(6) 

go func() { 
    service = GetServices(s) 
    wg.Done() 
}() 

go func() { 
    servicePlans = GetServicePlans(s) 
    wg.Done() 
}() 

go func() { 
    serviceInstances = GetServiceInstances(s) 
    wg.Done() 
}() 

go func() { 
    serviceBindings = GetServiceBindings(s) 
    wg.Done() 
}() 

go func() { 
    orgs = GetOrganizations(s) 
    wg.Done() 
}() 

go func() { 
    spaces = GetSpaces(s) 
    wg.Done() 
}() 

// blocks until all six complete 
wg.Wait() 
// stitch all the data together later 

私はこのことができますです願っています。

+0

これは私がこれまで見てきた最も完全な例だと思います。私はWaitGroupの例を使用して終了し、それは完全に動作します。 –

+0

私はここで待機グループの使用が好きです。しかし、あなた自身をやり直す方法はありますか?関数のスライスを作成し、それをループするのと同じですか? – Breedly

4

interface{}の1つのチャネルを作成して、値を送信する方が簡単な場合があります。次に、受信側では、特定のタイプのタイプアサーションを実行できます。

c := make(chan interface{}) 

/* Sending: */ 
c <- 42 
c <- "test" 
c <- &ServicePlan{} 

/* Receiving */ 
something := <-c 
switch v := something.(type) { 
case int:   // do something with v as an int 
case string:  // do something with v as a string 
case *ServicePlan: // do something with v as an instance pointer 
default: 
} 
+0

私は同様のアプローチを考えていましたが、私が見ている問題は、応答の1つが戻ってくるのを待つだけで、すべてではありません。私はスイッチが何らかのforループに入る必要があるという前提のもとで、そのループがいつ壊れるべきかを知る方法を持っていました。 –

+0

@RossPeoples複数の値を受け取る必要がある場合は、受信/切り替えコードを 'for {}'ループに入れることができます。 –

+0

@RossPeoples、forの周りにスイッチをラップする必要はありません。 https://golang.org/ref/spec#Break_statements: "break"ステートメントは、同じ関数内の最も内側の "for"、 "switch"、または "select"ステートメントの実行を終了します。 –

0

あなたはアプローチはほとんど免責事項で意味があります。端部では、あなたは、順次

services := <- c1 
servicePlans := <- c2 
serviceInstances := <- c3 
serviceBindings := <- c4 

チャンネルから受け取るが、これはあなたのコードの同期を行う

ため
go func() { 
    c2 <- GetServicePlans(s) 
}() 

読んでいました限り、反対側をC2に書き込めません、それは前にいないだろうことができますservices := <- c1が実行されます。だから、ゴルーチンにもかかわらず、コードは順番に実行されます。 GetServices(s)は、チャネルでの作業とバッファ結果だん - コードより多くの非同期を作るための最も簡単な方法は、少なくともC1 <ができるように、バッファチャンネル

c1 := make(chan map[string]Service, 1) //buffered channel with capacity one 

を提供することです。

+0

あなたは正しいです。複数のチャネルを使用すると、いくつかの問題が発生する可能性があります私は自分の答えを変更してうまくいきましたが、今はWaitGroup版を使用すると思います。同期の問題を指摘してくれてありがとう! –

0

chan interface{]アプローチの代わりに、送信する真の値を指すようにエンベロープオブジェクト(以下の例ではAPIObject)を作成することを検討することもできます。これにはメモリオーバーヘッドが伴いますが、タイプセーフティも強化されています。 (コメント二つのアプローチを計量意見がはるかに高く評価されるであろう。)

https://play.golang.org/p/1KIZ7Qfg43

package main 

import (
    "fmt" 
) 

type Service struct{ Name string } 
type ServicePlan struct{ Name string } 
type Organization struct{ Name string } 

type APIObject struct { 
    Service  *Service 
    ServicePlan *ServicePlan 
    Organization *Organization 
} 

func main() { 
    objs := make(chan APIObject) 

    go func() { 
     objs <- APIObject{Service: &Service{"Service-1"}} 
     objs <- APIObject{ServicePlan: &ServicePlan{"ServicePlan-1"}} 
     objs <- APIObject{Organization: &Organization{"Organization-1"}} 
     close(objs) 
    }() 

    for obj := range objs { 
     if obj.Service != nil { 
      fmt.Printf("Service: %v\n", obj.Service) 
     } 
     if obj.ServicePlan != nil { 
      fmt.Printf("ServicePlan: %v\n", obj.ServicePlan) 
     } 
     if obj.Organization != nil { 
      fmt.Printf("Organization: %v\n", obj.Organization) 
     } 
    } 
} 
関連する問題