GRPC
Link repositório
Logo abaixo vou explicar o que é GRPC, breve conceito de stream, rpc e sua importância, protocol buffer, HTTP2 e uma comunicação em Go Land, em projeto simples com comunicação Unary.
Grpc é uma estrutura robusta, moderna de código aberto e alto desempenho que pode ser executada em qualquer ambiente, facilita a comunicação entre sistemas e serviços indiferente das linguagens de programação utilizadas. Ela trabalha com RPC (Remote procedure call) pode conectar serviços de forma eficiente em e entre data centers com suporte conectável para balanceamento de carga, rastreamento, verificação de integridade e autenticação. Também é aplicável na última milha da computação distribuída para conectar dispositivos, aplicativos móveis e navegadores a serviços de back-end.
Sua aplicação/serviço utilizando protocol buffer, pode gerar contratos, criando (stubs — bibliotecas de forma automática), que é um poderoso conjunto de ferramentas e linguagem de serialização binária, ideal para micro serviços, assim como Rest.
Streaming bidirecional e autenticação integrada
Trabalha com Streaming bidirecional e autenticação plugável totalmente integrada com transporte baseado em HTTP/2, com este poder de protocolo, ele consegue enviar informações criptografadas em formato binário. Utiliza stream de dados permitindo uma conexão mais assertiva, enviando dados de forma continua sem request ou response.
RPC — Remote Procedure Call
É uma comunicação em que sempre trabalhamos de forma declarativa (enviamos/"recebemos", de acordo (contrato que o servidor formaliza)) onde temos uma comunicação de cliente X Servidor tendo request que é requisição que enviamos e o response que obtemos ao efetuar essa chamada.
Protocol Buffers
Protocol buffer criado pelo google, se destacou pela sua linguagem neutra com mecanismo de código aberto maduro para serializar dados estruturados (embora possa ser usado com outros formatos de dados, como JSON).
Para trabalhar com protocol buffer é necessário definir a estrutura dos dados que você deseja serializar em um arquivo proto: este é um arquivo de texto comum com uma extensão .proto. Os dados do protocol buffer são estruturados como mensagens, em que cada mensagem é um pequeno registro de informações contendo uma série de itens nome-valor chamados campos. Em caso de dúvidas acesse o doc.
syntax = "proto3";
package pb;
option go_package = "../pb";
message Book {
string id=1;
string description=2;
string title=3;
}
message BookResultStream {
string status = 1;
Book book = 2;
}
message Books {
repeated Book book = 1;
}
service BookService {
rpc AddBook (Book) returns (Book);
}
Protocol Buffer VS Json
Uma breve visão geral:
- Protocol buffer, são chamados de Protobuf, criados pelo Google, tem o objetivo de fornecer uma maneira melhor, em comparação com o XML, para efetuar serialização/deserialização . Tem a função em tornar mais simples, menor,rápido e mais fácil de manter do que o XML. Mas, esse protocolo até ultrapassou o JSON com melhor desempenho, melhor capacidade de manutenção e tamanho menor.
- JSON (JavaScript Object Notation) é um formato leve e é na linguagem de programação JavaScript.
JSON é “autodescritivo” e fácil de entender. - Exemplo Json
{
"id": "1",
"description": "Xuxa para baixinhos livro de colorir",
"title": "Xuxa para baixinhos"
}
- Exemplo Protobuf
syntax = "proto3";
package pb;
option go_package = "../pb";
message Book {
string id=1;
string description=2;
string title=3;
}
message BookResultStream {
string status = 1;
Book book = 2;
}
message Books {
repeated Book book = 1;
}
service BookService {
rpc AddBook (Book) returns (Book);
}
Vantagens do Protobuf:
- Simples, rápido e menor em tamanho.
- Suporte a RPC: as interfaces RPC do servidor podem ser declaradas como parte dos arquivos de protocolo.
- Validação da estrutura: Tendo uma estrutura pré-definida e maior quando comparada ao JSON, as mensagens serializadas no Protobuf podem ser validadas automaticamente pelo código responsável.
Json em relação Protobuf:
- JSON tem formato de texto e com estrutura simples, consegue ser visto e analisado a olhos humanos, mas dependendo do seu tamanho comparado com formato binário as troca de informações tornam se fluidas. Documentação para json é fácil e simples de comparar, agora em relação a protobuf. Abaixo segue uma breve explicação do stackoverflow indicando a diferença entre Json vs Protobuf, e termina no início do tópico de HTTP2.
When to use JSON (Reference)
- You need or want data to be human readable
- Data from the service is directly consumed by a web browser
- Your server side application is written in JavaScript
- You aren’t prepared to tie the data model to a schema
- You don’t have the bandwidth to add another tool to your arsenal
- The operational burden of running a different kind of network service is too great
Pros of ProtoBuf
- Relatively smaller size
- Guarantees type-safety
- Prevents schema-violations
- Gives you simple accessors
- Fast serialization/deserialization
- Backward compatibility
While we are at it, have you looked at flatbuffers?
Some of the aspects are covered here google protocol buffers vs json vs XML
https://codeclimate.com/blog/choose-protocol-buffers/
HTTP2
O HTTP2 é um protocolo binário, diferente do HTTP1 que é em formato texto. Apenas com este conceito já temos muitas diferenças. Com um protocolo binário é mais fácil saber o começo e fim dos pacotes, o que é bem mais complicado de fazer com protocolos textuais. Ou seja, a implementação em si é mais simples.
Compressão automática
No HTTP 1.1 habilitamos o GZIP para comprimir as informações que mandamos em nossas respostas. Uma boa prática que precisa ser habilitada explicitamente. No HTTP 2 GZIP é padrão e obrigatório.
Somente os headers que mudam são re-enviados
No HTTP 1.1 os headers são enviados em plain text, em cada requisição (o famoso User-Agent
por exemplo). No HTTP 2 os headers são binários e comprimidos, diminuindo o volume de dados. Além disso, é possível reaproveitar os headers para as requisições seguintes. Dessa forma, só temos que mandar os cabeçalhos que mudam. Isso reduz as requisições e as deixa menos volumosas.
Paralelização de requests
Para cada recurso que uma página possui, um request feito então para carrega-los mais rapidamente precisamos paralelizar essas requisições. O problema é que o HTTP 1.1 é um protocolo sequencial, só podemos fazer 1 request por vez. A solução é abrir mais de uma conexão ao mesmo tempo, paralelizando os requests em 4 a 8 requests (é o limite que temos). Uma forma comum de lidar com isso é usar vários hostnames na página (pag.com e img.pag.com), assim ganhamos mais conexões paralelas.
No HTTP 2 as requisições e respostas são paralelas automaticamente em uma única conexão. É o chamado multiplexing.
Priorização de requests
Uma otimização interessante é a de facilitar a renderização inicial, priorizando os recursos necessários para o usuário ver a página primeiro (CSS) e interagir (JS) depois.
No HTTP 2 podemos indicar nos requests quais deles são mais importantes através de priorização numérica. Assim o browser pode dar prioridade alta a um arquivo CSS no head que bloqueia a renderização, e prioridade mais baixa para um JS assíncrono no fim da página.
Server Push
Uma gambiarra comum no HTTP 1.1 é fazer inline de recursos, visando a renderização inicial mais rápida. O grande problema aqui é que anulamos o cache do navegador. CSS junto do HTML não pode ser cacheado independentemente.
Aí vem o Server Push no HTML 2. A ideia é ter o servidor mandando alguns recursos para o navegador sem ele ter requisitado ainda. O servidor “empurra” para o navegador recursos que ele sabe que serão requisitados logo. Assim quando o navegador precisar do recurso, já vai ter em cache e não fará um request.
Segurança
Um tempo atrás havia uma discussão se HTTP 2 permitiria uso sem SSL (parei de acompanhar faz algum tempo), mas na prática apenas conexões seguras HTTPS serão suportadas. Assim temos segurança e privacidade mais estabelecidas com o protocolo.
Por fim, recomendo o episódio do Hipsters.tech sobre HTTP2, vai te dar ainda mais informações sobre o assunto.
GRPC — Unary Call
É apenas feito uma única chamada rpc , o cliente envia uma única solicitação e o servidor responde com uma única mensagem.
Estrutura do projeto
Como gerar o arquivo proto, preparando para o server
Digite esse comando abaixo do go mod init, para criar o arquivo go.mod e também efetue o sync dos módulos de importação.
go mod init github.com/acpereira/grpc-ads, faça o sync, depois rode o comando abaixo e import as libs.
protoc — proto_path=proto proto/*.proto — go_out=pb — go-grpc_out=pb
Abra o terminal e digite o comando acima, veja a geração arquivo .grpc para efetuar a comunicação.
Efetue os imports.
Criando a camada de serviço
package services
import (
"context"
"fmt"
"github.com/acpereira/grpc-ads/pb"
)
type BookService struct {
pb.UnimplementedBookServiceServer
}
func NewBookService() *BookService{
return &BookService{}
}
func (*BookService) AddBook(ctx context.Context, req *pb.Book) (*pb.Book, error) {
fmt.Println(req.Description)
return &pb.Book{
Id: "123",
Description: req.GetDescription(),
Title: req.GetTitle(),
}, nil
}
Criando o camada de infra (server) para receber as informações
func main() {
lis, err := net.Listen("tcp", "localhost:3000")
if err != nil {
log.Fatal("Could not connect %v", err)
}
grpcServer := grpc.NewServer()
pb.RegisterBookServiceServer(grpcServer, &services.BookService{})
reflection.Register(grpcServer)
if err := grpcServer.Serve(lis); err != nil {
log.Fatal("Could not serve %v", err)
}
}
Agora vamos rodar, abra o terminal.
Criando o camada de infra (Client, consumir) para receber as informações
func main() {
connection, err := grpc.Dial("localhost:3000", grpc.WithInsecure())
if err != nil {
log.Fatalf("Could not connect to gRPC Server: %v", err)
}
defer connection.Close()
client := pb.NewBookServiceClient(connection)
AddBook(client)
}
func AddBook(client pb.BookServiceClient) {
req := &pb.Book{
Id: "1",
Description: "Xuxucao DVD para baixainhos",
Title: "Xuxucao Dvd",
}
res, err := client.AddBook(context.Background(), req)
if err != nil {
log.Fatalf("Could not make gRPC request: %v", err)
}
fmt.Println(res)
}
Agora, para o antigo server, rode novamente, abra outro terminal e execute o comando abaixo e veja o resultado.
go run cmd/client/client.go
Próximas postagens será sobre stream e streaming!