Rust gRPC---Tonic实战

API

一个API做了两件事

  • 客户端发起请求Request
  • 服务端作出响应Response

REST是什么

REST(Representational State Transfer):表现层状态传输,是一种设计风格,通常将 HTTP API 称为 RESTful API、RESTful 服务或 REST 服务

  • 资源由URL决定
  • 通过GET、POST、PUT、DELETE、PATCH、OPTIONS、HEAD、TRACE方法来操作资源
  • 操作资源的表现形式是XML、HTML、JSON等格式
  • 由客户端保存状态

RPC是什么

RPC(Remote Procedure Call):远程过程调用,像调用本地函数一样调用远程函数,grpc是RPC的一种实现

Tonic是什么

tonic基于HTTP/2构建,grpc的Rust实现

在这里插入图片描述

Protocol Buffers

Protocol Buffers协议缓冲区

服务方法

允许定义四种服务方法

  • 一元RPC:客户端向服务端发送单个请求并得到单个响应,就像调用普通函数一样
  • 服务端流式RPC:客户端向服务端发送请求并获取流以读取一系列消息,客户端从返回的流中读取直到没有消息
  • 客户端流式RPC:客户端使用流编写一系列消息发送到服务端,等待服务端做出响应
  • 双向流式RPC:双方使用读写流发送一系列消息,两个流独立运行

Protocol Buffers语法

  • 文件名以.proto结尾
  • 类似于json,体积更小、速度更快,会生成本机语言绑定

message:定义数据传递格式

  • 一个proto文件可以定义多个消息,消息可以嵌套

  • required:proto2必填、proto3不需要填

  • optional:可选字段

  • repeate:可重复字段

  • 标识号:每个字段必须要有一个唯一的标识号,范围1~2^29 -1(19000-19999保留标识号不能用)
    service:RPC服务接口

认证

  • SSL/TLS认证
    • TLS(Transport layerSecurity)安全传输层,建立在TCP协议上,前身是SSL(Secure Socket Layer)安全套接字层,将应用层的报文进行加密后再交由TCP传输
  • 基于Token认证
  • 自定义认证

安装protobuf

https://github.com/protocolbuffers/protobuf/releases/tag/v28.2

在这里插入图片描述

解压后将路径加入PATH

在这里插入图片描述

验证

protoc --version

Tonic实战

创建项目

cargo new tonic-server

cargo new tonic-client

两个项目都添加依赖

[dependencies]
tonic = "0.12.2"
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] }
# grpc编码器
prost = "0.13.2"
# 我们只在构建的时候需要它
[build-dependencies]
tonic-build = "0.12.2"

在两个项目同级新建proto\order.proto

syntax = "proto3";// 指定proto版本
package order;// 指定包名
// 定义服务
service OrderService {
    rpc GetOrder (OrderRequest) returns (OrderResponse);
    rpc SetOrder (Order) returns (OrderResponse);
}
// 定义message类型
message OrderRequest {
    int32 id = 1;
}

message OrderResponse {
    int32 id = 1;
    string description = 2;
    double price = 3;
}

message Order {
    int32 id = 1;
    string description = 2;
    double price = 3;
}

两个项目根目录新建build.rs,用于编译客户端和服务端的代码

// 告诉tonic-build,需要编译哪些文件
fn main() -> Result<(), Box<dyn std::error::Error>> {
   
    tonic_build::compile_protos("../proto/order.proto")?;// 注意导入的是项目外也就是项目同级的文件夹
    Ok(())
}

此时两个项目编译

cargo build

生成的rust代码在target\debug\build\tonic-server-****\out\order.rs

server项目新建src\order_service.rs

use tonic::{
    Request, Response, Status };
use order::order_service_server::{
    OrderService, OrderServiceServer };
use order::{
    OrderRequest, OrderResponse, Order };

pub mod order {
   
    tonic::include_proto!("order");// 使用宏引入proto文件
}

#[derive(Debug, Default)] // 实现打印、默认值
pub struct MyOrderService;

#[tonic::async_trait] // 异步方法
impl OrderService for MyOrderService {
   
    // 获取订单
    async fn get_order(
        &self,
        request: Request<OrderRequest>
    ) -> Result<Response<OrderResponse>, Status> {
   
        let req = request.into_inner();
        let response = OrderResponse {
   
            id: req.id,
            description: format!("Order {}", req.id),
            price: 100.0,
        };
        Ok(Response::new(response))
    }
    // 创建订单
    async fn set_order(&self, request: Request<Order>) -> Result<Response<OrderResponse>, Status> {
   
        let order = request.into_inner();
        let response = OrderResponse {
   
            id: order.id,
            description: order.description,
            price: order.price,
        };
        Ok(Response::new(response))
    }
}
// 启动grpc服务
pub fn create_server() -> OrderServiceServer<MyOrderService> {
   
    OrderServiceServer::new(MyOrderService::default())
}

server项目main.rs

