rust使用grpc入门示例

文件目录

image

toml 用的不是新版本都是稳定版本,新版本各种报错不兼容

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

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

proto

syntax = "proto3";

package biz_activity;

// 定义业务活动消息
message BizActivity {
  optional string id = 1;
  optional string name = 2;
  optional int32 status = 3;
  optional int64 create_time = 4;  // 使用时间戳
  optional string additional_field = 5;
}

// 定义请求和响应消息
message GetBizActivityRequest {
  string id = 1;
}

message GetBizActivityResponse {
  BizActivity activity = 1;
}

message CreateBizActivityRequest {
  BizActivity activity = 1;
}

message CreateBizActivityResponse {
  BizActivity activity = 1;
}

message UpdateBizActivityRequest {
  BizActivity activity = 1;
}

message UpdateBizActivityResponse {
  BizActivity activity = 1;
}

message DeleteBizActivityRequest {
  string id = 1;
}

message DeleteBizActivityResponse {
  bool success = 1;
}

message ListBizActivitiesRequest {
  optional int32 page = 1;
  optional int32 size = 2;
}

message ListBizActivitiesResponse {
  repeated BizActivity activities = 1;
}

// 定义 gRPC 服务
service BizActivityService {
  rpc GetBizActivity(GetBizActivityRequest) returns (GetBizActivityResponse);
  rpc CreateBizActivity(CreateBizActivityRequest) returns (CreateBizActivityResponse);
  rpc UpdateBizActivity(UpdateBizActivityRequest) returns (UpdateBizActivityResponse);
  rpc DeleteBizActivity(DeleteBizActivityRequest) returns (DeleteBizActivityResponse);
  rpc ListBizActivities(ListBizActivitiesRequest) returns (ListBizActivitiesResponse);
}

build.rs

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 编译 .proto 文件用于 gRPC 服务
    tonic_build::compile_protos("src/protos/biz_activity.proto")?;
    Ok(())
}

server

use tonic::{transport::Server, Request, Response, Status};
use std::time::{SystemTime, UNIX_EPOCH};

// 引入生成的 gRPC 代码
pub mod biz_activity {
    tonic::include_proto!("biz_activity");
}

use biz_activity::{
    biz_activity_service_server::{BizActivityService, BizActivityServiceServer},
    BizActivity, GetBizActivityRequest, GetBizActivityResponse,
    CreateBizActivityRequest, CreateBizActivityResponse,
    UpdateBizActivityRequest, UpdateBizActivityResponse,
    DeleteBizActivityRequest, DeleteBizActivityResponse,
    ListBizActivitiesRequest, ListBizActivitiesResponse,
};

// 模拟数据库存储
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

#[derive(Default)]
pub struct BizActivityServiceImpl {
    // 使用内存存储模拟数据库
    activities: Arc<Mutex<HashMap<String, BizActivity>>>,
}

#[tonic::async_trait]
impl BizActivityService for BizActivityServiceImpl {
    async fn get_biz_activity(
        &self,
        request: Request<GetBizActivityRequest>,
    ) -> Result<Response<GetBizActivityResponse>, Status> {
        let req = request.into_inner();
        let activities = self.activities.lock().unwrap();
        let activity = activities.get(&req.id).cloned();

        match activity {
            Some(activity) => {
                let response = GetBizActivityResponse {
                    activity: Some(activity),
                };
                Ok(Response::new(response))
            }
            None => Err(Status::not_found("Activity not found")),
        }
    }

    async fn create_biz_activity(
        &self,
        request: Request<CreateBizActivityRequest>,
    ) -> Result<Response<CreateBizActivityResponse>, Status> {
        let req = request.into_inner();
        let mut activity = req.activity.unwrap();
        
        // 如果没有提供 ID,则生成一个
        if activity.id.is_none() {
            let id = format!("activity_{}", 
                SystemTime::now()
                    .duration_since(UNIX_EPOCH)
                    .unwrap()
                    .as_secs()
            );
            activity.id = Some(id);
        }

        // 设置创建时间
        if activity.create_time.is_none() {
            let timestamp = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_secs() as i64;
            activity.create_time = Some(timestamp);
        }

        let id = activity.id.clone().unwrap();
        let mut activities = self.activities.lock().unwrap();
        activities.insert(id.clone(), activity.clone());
        
        let response = CreateBizActivityResponse {
            activity: Some(activity),
        };

        Ok(Response::new(response))
    }

