successful go program design
play

Successful Go program design Six years on S T O B O R - PowerPoint PPT Presentation

Successful Go program design Six years on S T O B O R Successful Go program design Six years on My background + My background C++ + My background C++ + 2009 My background C++ Go +


  1. Externalize communication type bar struct { toBaz chan<- event // ... c := make(chan event) } ⟶ bar := newBar(c, ...) type baz struct { baz := newBaz(c, ...) fromBar <-chan event // ... }

  2. Externalize communication type bar struct { toBaz chan<- event // ... c := make(chan event) } ⟶ bar := newBar(c, ...) type baz struct { baz := newBaz(c, ...) fromBar <-chan event // ... }

  3. X. Concurrency patterns

  4. Channels are bad?

  5. Channels are bad? NO

  6. Channels are fine • Sharing memory between goroutines – use a mutex • Orchestrating goroutines – use channels • "Channels orchestrate; mutexes serialize." • go-proverbs .github.io

  7. Good uses for a channel semaphore := make(chan struct {} , 3 ) for i := 0; i < 1000; i++ { go func() { semaphore <- struct {}{} defer func() { <- semaphore } () // process } () }

  8. Good uses for a channel resultc := make(chan int, n) // Scatter for i := 0; i < n; i++ { go func() { resultc <- process() } () } // Gather for i := 0; i < n; i++ { fmt.Println(<- resultc ) }

  9. Good uses for a channel func (f *foo) loop() { func (f *foo) set(k, v string) { for { f.setc <- setReq { k, v } select { } case req := <- f.setc : f.m[req.k] = req.v func (f *foo) get(k string) string { req := getReq { k, make(chan string) } case req := <- f.getc : f.getc <- req req.res <- f.m[req.k] return <-req.res } case <- f.quitc : return func (f *foo) stop() { } close( f.quitc ) } } }

  10. Good uses for a channel func (f *foo) set(k, v string) { f.actionc <- func() { f.m[k] = v func (f *foo) loop() { } for { } select { case fn := <- f.actionc : func (f *foo) get(k string) (v string) { fn() done := make(chan struct {} ) f.actionc <- func() { case <-f.quitc: v = f.m[k] return close(done) } } } <-done } return v }

  11. Good uses for a channel func (f *foo) set(k, v string) { f.actionc <- func() { f.m[k] = v func (f *foo) loop() { } for { } select { case fn := <- f.actionc : func (f *foo) get(k string) (v string) { fn() TOP done := make(chan struct {} ) TIP f.actionc <- func() { case <-f.quitc: v = f.m[k] return close(done) } } } <-done } return v }

  12. Bad uses for a channel type foo struct { m map[string]string setc chan setReq getc chan getReq quitc chan struct {} }

  13. Bad uses for a channel type foo struct { m map[string]string mtx sync.RWMutex }

  14. Bad uses for a channel func iterator() (<-chan string) { // ... }

  15. Bad uses for a channel func iterator( cancel <-chan struct {} ) (<-chan string) { // ... }

  16. Bad uses for a channel func iterator() (results <-chan string, cancel chan<- struct {} ) { // ... }

  17. Bad uses for a channel func iterator(results chan<- string, cancel <-chan struct {} ) { // ... }

  18. Bad uses for a channel func iterator(f func(item) error) { // ... }

  19. Bad uses for a channel TOP func iterator(f func(item) error) { TIP // ... }

  20. Construction foo, err := newFoo(*fooKey, fooConfig { Bar: bar, Baz: baz, Period: 100 * time.Millisecond, } ) if err != nil { log.Fatal(err) } defer foo.close()

  21. Be explicit foo, err := newFoo(*fooKey, fooConfig { Bar: bar, Baz: baz, Period: 100 * time.Millisecond, } ) if err != nil { log.Fatal(err) } defer foo.close()

  22. Be explicit foo, err := newFoo(*fooKey, fooConfig { Bar: bar, DEPENDENCIES Baz: baz, Period: 100 * time.Millisecond, } ) if err != nil { log.Fatal(err) } defer foo.close()

  23. MAKE DEPENDENCIES TOP TOP TIP TIP EXPLICIT

  24. Dependencies func (f *foo) process() { fmt.Fprintf(f.Output, "beginning\n") result := f.Bar.compute() log.Printf("bar: %v", result) // ... }

  25. Dependencies func (f *foo) process() { fmt.Fprintf(f.Output, "beginning\n") result := f.Bar.compute() log.Printf("bar: %v", result) // ... }

  26. Dependencies func (f *foo) process() { Not a dependency fmt.Fprintf(f.Output, "beginning\n") D e p e n d e n c y result := f.Bar.compute() log.Printf("bar: %v", result) Dependency // ... }

  27. Dependencies func (f *foo) process() { fmt.Fprintf(f.Output, "beginning\n") result := f.Bar.compute() f.Logger .Printf("bar: %v", result) // ... }

  28. Dependencies func (f *foo) process() { fmt.Fprintf(f.Output, "beginning\n") result := f.Bar.compute() TOP f.Logger .Printf("bar: %v", result) TIP // ... }

  29. Dependencies foo, err := newFoo(*fooKey, fooConfig { Bar: bar, Baz: baz, Period: 100 * time.Millisecond, Logger: log.NewLogger(dst, ...) , } ) if err != nil { log.Fatal(err) } defer foo.close()

  30. Dependencies func newFoo(..., cfg fooConfig) *foo { if cfg.Output == nil { cfg.Output = ioutil.Discard } if cfg.Logger == nil { cfg.Logger = log.NewLogger(ioutil.Discard, ...) } // ... }

  31. MAKE DEPENDENCIES TOP TOP TIP TIP EXPLICIT

  32. 5. Logging and instrumentation

  33. Logging • More expensive than you think • Actionable info only – read by humans or consumed by machines • Avoid many levels – info+debug is fine • Use structured logging – key=val • Loggers are dependencies, not globals!

  34. Instrumentation • Cheaper than you think • Instrument every significant component of your system • Resource – Utilization, Saturation, Error count (USE, Brendan Gregg) • Endpoint – Request rate, Error rate, Duration (RED, Tom Wilkie) • Use Prometheus • Metrics are dependencies, not globals!

  35. Logging and instrumentation • blog.raintank.io/logs-and-metrics-and-graphs-oh-my • bit.ly/ GoLogsAndMetrics • peter.bourgon.org/blog/2016/02/07/logging-v-instrumentation.html • bit.ly/ GoLoggingVsInstrumentation

  36. Global state • log.Print uses a fixed, global log.Logger • http.Get uses a fixed, global http.Client • database/sql uses a fixed, global driver registry • func init exists only to have side effects on package-global state

  37. Global state • log.Print uses a fixed, global log.Logger • http.Get uses a fixed, global http.Client • database/sql uses a fixed, global driver registry • func init exists only to have side effects on package-global state

  38. Eliminate implicit global deps func foo() { resp, err := http.Get("http://zombo.com") // ... }

  39. Eliminate implicit global deps func foo( client *http.Client ) { resp, err := client .Get("http://zombo.com") // ... }

  40. Eliminate implicit global deps func foo( doer Doer ) { req, _ := http.NewRequest("GET", "http://zombo.com", nil) resp, err := doer.Do (req) // ... }

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend