Hot Reloading Go
Reading Time: 1 minute
The example used in this post is based on section 1.7 of the book ‘The Go Programming Language’
While reading the mentioned chapter, I noticed how easy it is to start a web server with the Go language, which gave me the idea to implement a hot-reloader from scratch, a tool very common in web frameworks that significantly aids programming productivity.
Below is a step-by-step guide of how I implemented it.
Code: https://github.com/rafaelmatsumoto/hotreloading-go
Prerequisites:
- Docker
- Docker Compose
- Go
Guide
First step, create a basic script to run the application:
// ./main.go
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
log.Print("Server loaded on port 8000")
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe("localhost:8000", nil))
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}
To run the application, execute the command:
go run main.go &
After that, when making a GET request, the following response is obtained:
http localhost:8000 HTTP/1.1 200 OK Content-Length: 15 Content-Type: #00AFAF">text/plain; charset=utf-8 Date: Sat, 01 Feb 2020 16:55:20 GMT URL.Path = "/"
Implementing hot reloading functionality
We need to create a Dockerfile with the following configuration:
# ./Dockerfile
FROM golang:1.13-alpine as base
RUN apk add git
EXPOSE 8000
FROM base as dev
RUN go get github.com/cespare/reflex
COPY reflex.conf /
ENTRYPOINT ["reflex", "-c", "/reflex.conf"]
The reflex library allows adding a listener to execute commands whenever certain file types are changed. The configuration used was:
./reflex.conf
-r '(\.go$|go\.mod)' -s go run main.go &
This configuration determines that if a file with the .go extension or named go.mod is changed, the server is automatically restarted.
After this step, we create a docker-compose.yml file to assist with Docker usage:
# ./docker-compose.yml
version: "2.4"
services:
app:
build: .
volumes:
- .:/app
working_dir: /app
ports:
- 8000:8000
And we modify the main script for the server to accept external requests:
// ./main.go
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
log.Print("Server loaded on port 8000")
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe("0.0.0.0:8000", nil))
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}
Then we run the command
docker-compose up -dand that’s it, every new change will be automatically implemented.
Example in practice: