Ir al contenido principal

Tonic: gRPC con Rust

 

Tonic es una implementación en Rust de gRPC, un marco de trabajo RPC general, de código abierto y de alto rendimiento que prioriza los dispositivos móviles y HTTP/2.

Es una librería que se creó para ofrecer soporte de primera clase para async/await y para servir como componente fundamental en sistemas de producción escritos en Rust.

Sus características principales son:

  • Transmisión bidireccional E/S asíncrona de alto rendimiento.
  • Interoperabilidad TLS con soporte de rustls.
  • Balanceo de carga.
  • Metadatos personalizados.
  • Autenticación.
  • Verificación de estado.

Para conocer mejor esta librería haremos un proyecto cliente-servidor con Rust como lenguaje base.

Requisitos:

  • Tener Rust instalado. 
  • Tener Cargo instalado.

Verifiquemos la versión de Rust:

$ rustc --version

Verifiquemos la versión de Cargo:

$ cargo version

Empezando con Tonic y Rust (con Cargo)

1. Crearemos un proyecto Rust con ayuda de Cargo, una vez creado nos ubicaremos en directorio:

$ cargo new demo-tonic-rust
$ cd demo-tonic-rust

2. Modificaremos el archivo ``Cargo.toml`` para agregar las dependencias:

[package]
name = "demo-tonic-rust"
version = "0.1.0"
edition = "2024"

[dependencies]
tokio = { version = "1", features = ["full"] }
tonic = "0.11"
prost = "0.12"

[build-dependencies]
tonic-build = "0.11"

3. Usaremos el archivo ``validar.proto`` del ejemplo de gRPC con Go del post anterior:

syntax = "proto3";

package validar;

option go_package = "demo-grpc-go/proto;validar";

service Validar {
  rpc EsMayorQue100 (NumeroRequest) returns (NumeroResponse);
}

message NumeroRequest {
  int32 valor = 1;
}

message NumeroResponse {
  bool es_mayor = 1;
}

4. Para la generación del código (con tonic-build) crearemos un archivo ``build.rs``:

fn main() {
    tonic_build::configure()
        .build_server(true)
        .build_client(true)
        .compile(&["proto/validar.proto"], &["proto"])
        .unwrap();
}

Esto compilará el .proto y generará el código Rust en target.

5. Crearemos el programa ``servidor.rs``:

use tonic::{transport::Server, Request, Response, Status};
use validar::validar_server::{Validar, ValidarServer};
use validar::{NumeroRequest, NumeroResponse};

pub mod validar {
    tonic::include_proto!("validar");
}

#[derive(Default)]
pub struct MyValidar {}

#[tonic::async_trait]
impl Validar for MyValidar {
    async fn es_mayor_que100(
        &self,
        request: Request<NumeroRequest>,
    ) -> Result<Response<NumeroResponse>, Status> {
        let numero = request.into_inner().valor;
        let respuesta = NumeroResponse {
            es_mayor: numero > 100,
        };
        Ok(Response::new(respuesta))
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "[::1]:50051".parse()?;
    let validar = MyValidar::default();

    println!("Servidor gRPC escuchando en {}", addr);

    Server::builder()
        .add_service(ValidarServer::new(validar))
        .serve(addr)
        .await?;

    Ok(())
}

6. Crearemos el programa ``cliente.rs``:

use tonic::transport::Channel;
use validar::validar_client::ValidarClient;
use validar::NumeroRequest;

pub mod validar {
    tonic::include_proto!("validar");
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = ValidarClient::connect("http://[::1]:50051").await?;

    let numero = 150; 
    let request = tonic::Request::new(NumeroRequest { valor: numero });

    let response = client.es_mayor_que100(request).await?;

    println!("Número enviado: {}", numero);
    println!("¿Es mayor a 100? {}", response.into_inner().es_mayor);

    Ok(())
}

7. Editamos el archivo ``Cargo.toml`` para agregar los "binarios" del cliente y servidor:

[package]
name = "demo-tonic-rust"
version = "0.1.0"
edition = "2024"

[dependencies]
tokio = { version = "1", features = ["full"] }
tonic = "0.11"
prost = "0.12"

[build-dependencies]
tonic-build = "0.11"

[[bin]]
name = "servidor"
path = "src/servidor.rs"

[[bin]]
name = "cliente"
path = "src/cliente.rs"

8. Compilamos el proyecto:

$ cargo build

9. Ejecutamos el servidor:

$ cargo run --bin servidor

10. En otra terminal ejecutamos el cliente:

$ cargo run --bin cliente

Si todo va bien, veremos esto en el servidor:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.32s
     Running `target\debug\servidor.exe`
Servidor gRPC escuchando en [::1]:50051

En el cliente:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.86s
     Running `target\debug\cliente.exe`
Número enviado: 150
¿Es mayor a 100? true

Lo cual es correcto, pues le hemos enviado un número entero con valor de 150.

11. Modifiquemos el ``cliente.rs`` de tal manera pueda enviar cualquier número al servidor.

use tonic::transport::Channel;
use validar::validar_client::ValidarClient;
use validar::NumeroRequest;
use std::env;

pub mod validar {
    tonic::include_proto!("validar");
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        eprintln!("Uso: cargo run --bin client <numero>");
        std::process::exit(1);
    }

