Go na prática

Construindo um serviço HTTP com direito a concorrência

Eduardo Pereira
Floripa Gophers
5 min readOct 13, 2015

--

Em meu artigo anterior: Golang um mês depois, fiz uma breve análise sobre a linguagem de programação que nasceu dentro do Google e mostrei alguns pontos que achei interessante e outros não tão interessantes.

Decidi então mostrar na prática (conforme as minhas limitações) como construir um simples serviço com requests HTTP e um pouquinho de concorrência (nada muito complexo).

O serviço em questão trata-se de um consultor de USD para BRL (Dólares para Real), ou seja, o usuário vai enviar uma request para o serviço e o mesmo vai responder o valor atual do dólar dos último 5 minutos.

Porque dos últimos 5 minutos ? é nesse momento que entra a concorrência, por que também estará rodando uma rotina que vai checar o dólar via request HTTP a cada 5 minutos (tudo ao mesmo tempo). Desta forma sempre que uma request do usuário chegar no serviço, logo retorna o valor que precisa de imediato, por não precisar consultar outro serviço sempre que uma nova request chega, e tudo isso por que o último valor estará guardado em memória e de forma concorrente.

Irei explicar de uma forma não linear, quero dizer, não da forma que se executa, mas no final toda explicação vai fazer sentido (pelo menos eu espero).

Estrutura inicial

Para interpretar o JSON precisamos associá-lo à uma estrutura, isso é muito comum na linguagem

A nossa estrutura ou type struct precisa refletir o retorno do serviço que vai ser consultado. Vamos consultar um serviço do Yahoo! que faz a conversão USD para BRL e retorna os valores requisitados no formato JSON. A estrutura inicial será:

Para ficar claro, a estrutura esta "refletindo" o formato do JSON que é esperado e respondido pelo serviço do Yahoo!.

{
“query”: {
“count”:1,
”created”:”2015–10–13T00:55:09Z”,
”lang”:”en-US”,
”results”:”3.7616 BRL”
}
}

Em seguida vamos fazer a request para o serviço de acordo com a URL e código a seguir (não vou explicar essa URL, aconselho visitar a documentação do Yahoo! em https://developer.yahoo.com/yql/)

A função checker irá retorna dois parâmetros (lembra que eu falei no artigo anterior sobre funções retornarem o erro e um resultado). De forma simples na linha 5 estamos fazendo a requisição utilizando a função Get da package http (pertence a biblioteca padrão), testamos se houve algum erro (err != nil), logo em seguida vamos ler o conteúdo na linha 18 e retornar o resultado da função na linha 24.

E o defer na linha 11 ? Essa é uma rotina criada para ser executada somente quando a função termina, então função com defer na linha 11 será executada para terminar a request HTTP. É uma ótima estratégia para limpar quaisquer resources que ficaram pendentes na função.

Interpretando o JSON

Não é JASON é o JSON, esse é mais fácil de interpretar

Estava até pensando em colocar uma foto do JASON do Sexta-Feira 13, mas fui mais forte que essa piada velha.

A idéia agora é obter a string da função checker e retornar somente o que nos interessa, o valor em USD, para isso será executada a seguinte função:

Quando a função parseJSON receber o resultado em texto no formato JSON na linha 3 vamos criar um objeto baseado na estrutura requestResult e na linha 5 a função json.Unmarshal será responsável por interpretar o valor e associá-lo a estrutura, por isso a importância da estrutura "refletir" o formato do JSON e o retorno será somente o que interessa, valor em USD.

Channels e Routines

Chegou a hora de falar sobre uma das principais características da linguagem Go.

Sem dúvida uma das coisas mais interessantes da linguagem é que a concorrência esta no "sangue", esta no core da linguagem, não precisa de biblioteca auxiliar nem mesmo de "gambiarras" ou "macgyverismos".

E os responsáveis por isso são: a chamada go, o tipo channel e a routine. A chamada ou keyword go irá fazer com que uma função atrelada a thread principal, torne-se independe, ou seja, uma routine e fique em background executando até que termine e o tipo channel é responsável por facilitar a comunicação com as rotinas em background, o tipo channel é sincronizado e quando tem um valor inserido (pense como se fosse um cano), só receberá outro valor se o primeiro for lido (se o cano estiver vazio), essa atitude previne que as funções conectadas pelo channel fique foram de sincronia. Também existem os buffered channels, mas esse assunto fica para lição de casa (mais informação em https://tour.golang.org/concurrency/1).

Quando executado a função pool irá criar um canal do tipo string que será utilizado para comunicarmos com a rotina que será criada na linha 3, veja que a keyword go esta presente fazendo com que a função anônima fique em background. Na linha 5 estamos criando uma função e atribuindo a uma variável (função anônima), a mesma faz a requisição chamando as funções checker e parseJSON para na linha 9 que vai inserir no canal de comunicação o resultado do USD.

O loop na linha 14 termina quando temos um valor no canal, pois as vezes a requisição feita para o Yahoo! não retorna o valor em USD, então precisamos garantir que tenha valor logo de início.

Na linha 24 temos um loop infinito que executa a cada 5 minutos com ajuda da função time.Tick responsável por fazer um tipo de ping no loop para que ele execute com a pausa necessária e faça a requisição novamente.

Função principal

Assim como e seu antecessor a linguagem C, a função principal também se chama main.

Agora sim, podemos finalizar (ou começar, depende do ponto de vista). Na linha 3 será criada pool utilizando a função pool que retorna o canal e na linha 4 o valor contido no canal será retirado e inserido na variável latestResult (que como o nome sugere, irá guardar o último resultado).

Até a linha 19 serão executadas rotinas de inicialização, como criação de variáveis e checagens no ambiente, com o intuito de saber se é preciso trocar a porta HTTP (necessário quando utiliza Heroku).

Na linha 21 é criado um handler para a rota "/" e atribuída a uma função, que receberá a request HTTP, neste momento na linha 22 entra em ação a keyword select que testa: se caso um valor foi retirado do canal com sucesso segue com a execução ou valor default: se caso não fou retirado valor, ou seja, o se o canal estiver vazio também pode seguir com sucesso. E para finalizar é escrito o valor de latestResult na resposta HTTP, agora o resultado será retornado para o usuário que requisitou.

Bonus

Deploy no Heroku

Para fazer o deploy no Heroku você precisará seguir os seguintes passos:

  • Criar a sua conta no Heroku e uma app nova
  • Executar o comando godep save em seu projeto, para criar uma listas das suas dependências necessárias.
  • Cria um arquivo Procfile com o nome da sua app no formato "web: myapp-name" (sem as aspas duplas).
  • Também criar uma variável de ambiente chamada GO_ENV com conteúdo PRODUCTION
  • É possível que também você precise rodar uma dyno, basta executar o comando: "heroku ps:scale web=1 -a myapp-name" (sem as aspas duplas, também é necessário ter instalado o Heroku Toolbelt).

Bonus 2

Links

--

--

Eduardo Pereira
Floripa Gophers

Software development for over a decade, I enjoy working with talented people who also love solving problems and find intelligent solutions