Como fazer graceful shutdown correto em um server HTTP

Embora esse tema não seja abordado em exemplos simples de APIs, ele é muito importante. Encerrar um servidor HTTP de forma abrupta, acaba por fechar todas as conexões que ele tem abertas da mesma forma. Em outras palavras, não tratar desligamentos via SIGINT ou SIGTERM de uma API, pode acabar gerando grandes problemas.

Para evitar tais problemas, vou mostrar nesse post como tratar tais sinais e fazer um graceful shutdown do seu servidor HTTP.

Servidor HTTP

Para começar, vamos escrever uma rota que retornará um “hello world”. Vou utilizar o package go-chi para definir a rota não por ser necessário, mas para facilitar o entendimento em aplicações do mundo real.

r := chi.NewRouter()

r.Get("/", func(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello World"))
})
Leia mais »
photo of a turtle swimming underwater

O que é e como utilizar o tipo enum

Muitas vezes, dentro dos sistemas que desenvolvemos, temos valores constantes. Dentre outras possibilidades, um exemplo desses valores, seria a representação do status de um cadastro. Nesse caso, pense em um status onde temos variações além de ativo e inativo.

Caso esses status sejam definidos como string, sua validação dentro do sistema pode acabar se tornando uma grande dor de cabeça. Além, é claro, de “inflar” o binário. Afinal, em cada validação teremos 2 strings (valor esperado e valor sendo validado).

Na tentativa de evitar esses problemas, podemos utilizar o famoso tipo enum. Se você não está familiarizado com esse tipo, ele nada mais é do que um tipo de tamanho limitado ou fixo.

Para ficar mais claro, vamos escrever um pouco de código. Seguindo a ideia apresentada no inicio do post, vamos criar um tipo enum para validar status de um cadastro.

Leia mais »
rock formation

Como utilizar “herança” em interfaces

Embora o Golang não implemente orientação a objetos, de forma superficial, sua estratégia de embedding – que inclusive é muito utilizada nos packages nativos da linguagem – é muito parecida com a famosa herança.

Nesse post, vou mostrar como utilizar essa estratégia para que possamos ter interfaces mais granulares.

Antes de começar, como o objetivo desse post é exemplificar como utilizar embedding em interfaces, não vamos implementar os métodos. Também não vamos mostrar como utilizá-las em assinaturas de funções/métodos. Caso você queria saber mais sobre como utilizar interfaces, recomendo a leitura do post “Trabalhando com Interfaces”.

Sem mais delongas, vamos criar 3 interfaces. Writer, Reader e Closer.

type Writer interface {
	Write(p []byte) (n int, err error)
}

type Reader interface {
	Read(p []byte) (n int, err error)
}

type Closer interface {
	Close() error
}
Leia mais »
snow top mountain under clear sky

Como implementar uma função utilizando context

Nesse post vamos implementar uma função que utiliza context. No exemplo de chamada, vou utilizar o context WithTimeout. Dessa forma, conseguiremos fazer a função ser cancelada automaticamente, caso o tempo de execução dela ultrapasse o tempo estipulado no context.

Vamos iniciar criando uma função com o nome doSomeHeavyWork.

func doSomeHeavyWork(ctx context.Context, msg string) {
}

Para simular um processamento longo, vamos adicionar uma goroutine com um sleep de 2 segundos. Essa goroutine irá receber um channel do tipo bool. Ele irá sinalizar que a goroutine foi finalizada.

Leia mais »
brown and pink pendant

Como utilizar tags customizadas

A utilização de tags ajuda muito na hora de escrever funções genéricas. Um exemplo disso é a função json.Marshal. Não importa como sua struct está estruturada, se ela tiver a tag json no atributo, a função consegue fazer o que precisa ser feito.

Se você não sabe do que eu estou falando, te convido à ler o post “O que são e como utilizar tags”. Lá dou todo o contexto para que você possa entender melhor o que vamos fazer aqui nesse post.

Além do package json, existe uma infinidade de outros packages que tiram vantagem da utilização das tags. Esses packages vão desde encoders até ORMs.

Embora existam packages que ajudem na validação de campos, para fins didáticos, vamos criar uma função que utiliza da tag required para saber se um campo é ou não obrigatório.

