La comunicación entre procesos (IPC) permite que aplicaciones desarrolladas en distintos lenguajes intercambien datos y ejecuten procedimientos de forma remota. Esta arquitectura utiliza gRPC (basado en HTTP/2 y Protocol Buffers) para construir un servicio de cálculo de alto rendimiento. En este esquema, un servidor programado en Go expone funciones lógicas que son consumidas por clientes en Python y C#, garantizando la consistencia mediante un contrato de interfaz (IDL).
Modelado de la Arquitectura
La integración se basa en tres pilares: la definición del servicio en un archivo .proto, la generación automática de código (stubs/skeletons) y la implementación de la lógica de negocio en cada plataforma.
Definición de la Interfaz (Protocol Buffers)
El archivo de contrato define las estructuras de datos y los métodos disponibles. En este caso, el servicio ComputeEngine ofrece operaciones síncronas y una transmisión de flujo (streaming).
syntax = "proto3";
package compute_engine;
option go_package = "grpc_demo/server/gen";
option csharp_namespace = "ComputeEngine.Client";
service ComputeEngine {
// Cálculo de Fibonacci (Unario)
rpc GetFibonacci (SequenceRequest) returns (ValueResponse);
// Procesamiento de polinomios (Unario)
rpc ProcessMetrics (MetricRequest) returns (MetricResponse);
// Generación de secuencia (Server Streaming)
rpc FetchDataStream (StreamRequest) returns (stream DataPacket);
}
message SequenceRequest {
int32 position = 1;
}
message ValueResponse {
int64 result = 1;
}
message MetricRequest {
int32 sensor_id = 1;
double input_val = 2;
repeated double weights = 3;
}
message MetricResponse {
int32 sensor_id = 1;
double calculated_result = 2;
string status_msg = 3;
}
message StreamRequest {
int32 limit = 1;
}
message DataPacket {
int64 sequence_id = 1;
int64 timestamp = 2;
}
Implementación del Servidor en Go
El servidor implementa la interfaz generada y gestiona las peticiones entrantes en el puerto 50051.
package main
import (
"context"
"fmt"
"log"
"math"
"net"
"time"
"google.golang.org/grpc"
pb "grpc_demo/server/gen"
)
type engineServer struct {
pb.UnimplementedComputeEngineServer
}
func (s *engineServer) GetFibonacci(ctx context.Context, req *pb.SequenceRequest) (*pb.ValueResponse, error) {
n := req.Position
var a, b int64 = 0, 1
for i := int32(0); i < n; i++ {
a, b = b, a+b
}
return &pb.ValueResponse{Result: a}, nil
}
func (s *engineServer) ProcessMetrics(ctx context.Context, req *pb.MetricRequest) (*pb.MetricResponse, error) {
total := 0.0
for i, w := range req.Weights {
total += w * math.Pow(req.InputVal, float64(i))
}
return &pb.MetricResponse{
SensorId: req.SensorId,
CalculatedResult: total,
Status_msg: fmt.Sprintf("Procesado exitosamente a las %v", time.Now().Format(time.RFC3339)),
}, nil
}
func (s *engineServer) FetchDataStream(req *pb.StreamRequest, stream pb.ComputeEngine_FetchDataStreamServer) error {
for i := int32(0); i < req.Limit; i++ {
packet := &pb.DataPacket{
SequenceId: int64(i),
Timestamp: time.Now().UnixMilli(),
}
if err := stream.Send(packet); err != nil {
return err
}
time.Sleep(200 * time.Millisecond)
}
return nil
}
func main() {
listener, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("Error al abrir puerto: %v", err)
}
server := grpc.NewServer()
pb.RegisterComputeEngineServer(server, &engineServer{})
log.Println("Servidor gRPC ejecutándose en el puerto 50051...")
if err := server.Serve(listener); err != nil {
log.Fatalf("Error al iniciar servidor: %v", err)
}
}
Cliente en Python
Python es ideal para clientes ligeros o herramientas de aálisis que consumen servicios de backend.
import grpc
import engine_pb2
import engine_pb2_grpc
def run_client():
with grpc.insecure_channel('localhost:50051') as channel:
stub = engine_pb2_grpc.ComputeEngineStub(channel)
# 1. Llamada Unaria: Fibonacci
fib_resp = stub.GetFibonacci(engine_pb2.SequenceRequest(position=10))
print(f"Fibonacci(10): {fib_resp.result}")
# 2. Llamada Unaria: Métricas
metric_resp = stub.ProcessMetrics(engine_pb2.MetricRequest(
sensor_id=55,
input_val=1.5,
weights=[0.1, 0.5, 1.2]
))
print(f"Resultado Métricas: {metric_resp.calculated_result} - {metric_resp.status_msg}")
# 3. Server Streaming
print("Iniciando flujo de datos...")
stream = stub.FetchDataStream(engine_pb2.StreamRequest(limit=5))
for packet in stream:
print(f"Paquete recibido: ID={packet.sequence_id}, TS={packet.timestamp}")
if __name__ == '__main__':
run_client()
Cliente en C# (.NET)
Utilizando el SDK de .NET, gRPC se integra nativamente mediante la generación de código en tiempo de compilación.
using Grpc.Net.Client;
using ComputeEngine.Client;
using var channel = GrpcChannel.ForAddress("http://localhost:50051");
var client = new ComputeEngine.ComputeEngineClient(channel);
// Ejemplo de Fibonacci
var fibCall = await client.GetFibonacciAsync(new SequenceRequest { Position = 15 });
Console.WriteLine($"Fibonacci(15): {fibCall.Result}");
// Ejemplo de Procesamiento
var metricCall = await client.ProcessMetricsAsync(new MetricRequest
{
SensorId = 99,
InputVal = 2.0,
Weights = { 1.0, 2.0, 3.0 }
});
Console.WriteLine($"Resultado: {metricCall.CalculatedResult}");
// Ejemplo de Streaming
using var streamingCall = client.FetchDataStream(new StreamRequest { Limit = 3 });
await foreach (var packet in streamingCall.ResponseStream.ReadAllAsync())
{
Console.WriteLine($"Stream: {packet.SequenceId} a las {packet.Timestamp}");
}
Análisis de Capacidades Técnicas
- Eficiencia de Datos: A diferencia de JSON, Protocol Buffers utiliza una codificación binaria que reduce drásticamente el tamaño de los paquetes de red y el tiempo de serialización.
- HTTP/2: El uso de una única conexión TCP para múltiples flujos (multiplexación) elimina el bloqueo de cabecera de línea (Head-of-Line blocking) común en HTTP/1.1.
- Contrato Tipado: El archivo
.protoactúa como una "única fuente de verdad". Cualquier cambio en la estructura se detecta en tiempo de compilación en lenguajes como Go o C#. - Streaming Nativo: gRPC facilita el envío de datos en tiempo real sin necesidad de recurrir a WebSockets o long-polling complejos.
Consideraciones de Seguridad y Rendimiento
Para entornos de producción, es imperativo implementar TLS/SSL para cifrar el canal de comunicación. Aunque en ejemplos locales se utiliza insecure_channel, gRPC está diseñado para trabajar con certificados X.509 de manera nativa.
En cuanto al rendimiento, se recomienda la reutilización de canales (Stub/Channel pooling), ya que la creación de una conexión HTTP/2 es costosa en términos de recursos comparada con el envío de mensajes individuales.