    let numero: i32 = args[1].parse().expect("Debe ser un número entero");

    
    let mut client = ValidarClient::connect("http://[::1]:50051").await?;

    
    let request = tonic::Request::new(NumeroRequest { valor: numero });

    
    let response = client.es_mayor_que100(request).await?;

    println!("Número enviado: {}", numero);
    println!("¿Es mayor a 100? {}", response.into_inner().es_mayor);

    Ok(())
}

12. Ejecutamos el servidor:

$ cargo run --bin servidor

13. En otra terminal ejecutamos el cliente:

$ cargo run --bin cliente 1

Salida:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.79s
     Running `target\debug\cliente.exe 1`
Número enviado: 1
¿Es mayor a 100? false

¡Hemos creado nuestra primera aplicación Tonic y Rust!

Continuaremos con esta serie en próximas entregas.

Enlaces:

https://crates.io/crates/tonic
https://github.com/hyperium/tonic
https://crates.io/
https://rust-lang.org/es/



Comentarios

Entradas populares de este blog

Odin language: el lenguaje de programación orientado a datos para un desarrollo de software sensato

En esta serie sobre lenguajes de programación hablamos de Odin , un lenguaje orientado a datos. Un nuevo lenguaje de programación que nos podrá recordar a lenguajes como C++, Python, Golang o hasta Rust. Instalación (Windows): 1 2 3 4 $ git clone https://github.com/odin-lang/Odin $ cd Odin $ git pull $ build.bat release Debemos asegurarnos de crear la variable de entorno: Nombre: ODIN_HOME Valor: C:\Users\HP\Documents\pruebasOdin\Odin Ejemplo 1. Como primer programa tendremos al clásico "Hola, mundo". holamundo.odin package holamundo import " core:fmt " main : : proc ( ) { fmt . println ( " ¡Hola, mundo desde Odin! " ) } Construimos: $ odin build holamundo . odin - file Ejecutamos: $ holamundo .exe Recordar que si usamos Visual Studio Code podemos instalar el plugin de Odin Language: https://marketplace.visualstudio.com/items?itemName=DanielGavin.ols Ejemplo 2. Programa que muestre las variables que pod...

Programación Windows Batch (CMD) parte 3

Crear ciclos para efectuar operaciones tediosas nos ahorrará tiempo para realizar otras tareas. En está ocasión veremos ciclos con FOR . ¿Cuál es la síntaxis de bucle FOR en Windows Batch? Si está dentro de un archivo *.bat : FOR %%variable IN (seq) DO operaciones Si lo ejecutamos en una terminal: FOR %variable IN (seq) DO operaciones Ejemplo 1 . Recorrer una secuencia de números del 0 al 5: recorrer.bat @ echo off FOR %%i in ( 0 1 2 3 4 5 ) DO echo Hola no. %%i pause Nos imprimirá en pantalla: Hola no. 0 Hola no. 1 Hola no. 2 Hola no. 3 Hola no. 4 Hola no. 5 ¿Puedo usar contadores? Si, se pueden usar. Ejemplo 2 . Uso de contadores: contador.bat @ echo off set numeros = 1 2 3 4 5 6 7 8 9 10 set cont = 0 for %%a in ( %numeros% ) do ( echo Hola no. %%a :: Contador set /a cont+ = 1 ) echo Total: %cont% Este código nos imprimirá, además de los mensajes Hola no. 0 ..., el total de valores conta...

z/OS, ¿Qué es? y ¿Cuáles son sus herramientas?

  En este post veremos las herramientas de z/OS de IBM. z/OS es un sistema operativo de mainframe desarrollado por IBM.  Es parte de la serie IBM z Systems (anteriormente conocida como System z), que se utiliza en los sistemas mainframe de IBM. z/OS es uno de los sistemas operativos más utilizados en el mundo empresarial para ejecutar aplicaciones críticas y procesar grandes volúmenes de datos. Características clave de z/OS: Escalabilidad : z/OS es conocido por su capacidad para manejar grandes cargas de trabajo y escalabilidad vertical, lo que permite agregar recursos de hardware para satisfacer las demandas de procesamiento. Confiabilidad y disponibilidad : z/OS está diseñado para ofrecer alta disponibilidad y confiabilidad. Incorpora características como redundancia y recuperación ante fallas para garantizar la continuidad de las operaciones. Seguridad : Proporciona una amplia gama de funciones de seguridad, como autenticación, autorización y auditoría, para proteger los...