Rafael Matsumoto

Digital garden

01 Fev 2020

Hot Reloading Go

Reading Time: 2 minutos

O exemplo utilizado nesse post foi baseado na seção 1.7 do livro ‘The Go Programming Language’

Ao realizar a leitura do capítulo citado, notei a facilidade de se iniciar um web server com a linguagem Go, tive a ideia de implementar um hot-reloader do zero, ferramenta muito comum em frameworks para a web e que auxilia consideravelmente na produtividade ao programar.

A seguir há um passo-a-passo de como realizei a implementação.

Código: https://github.com/rafaelmatsumoto/hotreloading-go

Pré-requisitos:

  • Docker
  • Docker Compose
  • Go

Guia

Primeiro passo, criar um script básico para rodar a aplicação:

// ./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)
}

Para rodar a aplicação é preciso executar o comando:

go run main.go &

Após isso, ao realizar uma requisição GET a seguinte resposta é obtida:

http localhost:8000
HTTP/1.1 200 OK
Content-Length: 15
Content-Type: text/plain; charset=utf-8
Date: Sat, 01 Feb 2020 16:55:20 GMT

URL.Path = "/"

Implementando a funcionalidade de hot reloading

É necessário criar um Dockerfile com a seguinte configuração:

# ./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"]

A biblioteca reflex permite adicionar um listener para executar comandos sempre que algum tipo de arquivo for alterado, a configuração utilizada foi:

./reflex.conf

-r '(\.go$|go\.mod)' -s go run main.go &

Essa configuração determina que, se um arquivo com a extensão .go ou nomeado go.mod for alterado, o servidor é reinicializado automaticamente.

Após essa etapa, criamos um arquivo docker-compose.yml para auxiliar no uso do Docker:

# ./docker-compose.yml
version: "2.4"
services:
  app:
    build: .
    volumes:
      - .:/app
    working_dir: /app
    ports:
      - 8000:8000

E alteramos o script principal para o servidor aceitar requisições externas:

// ./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)
}

Então rodamos o comando

docker-compose up -d
e é isto, toda nova alteração será automaticamente implementada.

Exemplo na prática:

<- Voltar para a página principal