From dc000eb40f535c8ce20230bc4474bf1735848436 Mon Sep 17 00:00:00 2001 From: Jian Han Date: Sat, 6 Jan 2018 15:55:04 +1000 Subject: [PATCH] another package of fetchers --- .../divide_and_conquer/divide_and_conquer.go | 8 ++ concurrency/subtasks/fetchers/fetchers.go | 91 +++++++++++++++++++ concurrency/subtasks/main.go | 13 +-- 3 files changed, 102 insertions(+), 10 deletions(-) diff --git a/concurrency/subtasks/divide_and_conquer/divide_and_conquer.go b/concurrency/subtasks/divide_and_conquer/divide_and_conquer.go index 0c60ae5..a222445 100644 --- a/concurrency/subtasks/divide_and_conquer/divide_and_conquer.go +++ b/concurrency/subtasks/divide_and_conquer/divide_and_conquer.go @@ -9,6 +9,14 @@ import ( "github.com/davecgh/go-spew/spew" ) +// https://medium.com/capital-one-developers/buffered-channels-in-go-what-are-they-good-for-43703871828 + +// One common pattern for goroutines is fan-out. When you want to apply the same data to multiple algorithms, +// you can launch a goroutine for each subtask, and then gather the data back in when they are done. +// For example, you might want to process the same data via multiple scoring algorithms and return back +// all of the scores or pull data from multiple microservices to compose a single page. A buffered channel is an +// ideal way to gather the data back from your subtasks. + func RunDivideAndConquer() { type in struct { a int diff --git a/concurrency/subtasks/fetchers/fetchers.go b/concurrency/subtasks/fetchers/fetchers.go index cf1c9f0..4f04567 100644 --- a/concurrency/subtasks/fetchers/fetchers.go +++ b/concurrency/subtasks/fetchers/fetchers.go @@ -1 +1,92 @@ package fetchers + +import ( + "fmt" + "time" + + "github.com/davecgh/go-spew/spew" +) + +type Fetcher interface { + Fetch(url string) (string, error) + GetName() string +} + +type GoogleFetcher struct { + Name string +} + +func (g *GoogleFetcher) Fetch(url string) (string, error) { + return fmt.Sprintf("%s is fetching %s", g.Name, url), nil +} + +func (g *GoogleFetcher) GetName() string { + return g.Name +} + +func NewGoogleFetcher(name string) *GoogleFetcher { + return &GoogleFetcher{Name: name} +} + +type BingFetcher struct { + Name string +} + +func (b *BingFetcher) Fetch(url string) (string, error) { + return fmt.Sprintf("%s is fetching %s", b.Name, url), nil +} + +func (b *BingFetcher) GetName() string { + return b.Name +} + +func NewBingFetcher(name string) *BingFetcher { + return &BingFetcher{Name: name} +} + +type DuckDuckGoFetcher struct { + Name string +} + +func (d *DuckDuckGoFetcher) Fetch(url string) (string, error) { + return fmt.Sprintf("%s is fetching %s", d.Name, url), nil +} + +func (d *DuckDuckGoFetcher) GetName() string { + return d.Name +} + +func NewDuckDuckGoFetcherFetcher(name string) *DuckDuckGoFetcher { + return &DuckDuckGoFetcher{Name: name} +} +func FetchResults(url string, fetchers []Fetcher, timeout time.Duration) ([]string, []error) { + chStr := make(chan string) + chErr := make(chan error) + for _, f := range fetchers { + go func(f Fetcher) { + s, err := f.Fetch(url) + if err != nil { + chErr <- err + } else { + chStr <- s + } + }(f) + } + stringResults := []string{} + errorResults := []error{} + for range fetchers { + select { + case s := <-chStr: + stringResults = append(stringResults, s) + case e := <-chErr: + errorResults = append(errorResults, e) + } + } + return stringResults, errorResults +} + +func RunFetchers() { + fetchers := []Fetcher{NewGoogleFetcher("Google"), NewGoogleFetcher("Bing"), NewGoogleFetcher("Duck Duck Go")} + r, e := FetchResults("http://www.abc.com", fetchers, time.Millisecond*100) + spew.Dump(r, e) +} diff --git a/concurrency/subtasks/main.go b/concurrency/subtasks/main.go index 13d1ef6..ab8f7d2 100644 --- a/concurrency/subtasks/main.go +++ b/concurrency/subtasks/main.go @@ -1,15 +1,8 @@ package main -import "github.com/jianhan/go-patterns/concurrency/subtasks/divide_and_conquer" - -// https://medium.com/capital-one-developers/buffered-channels-in-go-what-are-they-good-for-43703871828 - -// One common pattern for goroutines is fan-out. When you want to apply the same data to multiple algorithms, -// you can launch a goroutine for each subtask, and then gather the data back in when they are done. -// For example, you might want to process the same data via multiple scoring algorithms and return back -// all of the scores or pull data from multiple microservices to compose a single page. A buffered channel is an -// ideal way to gather the data back from your subtasks. +import "github.com/jianhan/go-patterns/concurrency/subtasks/fetchers" func main() { - divide_and_conquer.RunDivideAndConquer() + // divide_and_conquer.RunDivideAndConquer() + fetchers.RunFetchers() }