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