    async fn update_biz_activity(
        &self,
        request: Request<UpdateBizActivityRequest>,
    ) -> Result<Response<UpdateBizActivityResponse>, Status> {
        let req = request.into_inner();
        let activity = req.activity.unwrap();
        let id = activity.id.clone().unwrap();
        
        let mut activities = self.activities.lock().unwrap();
        
        if !activities.contains_key(&id) {
            return Err(Status::not_found("Activity not found"));
        }

        activities.insert(id.clone(), activity.clone());
        
        let response = UpdateBizActivityResponse {
            activity: Some(activity),
        };

        Ok(Response::new(response))
    }

    async fn delete_biz_activity(
        &self,
        request: Request<DeleteBizActivityRequest>,
    ) -> Result<Response<DeleteBizActivityResponse>, Status> {
        let req = request.into_inner();
        let mut activities = self.activities.lock().unwrap();
        let existed = activities.remove(&req.id);

        let response = DeleteBizActivityResponse {
            success: existed.is_some(),
        };

        Ok(Response::new(response))
    }

    async fn list_biz_activities(
        &self,
        request: Request<ListBizActivitiesRequest>,
    ) -> Result<Response<ListBizActivitiesResponse>, Status> {
        let req = request.into_inner();
        let activities = self.activities.lock().unwrap();
        
        let mut activity_list: Vec<BizActivity> = activities.values().cloned().collect();
        
        // 分页处理 - 修复字段访问方式
        let page = req.page.unwrap_or(1) as usize;
        let size = req.size.unwrap_or(10) as usize;
        let start = (page - 1) * size;
        let end = start + size;
        
        if start < activity_list.len() {
            let end = std::cmp::min(end, activity_list.len());
            activity_list = activity_list[start..end].to_vec();
        } else {
            activity_list = vec![];
        }

        let response = ListBizActivitiesResponse {
            activities: activity_list,
        };

        Ok(Response::new(response))
    }
}

pub async fn start_grpc_server() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "127.0.0.1:50051".parse().unwrap();
    let service_impl = BizActivityServiceImpl::default();

    println!("gRPC 服务器运行在 {}", addr);

    Server::builder()
        .add_service(BizActivityServiceServer::new(service_impl))
        .serve(addr)
        .await?;

    Ok(())
}

client

use tonic::transport::Channel;
use std::time::{SystemTime, UNIX_EPOCH};

// 引入生成的 gRPC 代码
pub mod biz_activity {
    tonic::include_proto!("biz_activity");
}

use biz_activity::{
    biz_activity_service_client::BizActivityServiceClient,
    BizActivity, GetBizActivityRequest, CreateBizActivityRequest, 
    UpdateBizActivityRequest, DeleteBizActivityRequest, ListBizActivitiesRequest,
};

pub struct BizActivityGrpcClient {
    client: BizActivityServiceClient<Channel>,
}

impl BizActivityGrpcClient {
    pub async fn new(address: &str) -> Result<Self, Box<dyn std::error::Error>> {
        let client = BizActivityServiceClient::connect(address.to_string()).await?;
        Ok(BizActivityGrpcClient { client })
    }

    pub async fn create_activity(&mut self, activity: BizActivity) -> Result<BizActivity, tonic::Status> {
        let request = tonic::Request::new(CreateBizActivityRequest {
            activity: Some(activity),
        });

        let response = self.client.create_biz_activity(request).await?;
        Ok(response.into_inner().activity.unwrap())
    }

