Go gRPC ping barato
Introducción
En este artículo exploraremos gRPC con una aplicación sencilla de ping, básicamente realizaremos un ping y mediremos el tiempo que toma para que el mensaje vaya al servidor y regrese antes de reportarlo en la terminal. Puedes encontrar el código fuente aquí.
Protobuf
Como probablemente ya sepas, gRPC serializa datos utilizando protocol buffers. Vamos a crear un RPC Unario de la siguiente manera.
syntax = "proto3";
service PingService {
rpc Ping (PingRequest) returns (PingResponse);
}
message PingRequest {
string data = 1;
}
message PingResponse {
string data = 1;
}
Con este archivo estamos definiendo un servicio que será capaz de enviar una única PingRequest
y obtener una única PingResponse
. Tenemos un campo Data
que va y viene para enviar algunos bytes por la red (aunque no nos importe mucho, podría ser importante o crucial en una prueba de rendimiento).
Generando el código
Para poder utilizar protobuf necesitamos generar el código para la aplicación que estamos escribiendo, en este caso para golang, el comando sería el siguiente:
protoc -I ping/ ping/ping.proto --go_out=plugins=grpc:ping
Esto nos dará una definición del servicio y las estructuras necesarias para manejar los datos que hemos definido como mensajes.
Cliente
El cliente realiza la mayor parte del trabajo aquí. Como puedes ver, puedes suministrar 2 argumentos: uno para apuntar a otro host:puerto y el segundo para enviar una cadena de texto a tu gusto. Luego mide el tiempo que tarda en enviar y recibir el mensaje de vuelta, y lo imprime en la pantalla con una línea similar a la del comando ping
real en Linux.
package main
import (
"context"
"log"
"os"
"time"
pb "github.com/kainlite/grpc-ping/ping"
"google.golang.org/grpc"
)
const (
defaultAddress = "localhost:50000"
defaultData = "00"
)
func main() {
data := defaultData
address := defaultAddress
if len(os.Args) > 2 {
address = os.Args[1]
data = os.Args[2]
}
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewPingServiceClient(conn)
index := 0
for {
trip_time := time.Now()
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.Ping(ctx, &pb.PingRequest{Data: data})
if err != nil {
log.Fatalf("could not connect to: %v", err)
}
log.Printf("%d characters roundtrip to (%s): seq=%d time=%s", len(r.Data), address, index, time.Since(trip_time))
time.Sleep(1 * time.Second)
index++
}
}
Servidor
El servidor es simplemente un servidor de eco, ya que enviará de vuelta cualquier cosa que le envíes y lo registrará en la consola. De manera predeterminada, escuchará en el puerto 50000
.
package main
import (
"context"
"log"
"net"
pb "github.com/kainlite/grpc-ping/ping"
"google.golang.org/grpc"
)
const (
port = ":50000"
)
// server is used to implement ping.PingServer.
type server struct{}
// Ping implements ping.PingServer
func (s *server) Ping(ctx context.Context, in *pb.PingRequest) (*pb.PingResponse, error) {
log.Printf("Received: %v", in.Data)
return &pb.PingResponse{Data: "Data: " + in.Data}, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterPingServiceServer(s, &server{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
Probándolo
Ping regular
$ ping localhost -c 4
PING localhost(localhost (::1)) 56 data bytes
64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.145 ms
64 bytes from localhost (::1): icmp_seq=2 ttl=64 time=0.152 ms
64 bytes from localhost (::1): icmp_seq=3 ttl=64 time=0.154 ms
64 bytes from localhost (::1): icmp_seq=4 ttl=64 time=0.141 ms
--- localhost ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3144ms
rtt min/avg/max/mdev = 0.141/0.148/0.154/0.005 ms
Cliente
Esto es lo que veríamos en la terminal mientras lo probamos.
$ go run ping_client/main.go
2019/06/23 18:01:02 8 characters roundtrip to (localhost:50000): seq=0 time=1.941841ms
2019/06/23 18:01:03 8 characters roundtrip to (localhost:50000): seq=1 time=420.992µs
2019/06/23 18:01:04 8 characters roundtrip to (localhost:50000): seq=2 time=401.115µs
2019/06/23 18:01:05 8 characters roundtrip to (localhost:50000): seq=3 time=428.467µs
2019/06/23 18:01:06 8 characters roundtrip to (localhost:50000): seq=4 time=374.057µs
Como puedes ver, la conexión inicial toma un poco más de tiempo, pero después de eso el tiempo de ida y vuelta es muy consistente (por supuesto, nuestro simple ping no cubre errores, pérdida de paquetes, etc.).
Servidor
El servidor solo hace eco de lo que recibe y lo registra en la consola.
$ go run ping_server/main.go
2019/06/23 18:01:02 Received: 00
2019/06/23 18:01:03 Received: 00
2019/06/23 18:01:04 Received: 00
2019/06/23 18:01:05 Received: 00
2019/06/23 18:01:06 Received: 00
Notas finales
Como puedes ver, gRPC es bastante rápido y simplifica mucho todo lo que necesitas hacer para tener un sistema de mensajería altamente eficiente o una comunicación entre microservicios, por ejemplo. También es fácil generar el código base para cualquier lenguaje que prefieras y tener una interfaz común que todos deben aceptar.
Errata
Si encuentras algún error o tienes alguna sugerencia, por favor envíame un mensaje para que se corrija.
No tienes cuenta? Regístrate aqui
Ya registrado? Iniciar sesión a tu cuenta ahora.
-
Comentarios
Online: 0
Por favor inicie sesión para poder escribir comentarios.