mod order_service;
use order_service::create_server;
use tonic::transport::Server;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
   
    // 监听端口
    let addr = "[::1]:50051".parse()?;
    // 创建grpc服务
    let order_service = create_server();
    println!("OrderService listening on {}", addr);
    // 启动服务
    Server::builder().add_service(order_service).serve(addr).await?;
    Ok(())
}

此时启动server项目

cargo run

使用Apifox创建grpc请求

在这里插入图片描述

设置端口[::1]:50051

在这里插入图片描述

导入proto文件

在这里插入图片描述

此时调用即可响应请求

{
   
    "id": 0,
    "description": "Order 0",
    "price": 100
}

client项目main.rs

use order::order_service_client::OrderServiceClient;
use order::{
    OrderRequest, Order };

pub mod order {
   
    tonic::include_proto!("order");
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
   
    // 连接到gRPC服务端
    let mut client = OrderServiceClient::connect("http://[::1]:50051").await?;
    // 创建请求
    let request = tonic::Request::new(OrderRequest {
    id: 1 });
    // 发送请求并等待响应
    let response = client.get_order(request).await?;
    println!("RESPONSE={:?}", response);

    let order = Order {
   
        id: 1,
        description: "New Order".to_string(),
        price: 150.0,
    };
    // 创建请求
    let request = tonic::Request::new(order);
    // 发送请求并等待响应
    let response = client.set_order(request).await?;
    println!("RESPONSE={:?}", response);

    Ok(())
}

运行即可调用服务端的方法

cargo run

响应数据

RESPONSE=Response { metadata: MetadataMap { headers: {"content-type": "application/grpc", "date": "Fri, 20 Sep 2024 17:35:55 GMT", "grpc-status": "0"} }, message: OrderResponse { id: 1, description: "Order 1", price: 100.0 }, extensions: Extensions }
RESPONSE=Response { metadata: MetadataMap { headers: {"content-type": "application/grpc", "date": "Fri, 20 Sep 2024 tent-type": "application/grpc", "date": "Fri, 20 Sep 2024 17:35:55 GMT", "grpc-status": "0"} }, message: OrderResponse { id: 1, description: "Order 1", price: 100.0 }, extensions: Extensions }
RESPONSE=Response { metadata: MetadataMap { headers: {"content-type": "application/grpc", "date": "Fri, 20 Sep 2024 17:35:55 GMT", "grpc-status": "0"} }, message: OrderResponions: Extensions }
RESPONSE=Response { metadata: MetadataMap { headers: {"content-type": "application/grpc", "date": "Fri, 20 Sep 2024 17:35:55 GMT", "grpc-status": "0"} }, message: OrderRespontent-type": "application/grpc", "date": "Fri, 20 Sep 2024 17:35:55 GMT", "grpc-status": "0"} }, message: OrderResponse { id: 1, description: "New Order", price: 150.0 }, extensions: Extensions }

一元RPC、服务端流式RPC、客户端流式RPC、双向流式RPC

example.proto

syntax = "proto3";

package example;

service ExampleService {
  // Unary RPC
  rpc UnaryCall(RequestMessage) returns (ResponseMessage);

  // Server-side streaming RPC
  rpc ServerStream(RequestMessage) returns (stream ResponseMessage);

  // Client-side streaming RPC
  rpc ClientStream(stream RequestMessage) returns (ResponseMessage);

  // Bidirectional streaming RPC
  rpc BidiStream(stream RequestMessage) returns (stream ResponseMessage);
}

message RequestMessage {
  string message = 1;
}

message ResponseMessage {
  string message = 1;
}

server项目build.rs

// 告诉tonic-build,需要编译哪些文件
fn main() -> Result<(), Box<dyn std::error::Error>> {
   
    tonic_build::compile_protos("../proto/example.proto")?;// 注意导入的是项目外也就是项目同级的文件夹
    Ok(())
}

server项目cargo.toml

[dependencies]
tonic = "0.12.2"
tokio = { version = "1.40.0", features = ["full"] }
prost = "0.13.2"
futures-util = "0.3.30"
tokio-stream = "0.1.14"
tonic-reflection = "0.12.2"
[build-dependencies]
tonic-build = "0.12.2"

server项目main.rs

use tonic::{
    transport::Server, Request, Response, Status };
use futures_util::Stream; // 使用 futures_util 提供的 Stream trait
use tokio_stream::wrappers::ReceiverStream; // 引入 tokio_stream
use tokio::sync::mpsc;
use std::pin::Pin;
use example::example_service_server::{
    ExampleService, ExampleServiceServer };
use example::{
    RequestMessage, ResponseMessage };

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

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

#[tonic::async_trait]
impl ExampleService for MyExampleService {
   
    //    1. 一元 RPC 调用
    async fn unary_call(
        &self,
        request: Request<RequestMessage>
    ) -> Result<Response<ResponseMessage>, Status> {
   
        println!("一元调用");
        let message = request.into_inner().message;
        Ok(
            Response::new(ResponseMessage {
   
                message: format!("Hello from Unary: {}", 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cci497

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值