| Go: code that grows with grace |
| |
| Andrew Gerrand |
| Google Sydney |
| http://andrewgerrand.com |
| @enneff |
| https://go.dev |
| |
| * Video |
| |
| A video of this talk was recorded at Øredev in Malmö, Sweden in November 2012. |
| |
| .link http://vimeo.com/53221560 Watch the talk on Vimeo |
| |
| * Go |
| |
| You may have heard of Go. |
| |
| It's my favorite language. I think you'll like it, too. |
| |
| * What is Go? |
| |
| An open source (BSD licensed) project: |
| |
| - Language specification, |
| - Small runtime (garbage collector, scheduler, etc), |
| - Two compilers (`gc` and `gccgo`), |
| - 'Batteries included' standard library, |
| - Tools (build, fetch, test, document, profile, format), |
| - Documentation. |
| |
| As of September 2012 we have more than 300 contributors. |
| |
| * Go is about composition |
| |
| Go is Object Oriented, but not in the usual way. |
| |
| - no classes (methods may be declared on any type) |
| - no subtype inheritance |
| - interfaces are satisfied implicitly (structural typing) |
| |
| The result: simple pieces connected by small interfaces. |
| |
| * Go is about concurrency |
| |
| Go provides CSP-like concurrency primitives. |
| |
| - lightweight threads (goroutines) |
| - typed thread-safe communication and synchronization (channels) |
| |
| The result: comprehensible concurrent code. |
| |
| * Go is about gophers |
| |
| .image chat/gophers.jpg |
| |
| * Core values |
| |
| Go is about composition, concurrency, and gophers. |
| |
| Keep that in mind. |
| |
| * Hello, go |
| |
| .play chat/support/hello.go |
| |
| * Hello, net |
| |
| .play chat/support/hello-net.go |
| |
| * Interfaces |
| |
| Hey neato! We just used `Fprintln` to write to a net connection. |
| |
| That's because a `Fprintln` writes to an `io.Writer`, and `net.Conn` is an `io.Writer`. |
| |
| .code chat/support/hello-net.go /Fprintln/ |
| .code chat/support/defs.go /Fprintln/ |
| .code chat/support/defs.go /type.Writer/,/^}/ |
| .code chat/support/defs.go /type.Conn/,/^}/ |
| |
| * An echo server |
| |
| .play chat/support/echo-no-concurrency.go |
| |
| * A closer look at io.Copy |
| |
| .code chat/support/echo-no-concurrency.go /Copy/ |
| .code chat/support/defs.go /Copy/,/func/ |
| .code chat/support/defs.go /type.Conn/,/^}/ |
| .code chat/support/defs.go /type.Writer/,/^}/ |
| .code chat/support/defs.go /type.Reader/,/^}/ |
| |
| * Goroutines |
| |
| Goroutines are lightweight threads that are managed by the Go runtime. To run a function in a new goroutine, just put `"go"` before the function call. |
| |
| .play chat/support/goroutines.go |
| |
| * A concurrent echo server |
| |
| .play chat/support/echo.go |
| |
| * "Chat roulette" |
| |
| In this talk we'll look at a simple program, based on the popular "chat roulette" site. |
| |
| In short: |
| |
| - a user connects, |
| - another user connects, |
| - everything one user types is sent to the other. |
| |
| * Design |
| |
| The chat program is similar to the echo program. With echo, we copy a connection's incoming data back to the same connection. |
| |
| For chat, we must copy the incoming data from one user's connection to another's. |
| |
| Copying the data is easy. As in real life, the hard part is matching one partner with another. |
| |
| * Design diagram |
| |
| .image chat/diagrams.png |
| |
| * Channels |
| |
| Goroutines communicate via channels. A channel is a typed conduit that may be synchronous (unbuffered) or asynchronous (buffered). |
| |
| .play chat/support/chan.go |
| |
| * Select |
| |
| A select statement is like a switch, but it selects over channel operations (and chooses exactly one of them). |
| |
| .play chat/support/select.go |
| |
| * Modifying echo to create chat |
| |
| In the accept loop, we replace the call to `io.Copy`: |
| |
| .code chat/support/echo.go /for {/,/\n }/ |
| |
| with a call to a new function, `match`: |
| |
| .code chat/tcp-simple/chat.go /for {/,/\n }/ |
| |
| * The matcher |
| |
| The `match` function simultaneously tries to send and receive a connection on a channel. |
| |
| - If the send succeeds, the connection has been handed off to another goroutine, so the function exits and the goroutine shuts down. |
| - If the receive succeeds, a connection has been received from another goroutine. The current goroutine then has two connections, so it starts a chat session between them. |
| |
| .code chat/tcp-simple/chat.go /var.partner/,/^}/ |
| |
| * The conversation |
| |
| The chat function sends a greeting to each connection and then copies data from one to the other, and vice versa. |
| |
| Notice that it launches another goroutine so that the copy operations may happen concurrently. |
| |
| .code chat/tcp-simple/chat.go /func.chat/,/^}/ |
| |
| * Demo |
| |
| * Error handling |
| |
| It's important to clean up when the conversation is over. To do this we send the error value from each `io.Copy` call to a channel, log any non-nil errors, and close both connections. |
| |
| .code chat/tcp/chat.go /func.chat/,/^}/ |
| .code chat/tcp/chat.go /func.cp/,/^}/ |
| |
| * Demo |
| |
| * Taking it to the web |
| |
| "Cute program," you say, "But who wants to chat over a raw TCP connection?" |
| |
| Good point. Let's modernize it by turning it a web application. |
| |
| Instead of TCP sockets, we'll use websockets. |
| |
| We'll serve the user interface with Go's standard `net/http` package, and websocket support is provided by the `websocket` package from the `go.net` sub-repository, |
| |
| * Hello, web |
| |
| .play chat/support/hello-web.go |
| |
| * Hello, WebSocket |
| |
| .code chat/support/websocket.js |
| .play chat/support/websocket.go |
| |
| * Using the http and websocket packages |
| |
| .code chat/http/chat.go /package/,/^}/ |
| |
| * Serving the HTML and JavaScript |
| |
| .code chat/http/html.go /import/ |
| .code chat/http/html.go /func/,/<script>/ |
| .code chat/http/html.go /websocket.=/,/onClose/ |
| .code chat/http/html.go /<\/html>/,$ |
| |
| * Adding a socket type |
| |
| We can't just use a `websocket.Conn` instead of the `net.Conn`, because a `websocket.Conn` is held open by its handler function. Here we use a channel to keep the handler running until the socket's `Close` method is called. |
| |
| .code chat/http-noembed/chat.go /type.socket/,/END/ |
| |
| * Struct embedding |
| |
| Go supports a kind of "mix-in" functionality with a feature known as "struct embedding". The embedding struct delegates calls to the embedded type's methods. |
| |
| .play chat/support/embed.go /type/,$ |
| |
| * Embedding the websocket connection |
| |
| By embedding the `*websocket.Conn` as an `io.ReadWriter`, we can drop the explicit `socket` `Read` and `Write` methods. |
| |
| .code chat/http/chat.go /type.socket/,/END/ |
| |
| * Demo |
| |
| * Relieving loneliness |
| |
| What if you connect, but there's noone there? |
| |
| Wouldn't it be nice if we could synthesize a chat partner? |
| |
| Let's do it. |
| |
| * Generating text with markov chains |
| |
| .code chat/support/markov.txt |
| |
| * Generating text with markov chains |
| |
| Fortunately, the Go docs include a markov chain implementation: |
| |
| .link /doc/codewalk/markov go.dev/doc/codewalk/markov |
| |
| We'll use a version that has been modified to be safe for concurrent use. |
| |
| .code chat/markov/markov.go /Chain/,/{/ |
| .code chat/markov/markov.go /Write/,/{/ |
| .code chat/markov/markov.go /Generate/,/{/ |
| |
| * Feeding the chain |
| |
| We will use all text that enters the system to build the markov chains. |
| To do this we split the socket's `ReadWriter` into a `Reader` and a `Writer`, |
| and feed all incoming data to the `Chain` instance. |
| |
| .code chat/markov/chat.go /type.socket/,/^}/ |
| .code chat/markov/chat.go /var.chain/,/^}/ |
| |
| * The markov bot |
| |
| .code chat/markov/chat.go /\/\/.Bot/,/^}/ |
| .code chat/markov/chat.go /type.bot/,/^}/ |
| .code chat/markov/chat.go /func.+bot.+Write/,/^}/ |
| .code chat/markov/chat.go /func.+bot.+speak/,/^}/ |
| |
| * Integrating the markov bot |
| |
| The bot should jump in if a real partner doesn't join. |
| To do this, we add a case to the select that triggers after 5 seconds, starting a chat between the user's socket and a bot. |
| |
| .code chat/markov/chat.go /func.match/,/^}/ |
| |
| The `chat` function remains untouched. |
| |
| * Demo |
| |
| * One more thing |
| |
| * TCP and HTTP at the same time |
| |
| .code chat/both/chat.go /func main/,/^}/ |
| .code chat/both/chat.go /func netListen/,/^}/ |
| |
| * Demo |
| |
| * Discussion |
| |
| * Further reading |
| |
| All about Go: |
| |
| .link / go.dev |
| |
| The slides for this talk: |
| |
| .link /talks/2012/chat.slide go.dev/talks/2012/chat.slide |
| |
| "Go Concurrency Patterns" by Rob Pike: |
| |
| .link /s/concurrency-patterns go.dev/s/concurrency-patterns |