Há algum tempo a arquitetura de API's REST vem dominando o mercado, seja em aplicativos web ou microserviços. Porém existe outras arquiteturas mais modernas e com muito mais vantagens que o REST, hoje nós vamos falar do gRPC
O que é gRPC?
gRPC é um sistema de código aberto criado pela Google em 2015 como uma melhoria para a arquitetura de comunicação chamada RPC ( Remote Procedure Call).
O gRPC usa em seu sistema de mensagens o formato Protobuf (Protocol buffers), que é um forma de definir uma estrutura de dados assim como o JSON e XML só que muito mais rápido e muito mais leve.
Outra grande vantagem de usar o gRPC é que normalmente API's REST são escritas em HTTP/1.1 ao contrário do gRPC que usa HTTP/2
O que é Protobuf?
O Protobuf ou Protocol Buffer como dito anteriormente, é que é um forma de definir uma estrutura de dados assim como o JSON e XML, ele funciona de uma forma um pouco diferente desses dois e tem muitos benefícios sobre eles.
Outra vantagem muito legal do Protobuf é que você pode gerar automaticamente código para várias linguagens, no momento que eu escrevo são elas C++, C#, Dart, Go, Java, Kotlin e Python. Mas para isso precisamos criar um arquivo com a extensão .proto
para que isto dê certo,dito isso vamos colocar em prática esses conceitos.
E qual a vantagem de usar HTTP/2?
Além do fato de que o HTTP/2 ser muito mais recente que o HTTP/1.1 as principais vantagens são:
- Conexão Única: O HTTP/1.1 é um protocolo sequencial, onde o navegador precisa abrir diversas conexões TCP em sequência para os arquivos e caso um arquivo seja muito pesado o processamento acaba sendo lento, já o HTTP/2 ele precisa de apenas uma conexão TCP onde ele fará todas as requisições necessárias para buscar os arquivos, e isso trás um custo de processamento e memória menor junto com uma latência mais baixa também, trazendo benefícios de hardware e software.
Compactação dos cabeçalhos: Tanto o HTTP/1.1 quanto o HTTP/2 comprimem suas mensagens nas requisições, porém o HTTP/2 usa o HPACK, que é um algoritmo mais avançado que elimina algumas informações repetidas nos cabeçalhos tornando o carregamento de arquivos e pacotes muito mais rápído.
Server Push: Outra vantagem do HTTP/2 é que ele pode ser bidirecional ou seja, tanto o servidor quanto o cliente podem trocar informações entre si sem que seja solicitado, diferentemente do HTTP/1.1 que é necessário fazer uma requisição nova sempre que precisar de um novo conteúdo
Antes de tudo vamos usar a linguagem Go para o desenvolvimento do nosso servidor gRPC, não se apegue a linguagens porque isso não vai ser importante por agora.
Agora vamos iniciar nosso projeto com
go mod init grpc-go
Logo depois vamos criar uma pasta chamada 'contracts' que será onde vamos criar nosso um arquivo chamado hello.proto
.
Feito isso vamos criar nossa primeira estrutura de dados, que será um request e um response da nossa aplicação
syntax = "proto3";
message HelloRequest {
string name = 1;
}
message HelloResponse {
string msg = 1;
}
Aqui nós criamos duas estruturas de dados, o número seguido do tipo e nome da variável é a ordem que ele vai ficar na estrutura.
Feito isso vamos adicionar também mais duas coisas nesse arquivo
option go_package = "./pb";
service HelloService {
rpc Hello(HelloRequest) returns (HelloResponse) {};
}
a primeira linha que nós adicionamos é para o gerador de código do protobuf sabe onde ele deve criar os arquivos, que no caso vai ser na pasta pb
, já o service HelloService
que nós criamos é os serviços que vamos usar no nosso servidor, dentro dele criamos a função Hello
e nela recebemos a estrutura de dados de request e vamos retornar a estrutura de response.
Antes de prosseguirmos, vamos instalar o compilador do Protobuf que irá gerar nosso código.
- Caso esteja usando Linux, você pode usar o
apt
ou oapt-get
, por exemplo
apt install -y protobuf-compiler
protoc --version
- Caso esteja no Mac, você pode usar o Homebrew
brew install protobuf
protoc --version
- Caso esteja no Windows ou outro SO, você pode consultar o site oficial para fazer a instalação
Feito isso vamos rodar o comando
protoc --go_out=plugins=grpc:./ contracts/*.proto
Isso vai criar a pasta pb
e gerar um novo arquivo .go
chamado hello.pb.go
e nesse arquivo vamos ter uma série de métodos criados pelo gerador de código automático do protobuf. Vamos também instalar a dependência do gRPC do go, para isso damos esse comando
go get -u google.golang.org/grpc
Agora depois de finalizado a instalação, vamos criar nosso servidor. Primeiro vamos criar uma pasta chamada server
e dentro dela um arquivo chamado main.go
. Dentro desse arquivo vamos começar com o seguinte
package main
import (
"context"
"grpc-go/pb"
"log"
"net"
"google.golang.org/grpc"
)
type Server struct {}
func (s *Server) Hello(ctx context.Context, request *pb.HelloRequest) (*pb.HelloResponse, error) {
}
func main(){
}
Nós criamos uma struct chamada Server
e depois criamos um método para essa struct chamado Hello
que vai ser nossa primeira função do servidor, e essa função recebe dois parâmetros, o primeiro sendo um context
que não nos interessa por agora, e o segundo sendo o HelloRequest
que nós tinhamos criado no arquivo hello.proto
. Isso tudo é possível graças ao gerador de código do protobuf que criou o módulo pb
que contém todas essas informações.
Após isso vamos criar nosso retorno da função fazendo o seguinte
func (s *Server) Hello(ctx context.Context, request *pb.HelloRequest) (*pb.HelloResponse, error) {
return &pb.HelloResponse{Msg: "Hello " + request.GetName()}, nil
}
Como podem ver nós estamos retornando a estrutura de dados de response, porém temos um request.GetName()
, uma função que nós não definimos antes, e isso acontece por que o protobuf ao gerar o código para gente, faz os getters e setters automaticamente, isso ajuda muito no desenvolvimento.
Agora vamos criar iniciar nosso servidor na função main
do nosso arquivo
func main(){
listen, err := net.Listen("tcp", "0.0.0.0:9000")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
pb.RegisterHelloServiceServer(grpcServer, &Server{})
if err := grpcServer.Serve(listen); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}
Na nossa primeira linha da função nós iniciamos nosso servidor na porta 9000 e logo após nós validamos para ver se não deu nada de errado. Após isso iniciamos nosso servidor gRPC e depois nós registramos nosso serviço que foi criado no arquivo hello.proto
, nós passamos como parâmetro o servidor gRPC que foi iniciado e depois nossa struct do servidor.
Logo após isso iniciamos o nosso servidor gRPC e verificamos se não vai dar nenhum erro.
Agora vamos rodar nosso servidor com um
go run server/main.go
Bom a parte de servidor está pronta, porém nós precisamos testar para ver tudo funcionando, para isso vamos criar uma nova pasta chamada de client
, e dentro dela vamos criar um arquivo main.go
. Dentro desse arquivo vamos colocar isso
package main
import (
"context"
"grpc-go/pb"
"log"
"google.golang.org/grpc"
)
func main(){
connection, err := grpc.Dial("localhost:9000", grpc.WithInsecure())
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer connection.Close()
client := pb.NewHelloServiceClient(connection)
request := &pb.HelloRequest{Name: "Seu nome"}
response, err := client.Hello(context.Background(), request)
if err != nil {
log.Fatalf("Failed to call: %v", err)
}
log.Printf("Response: %v", response.Msg)
}
Nós chamamos a função main
e nela começamos criando uma conexão com o gRPC e nessa função de conexão grpc.Dial
nós passamos dois parâmetros, o endereço do servidor e também uma função que nada mais é que o tipo de conexão que estamos fazendo, como nós estamos localhost, vamos usar o WithInsecure
.
Depois nós verificamos se não existe nenhum erro, logo após isso nós damos um defer connection.Close()
, que basicamente vai fechar a nossa conexão antes da função ser encerrada.
Criamos também nossa variável de cliente, onde ele conecta diretamente com o módulo gerado pelo protobuf, e também montamos nosso request puxando também o módulo gerado automaticamente.
Depois chamamos nossa função Hello
que está atrelada a nossa variável client
e nela nós passamos nossos dois parâmetros necessários, o context.Background()
que não nos interessa e também o nosso request
que nós criamos acima.
Após isso nós verificamos se não deu nenhum erro durante a chamada da nossa função e então nós mostramos o resultado que o nosso servidor nos enviou. Vamos testar isso dando um
go run client/main.go
Caso o console exiba a mensagem Response: Hello Seu nome
foi porque nosso servidor e nosso cliente funcionou perfeitamente.
Conclusão
Mesmo que ainda hoje boa parte da WEB se concentra em arquiteturas REST, usar gRPC para microserviços ou até mesmo aplicações maiores pode ser uma vantagem para você e para seu time, as vantagens que o gRPC trás valem muito a pena para serem usadas no dia a dia principalmente quando o foco é reduzir latências.
Top comments (0)