func validateFields(stc any) error {

}
Leia mais »
man love people woman

Como criar templates para arquivos JSON, YAML e TXT

Um package padrão do Go e que, no meu ponto de vista, é pouco explorado, é o text/template. Com a ajuda desse package, podemos preencher os valores em qualquer template no formato texto, como por exemplo, arquivos JSON, YAML e TXT.

Para exemplificar melhor, vamos criar um arquivo YAML com o nome template.yaml e o seguinte conteúdo.

name: {{ .Name }}
age: {{ .Age }}

Com o template criado, podemos iniciar nosso main.go.

package main

func main() {
}
Leia mais »
close up shot of keyboard buttons

Como fazer fuzz test em requests HTTP (parte 2)

Na primeira parte desse post, vimos como utilizar o fuzz test para gerar payloads automaticamente em nossos testes, o que nos ajudou a encontrar problemas quando o payload não vinha no formato que esperávamos.

Nessa segunda parte, vamos utilizar o fuzz para gerar os inputs que serão utilizados nos campos de um payload correto.

Utilizando o mesmo código do post anterior, antes de começar, vamos fazer uma pequena mudança na validação de e-mail. Além de validar se ele foi preenchido, vamos validar se o e-mail é válido.

Para isso, vamos criar um novo erro e um else para quando o valor do e-mail não esteja vazio.

...

ErrEmailRequired = errors.New("Email is required")

ErrEmailInvalid = errors.New("Email is invalid")

...

if p.Email == "" {
		return ErrEmailRequired
} else {
		rgx := regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)*$")
		if !rgx.MatchString(p.Email) {
			return ErrEmailInvalid
		}
}
...
Leia mais »
blue threads

Quais as diferenças entre goroutines e threads

Um pensamento muito comum para quem está chegando na linguagem Go, ou só conhece a linguagem pelo o que “ouviu na rua”, é achar que as goroutines são threads.

Acontece que na prática, goroutines e threads, embora parecidas, são coisas bem diferentes. Vejamos.

Uma thread, nada mais é do que um espaço que o sistema operacional aloca na memória. Normalmente, uma thread ocupa um espaço de 2mb.

Quando uma goroutine é criada, ela normalmente ocupa 2kb de espaço em memória, o que dá aproximadamente 1% do tamanho de uma thread. Ou seja, para uma goroutine simples, ter uma thread inteira só para ela acaba sendo um grande desperdício de memória.

Embora modificar o tamanho das threads do sistema operacional pudesse resolver o problema de desperdício de memória em goroutines simples, isso acabaria gerando um grande problema para goroutines mais complexas, onde podemos ver seu consumo chegar na casa de gigas.

Leia mais »
a person sitting on wooden planks across the lake scenery

Onde e qual Context utilizar

O package Context do Go é algo que vemos sendo cada vez mais utilizados por grandes packages. No entanto, não é nada estranho ver pessoas sem saber qual context utilizar dentre os diferentes tipos disponíveis.

Nesse post, vou falar sobre os diferentes tipos de context que temos, como fazemos para obtê-los e onde normalmente são utilizados.

Se você nunca viu esse package, aconselho assistir o vídeo Como funciona e para que serve o package Context (Golang) que temos no nosso canal do YouTube.

Bora começar?

Leia mais »
squirrel eating cone in forest

Como fazer fuzz test em requests HTTP (parte 1)

Na edição de 2022 da GopherCon Brasil, tive o prazer de palestrar sobre Fuzz Test. Foi muito bacana, pois durante a palestra, assim como nos corredores do evento, fizeram vários questionamentos que eu ainda não tinha feito sobre essa feature do Go.

Se você ainda não conhece esse tipo de teste, convido você a ler um post que publicamos aqui no blog (link para o post) onde explicamos melhor o assunto.

O que vou tratar nesse post é o resultado das perguntas feitas no evento mais um link que o Ricardo Maricato me enviou.

Para ver uma das formas de implementar o Fuzz Test para requests HTTP, vamos implementar um endpoint para validação dos dados de uma pessoa.

Vamos começar criando uma struct com um método de validação, e algumas variáveis para armazenar os erros de validação que podemos ter.

Leia mais »