    pub async fn get_activity(&mut self, id: String) -> Result<Option<BizActivity>, tonic::Status> {
        let request = tonic::Request::new(GetBizActivityRequest { id });

        match self.client.get_biz_activity(request).await {
            Ok(response) => Ok(response.into_inner().activity),
            Err(status) => {
                if status.code() == tonic::Code::NotFound {
                    Ok(None)
                } else {
                    Err(status)
                }
            }
        }
    }

    pub async fn update_activity(&mut self, activity: BizActivity) -> Result<BizActivity, tonic::Status> {
        let request = tonic::Request::new(UpdateBizActivityRequest {
            activity: Some(activity),
        });

        let response = self.client.update_biz_activity(request).await?;
        Ok(response.into_inner().activity.unwrap())
    }

    pub async fn delete_activity(&mut self, id: String) -> Result<bool, tonic::Status> {
        let request = tonic::Request::new(DeleteBizActivityRequest { id });

        let response = self.client.delete_biz_activity(request).await?;
        Ok(response.into_inner().success)
    }

    pub async fn list_activities(&mut self, page: Option<i32>, size: Option<i32>) -> Result<Vec<BizActivity>, tonic::Status> {
        let request = tonic::Request::new(ListBizActivitiesRequest {
            page,
            size,
        });

        let response = self.client.list_biz_activities(request).await?;
        Ok(response.into_inner().activities)
    }
}

pub async fn run_grpc_client_test() -> Result<(), Box<dyn std::error::Error>> {
    println!("连接到 gRPC 服务器...");
    let mut client = BizActivityGrpcClient::new("http://127.0.0.1:50051").await?;

    // 创建一个活动
    println!("创建业务活动...");
    let new_activity = BizActivity {
        id: Some("test_activity_1".to_string()),
        name: Some("测试活动".to_string()),
        status: Some(1),
        create_time: Some(
            SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_secs() as i64
        ),
        additional_field: Some("额外信息".to_string()),
    };

    let created_activity = client.create_activity(new_activity).await?;
    println!("创建的活动: {:?}", created_activity.name);

    // 获取活动
    println!("获取业务活动...");
    if let Some(activity) = client.get_activity("test_activity_1".to_string()).await? {
        println!("获取的活动: {:?}", activity.name);
    } else {
        println!("未找到活动");
    }

    // 更新活动
    println!("更新业务活动...");
    let mut updated_activity = created_activity.clone();
    updated_activity.name = Some("更新后的活动".to_string());
    updated_activity.status = Some(2);
    
    let updated_activity = client.update_activity(updated_activity).await?;
    println!("更新后的活动: {:?}", updated_activity.name);

    // 列出活动
    println!("列出业务活动...");
    let activities = client.list_activities(Some(1), Some(10)).await?;
    println!("找到 {} 个活动", activities.len());
    for activity in &activities {
        println!("  - ID: {:?}, Name: {:?}", activity.id, activity.name);
    }

    // 删除活动
    println!("删除业务活动...");
    let deleted = client.delete_activity("test_activity_1".to_string()).await?;
    println!("删除结果: {}", deleted);

    // 再次尝试获取已删除的活动
    println!("尝试获取已删除的活动...");
    if let Some(activity) = client.get_activity("test_activity_1".to_string()).await? {
        println!("获取的活动: {:?}", activity.name);
    } else {
        println!("活动已被删除,未找到");
    }

    println!("gRPC 客户端测试完成!");
    Ok(())
}

lib

pub mod grpc_client;
pub mod grpc_server;

main

mod grpc_server;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("启动 gRPC 服务器...");
    
    // 启动 gRPC 服务器
    grpc_server::start_grpc_server().await?;
    
    Ok(())
}

client_test 测试客户端,对应下面的启动命令 cargo run --bin client_test

use rbatiss::grpc_client;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("启动 gRPC 客户端测试...");
    
    // 运行客户端测试
    grpc_client::run_grpc_client_test().await?;
    
    Ok(())
}

启动命令

服务器 cargo run --bin 项目名

客户端 cargo run --bin client_test

image

posted @ 2025-12-24 09:44  朝阳1  阅读(3)  评论(0)    收藏  举报