好,我们以你的 `euclidolap.proto` 文件为例,调整代码结构,让服务逻辑更清晰,同时将 `euclidolap` 模块分离到独立文件中

好,我们以你的 euclidolap.proto 文件为例,调整代码结构,让服务逻辑更清晰,同时将 euclidolap 模块分离到独立文件中。


假设文件结构调整

我们将 euclidolap.proto 生成的代码放到 src/euclidolap 模块中,同时将服务端逻辑分开组织。

最终文件结构如下:

project/
├── build.rs                # Protobuf 代码生成脚本
├── Cargo.toml              # Rust 项目配置
├── proto/
│   └── euclidolap.proto    # Protobuf 接口定义文件
├── src/
│   ├── euclidolap/
│   │   ├── mod.rs          # euclidolap 模块主入口
│   │   └── euclidolap.rs   # 自动生成的 gRPC 代码
│   ├── main.rs             # 主程序文件

1. build.rs 文件

确保 build.rseuclidolap.proto 的代码生成到 src/euclidolap/euclidolap.rs

fn main() {
    let out_dir = "src/euclidolap"; // 指定代码生成目录

    if let Err(e) = tonic_build::configure()
        .out_dir(out_dir) // 输出到 euclidolap 模块目录
        .compile(&["proto/euclidolap.proto"], &["proto"]) // 编译 .proto 文件
    {
        eprintln!("Failed to compile proto files: {}", e);
        std::process::exit(1);
    }

    // 通知 Cargo 监控 .proto 文件的变化
    println!("cargo:rerun-if-changed=proto/euclidolap.proto");
}

这段代码会将生成的 euclidolap.rs 放在 src/euclidolap/ 目录下。


2. src/euclidolap/mod.rs 文件

mod.rseuclidolap 模块的主入口,内容如下:

// 引用自动生成的 gRPC 代码
pub mod euclidolap {
    include!("euclidolap/euclidolap.rs");
}

这里通过 include! 将自动生成的 euclidolap.rs 文件引入模块中。


3. src/main.rs 文件

主文件 main.rs 使用 euclidolap 模块实现服务逻辑:

mod euclidolap;

use euclidolap::euclidolap::olap_service_server::{OLAPService, OLAPServiceServer};
use euclidolap::euclidolap::{OLAPRequest, OLAPResponse, Row};
use tonic::{transport::Server, Request, Response, Status};

// 自定义服务实现
#[derive(Debug, Default)]
pub struct MyOLAPService {}

#[tonic::async_trait]
impl OLAPService for MyOLAPService {
    async fn execute_operation(
        &self,
        request: Request<OLAPRequest>, // 客户端的请求
    ) -> Result<Response<OLAPResponse>, Status> {
        println!("Received request: {:?}", request);

        // 从请求中解析操作类型和语句
        let operation_type = request.into_inner().operation_type;
        let statement = request.into_inner().statement;

        println!("Operation Type: {}, Statement: {}", operation_type, statement);

        // 伪造一个响应,返回结果
        let response = OLAPResponse {
            rows: vec![Row {
                columns: vec!["Result 1".to_string(), "Result 2".to_string()],
            }],
        };

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

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "0.0.0.0:50052".parse().unwrap();
    let olap_service = MyOLAPService::default();

    println!(">>> EuclidOLAP Server is listening on {} <<<", addr);

    Server::builder()
        .add_service(OLAPServiceServer::new(olap_service)) // 注册 OLAP 服务
        .serve(addr)
        .await?;

    Ok(())
}

代码逻辑

  • MyOLAPService 服务实现

    • 实现了 OLAPService 接口中的 execute_operation 方法。
    • 解析客户端请求,返回一个伪造的响应数据(包含两列结果)。
  • 服务端启动

    • 服务监听 0.0.0.0:50052,通过 OLAPServiceServer::new 注册服务。

4. euclidolap.proto 示例

为了匹配代码示例,假设你的 euclidolap.proto 文件内容如下:

syntax = "proto3";

package euclidolap;

service OLAPService {
  rpc ExecuteOperation(OLAPRequest) returns (OLAPResponse);
}

message OLAPRequest {
  string operation_type = 1; // 操作类型
  string statement = 2;      // 多维查询语句
}

message OLAPResponse {
  repeated Row rows = 1;     // 返回的结果行
}

message Row {
  repeated string columns = 1; // 每行的列数据
}

代码生成后会包含什么

build.rs 会根据 euclidolap.proto 文件生成如下内容到 src/euclidolap/euclidolap.rs

  • Rust 结构体对应于 .proto 中的 message
  • Rust Trait 用于实现 gRPC 服务接口,例如 OLAPServiceServer

生成的代码会自动处理序列化和反序列化逻辑,简化服务实现。


运行流程

  1. 启动服务

    • 执行 cargo run 启动服务端,监听 0.0.0.0:50052
  2. 客户端请求

    • 客户端发送 OLAPRequest 请求,其中包含 operation_typestatement
  3. 服务端处理

    • 服务端解析请求,执行 execute_operation 方法。
    • 返回构造的 OLAPResponse,其中包含伪造的结果行。
  4. 响应客户端

    • 客户端接收到 OLAPResponse,完成请求。

总结

通过将 euclidolap 模块分离到单独的文件夹和模块中,你的代码结构更清晰且易于扩展。结合 build.rsTonic 的生成逻辑,服务端的实现变得高效而简洁。

posted on 2024-12-17 15:17  及途又八  阅读(32)  评论(0)    收藏  举报

导航