Session 07¶
Topic 2: The Go language¶
- tour of Go
- tour of Go (continued) 👈
- Go’s programming syntax
- many workers with Goroutines
- shared memory between workers
- message passing with Go channels
- case studies with concurrency
- performance and scalability
- case studies with parallel algorithms
Today¶
Tour of Go (continued)¶
- example: animated GIFs
- example: web server
Go and graphics¶
Graphics in Go¶
No heavy frameworks:
- pure standard library:
image,gif,color,math - no external dependencies, no build pain
- fast enough for real-time generation
- outputs an artifact (GIF)
Everything is standard Go:
- image creation (
image,image/color) - animation encoding (
image/gif) - math (
math,rand) - I/O abstraction (
io.Writer) - networking (
net/http)
From math to animation¶
Visualization pipeline:
math → data → visualization → artifact
- parametric equations (’60 sci-fi Lissajous curves)
- sampling of equations at a given resolution
- frame-by-frame animation
- image encoding as GIF
gopl.io, p13:
The next program demonstrates basic usage of Go’s standard image packages, which we’ll use to create a sequence of bit-mapped images and then encode the sequence as a GIF animation. The images, called Lissajous figures, were a staple visual effect in sci-fi films of the 1960s. They are the parametric curves produced by harmonic oscillation in two dimensions, such as two sine waves fed into the \(x\) and \(y\) inputs of an oscilloscope.
Computing Lissajous curves¶
core logic:
for range nframes {
rect := image.Rect(0, 0, 2*size+1, 2*size+1)
img := image.NewPaletted(rect, palette)
for t := 0.0; t < cycles*2*math.Pi; t += res {
x := math.Sin(t)
y := math.Sin(t*freq + phase)
img.SetColorIndex(size+int(x*size+0.5),
size+int(y*size+0.5),
blackIndex)
}
phase += 0.1
// ...
gopl.io, p15:
The lissajous function has two nested loops. The outer loop runs for 64 iterations, each producing a single frame of the animation. It creates a new 201&201 image with a palette of two colors, white and black. All pixels are initially set to the palette’s zero value (the zeroth color in the palette), which we set to white. Each pass through the inner loop generates a new image by setting some pixels to black. The result is appended, using the built-in append function, to a list of frames in anim, along with a specified delay of 80ms. Finally the sequence of frames and delays is encoded into GIF format and written to the output stream out.
Reusable interfaces - strength of Go¶
func lissajous(out io.Writer)
io.Writer is an interface used by:
- file output (disk)
- standard output (terminal)
- HTTP response (web)
… and many more:
anything that implements a Write method
Two modes: CLI tool ↔ web service¶
func lissajous(out io.Writer)
lissajous(os.Stdout) // terminal / command-line
lissajous(w) // web server (w has type http.ResponseWriter)
- single
lissajousfunction: one core logic - supports both kinds of outputs: CLI or web
- … more kind of outputs are easy to implement
🎯 No refactoring needed!
Hands-on: code/go/gif¶
-
open the terminal, ssh into Matrix
-
cdinto your clone of our course repo
code.vt.edu/jrudi/cmda3634 -
go into the folder of the example:
cd code/go/gif -
run the Go program, save output to
.giffile with redirection “>”go run lissajous.go > lofi.gif
Copy from Matrix to your laptop¶
How do you see the image stored on Matrix?
-
on Matrix, verify the folder path you’re currently in
ls # check if the GIF gets listed pwd # prints the current path -
use
scp(copies over SSH) in a new terminal window:scp <VT_PID>@matrix.math.vt.edu:<OUTPUT_FROM_PWD> lj.gif<VT_PID> # is your username (without @vt.edu) <OUTPUT_FROM_PWD> # is the output from step 1 : # (a colon) separates URL and path lj.gif # is the target filename (on your laptop)
Go and web services¶
A web server in Go¶
this line in Go starts a real HTTP server:
http.ListenAndServe("localhost:8000", nil)
- production-grade
- same code on laptop, cluster, or cloud
- each incoming request is handled concurrently
→ concurrency is automatic!
The Go standard library’s package net/http does the heavy lifting:
- routing
- request parsing
- response writing
- concurrency
Request response¶
server responds with your custom handler function
func handler(w http.ResponseWriter, r *http.Request)
r→ everything the client sentw→ everything you send back
🎯 Explicit inputs, explicit outputs!
Transfer data over the web¶
receive data from client using the struct r
clientURL := r.URL.Path
send data to client: just write to w like to a file
fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
same as for CLI programs and file output:
fmt.Fprintf(io.Stdout, "URL.Path = %q\n", r.URL.Path) // CLI output
file, err := os.Open(filename) // ... check err
fmt.Fprintf(file, "URL.Path = %q\n", r.URL.Path) // file output
Easy to grow: This tiny server can evolve into a …
- REST API
- JSON service
- metrics endpoint
- microservice
Same core logic, many roles¶
One Go program can be:
CLI tool / web server / backend service
→ No context switching between ecosystems.
Why Go shines for servers:
- fast startup, low memory
- strong standard library
- single static binary
- simple concurrency model (we’ll explore that later)
Hands-on: code/go/server/v1¶
- open the terminal, ssh into Matrix
cdinto your clone of our course repo
code.vt.edu/jrudi/cmda3634- into the folder:
cd code/go/server/v1
# first terminal window (on Matrix)
go run server.go
# ... <Ctrl+c> to stop the server (hint: cancels most terminal commands)
# second terminal window (also on Matrix)
curl http://localhost:8000/hello
the main function connects the handler function to URLs beginning with
/
func main() {
http.HandleFunc("/", handler) // each request calls handler
log.Fatal(http.ListenAndServe("localhost:8000", nil))
}
the handler function echos the Path component of the request URL in
r
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}
gopl.io, p19:
This example shows a minimal server that returns the path component of the URL used to access the server. That is, if the request is for
http://localhost:8000/hello, the response will beURL.Path = "/hello".The program is only a handful of lines long because library functions do most of the work. The main function connects a handler function to incoming URLs that begin with
/, which is all URLs, and starts a server listening for incoming requests on port 8000. A request is represented as a struct of typehttp.Request, which contains a number of related fields, one of which is the URL of the incoming request. When a request arrives, it is given to the handler function, which extracts the path component (/hello) from the request URL and sends it back as the response, usingfmt.Fprintf.
Tip: Open site of a server on Matrix
use port-forwarding with SSH
man ssh # to read the manual
-L [bind_address:]port:host:hostport
Specifies that connections to the given TCP port or
Unix socket on the local (client) host are to be
forwarded to the given host and port, or Unix socket,
on the remote side.
-N Do not execute a remote command. This is useful for
just forwarding ports.
ssh -N -L 8000:localhost:8000 <VT_PID>@matrix.math.vt.edu
Source: Answer to How to open port via SSH tunnel?
Exercise¶
Use the server feature that returns an animated GIF (Lissajous) as presented earlier.
To get the gif image from the web server:
- run the server program in one terminal window
go run lissajous.go web
- download from the server program in another terminal window
curl http://localhost:8000 --output lj.gif
-
Modify the Lissajous server to read parameter values from the URL. For example, a URL like
http://localhost:8000/?cycles=20sets the number of cycles to 20 (the default is 5).
Use the
strconv.Atoifunction to convert the string parameter into an integer (e.g.,"20"→20). You can see its documentation with the terminal command:go doc strconv.Atoi
more exercises are in the README of code/go/server
Key takeaways¶
- Go’s syntax prefers clarity
- from math to pixels in ~60 lines
- from terminal output to web service in ~10 lines
Looking ahead¶
- Go syntax: readability over cleverness
- basic syntax
- deeper dive into syntax