Select: selecionando o canal “mais rápido”
O desafio
Imagina que você quer consultar as informações de um local a partir de um CEP.
Existem algumas APIs de CEP, mas você quer que o resultado seja o mais rápido possível. O que você faz?
Você poderia fazer um benchmark para ver qual é a API mais rápida, mas isso não seria muito preciso, porque uma certa API pode ficar mais lenta em certos horários, ou pode ser mais rápida para alguns CEPs e não para outros...
Uma estratégia que podemos usar é disparar uma requisição para cada uma e usar a primeira que responder com um status de 200 OK
, e essa é justamente a ideia do projeto CEP Promise do BrasilAPI.
Dessa forma não só ganhamos em performance como também em disponibilidade, porque mesmo se uma API falhar, ainda vamos ter outra(s) de fallback.
Para implementar algo parecido, podemos usar o select
.
O select é como se fosse um switch
, onde cada case
é uma tentativa de comunicar com um canal.
O primeiro case
com um recebimento ou envio pronto para ser executado vai ser o case que a goroutine vai entrar.
Exemplo:
select {
case <-time.After(time.Second):
fmt.Println("1 segundo")
case <-time.After(500 * time.Millisecond):
fmt.Println("500ms")
}
// "500ms"
O select
por padrão pausa a execução da goroutine. Um select
sem case
vai parar a goroutine indefinidamente:
package main
func main() {
...
// Se não tiver outra goroutine, vai causar um deadlock e encerrar
select {}
}
O select
, assim como o switch
, também tem um caso default
.
Ele cai no default
quando nenhum dos outros casos está pronto para ser executado.
read := false
// combinando um loop com um select
for !read {
select {
case x := <-ch:
fmt.Println("li do channel:", x)
read = true
default:
fmt.Println("channel ainda não está preparado para leitura")
time.Sleep(100 * time.Millisecond)
}
}
// "channel ainda não está preparado para leitura"
// "channel ainda não está preparado para leitura"
// "li do channel: 123"
No exemplo de consulta de CEP, um código fictício para pegar o primeiro CEP que retornar seria:
cep := "12345678"
var info CepInfo
select {
case info = <-cepInfoCorreios(cep):
fmt.Println("Correios retornou primeiro")
case info = <-cepInfoViaCep(cep):
fmt.Println("ViaCEP retornou primeiro")
}
fmt.Println(info.City)
// obs.: num caso real você teria que cancelar as requisições HTTP também
Top comments (1)
Gostei, foi bem útil para mim. Eu estava estudando esse tema.