Volo - Rust gRPC 框架入门

news2024/11/16 5:29:27

一、参考资料

Volo-GitHub

Volo-Overview

二、开发环境搭建

1、安装脚手架

# 安装 volo-cli
cargo install volo-cli
# 验证安装
volo help

2、编写 IDL 

# 文件 volo_demo.proto

syntax = "proto3";
package volo.demo;

message Item {
    int64 id = 1;
    string title = 2;
    string content = 3;

    map<string, string> extra = 10;
}

message GetItemRequest {
    int64 id = 1;
}

message GetItemResponse {
    Item item = 1;
}

service ItemService {
    rpc GetItem(GetItemRequest) returns (GetItemResponse);
}

IDL 全称为 Interface Definition Language ,即为 接口定义语言,具体语法参考 proto3。

为什么需要 IDL ?首先我们搞明白 RPC 是个什么概念:Remote Produce Call ,远程过程调用。说白了就是 A 机器从直接调用 B 机器上的某个函数或者方法。例如我在 A 机器用 Java 语言写了一个 Add 函数,返回变量加一后的结果,B 机器的 Go 语言程序想直接调用 A 机器的 Add 函数,就像在调用 B 机器本身程序内的函数一样。因此,需要引入 IDL 来定义这样一套接口标准,让 A、B 机器实现这种交互,就算开发语言不同,也能清楚对应调用哪个函数、什么类型的参数。

3、使用脚手架初始化项目

# 初始化项目,并生成模板代码
volo init --includes=idl volo-demo idl/volo_demo.proto

# 如果只需要增加一个 IDL(如 client 的 IDL)而不需要生成模板
volo idl add idl/volo_example.proto
#初始化项目后,项目根文件夹下多出以下内容
$ ls
Cargo.lock  Cargo.toml  idl/  rust-toolchain.toml  src/  target/  volo-gen/

三、实现一个 gRPC Server

1、在 src/lib.rs 下实现一个 get_item 方法。

#![feature(type_alias_impl_trait)]

pub struct S;

#[volo::async_trait]
impl volo_gen::volo::demo::ItemService for S {
    // 这部分是我们需要增加的代码
    async fn get_item(
        &self,
        _req: volo_grpc::Request<volo_gen::volo::demo::GetItemRequest>,
    ) -> core::result::Result<volo_grpc::Response<volo_gen::volo::demo::GetItemResponse>, volo_grpc::Status>
    {
        Ok(volo_grpc::Response::new(Default::default()))
    }
}

 2、然后执行指令编译二进制程序,生成指定的 volo_gen.rs 文件::

cargo update
cargo build

3、此时,我们就可以执行指令把 server 运行起来 :

cargo run --bin server

可以看到 Server 已经成功运行起来,这里用到了 tracing 日志监控输出,后续再详细介绍。

四、实现一个 Client 

1、新增依赖

[package]
name = "volo_demo"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1"
async-trait = "0.1"
lazy_static = "1"
tokio = { version = "1", features = ["full"] }
tracing = "0.1"
prost = "0.11"

pilota = "*"      # we recommend to use the latest framework version for new features and bug fixes
volo = "*"        # we recommend to use the latest framework version for new features and bug fixes
volo-grpc = "*"   # we recommend to use the latest framework version for new features and bug fixes

volo-gen = { path = "./volo-gen" }

[profile.release]
opt-level = 3
debug = true
debug-assertions = false
overflow-checks = false
lto = true
panic = 'unwind'
incremental = false
codegen-units = 1
rpath = false

[workspace]
members = ["volo-gen"]
resolver = "2"

2、新增 client.rs 

use lazy_static::lazy_static;
use std::net::SocketAddr;

lazy_static! {
    static ref CLIENT: volo_gen::volo::demo::ItemServiceClient = {
        let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
        volo_gen::volo::demo::ItemServiceClientBuilder::new("volo_demo")
            .address(addr)
            .build()
    };
}

#[volo::main]
async fn main() {
    let req = volo_gen::volo::demo::GetItemRequest { id: 1024 };
    let resp = CLIENT.clone().get_item(req).await;
    match resp {
        Ok(info) => tracing::info!("{:?}", info),
        Err(e) => tracing::error!("{:?}", e),
    }
}

 3、Client 请求 Server 测试

cargo run --bin server
cargo run --bin client

可以看到已经成功请求。 

五、实现一个日志中间件 

1、新增日志依赖

tracing = "0.1"
tracing-subscriber = "0.3"

2、在 lib.rs 中新增中间件服务

#![feature(type_alias_impl_trait)]

pub struct S;

#[volo::async_trait]
impl volo_gen::volo::demo::ItemService for S {
    // 这部分是我们需要增加的代码
    async fn get_item(
        &self,
        _req: volo_grpc::Request<volo_gen::volo::demo::GetItemRequest>,
    ) -> core::result::Result<volo_grpc::Response<volo_gen::volo::demo::GetItemResponse>, volo_grpc::Status>
    {
        Ok(volo_grpc::Response::new(Default::default()))
    }
}

// 中间件服务,打印出我们收到的请求、返回的响应以及消耗的时间
#[derive(Clone)]
pub struct LogService<S>(S);

#[volo::service]
impl<Cx, Req, S> volo::Service<Cx, Req> for LogService<S>
where
    Req: Send + 'static,
    S: Send + 'static + volo::Service<Cx, Req>,
    Cx: Send + 'static,
{
    async fn call(&mut self, cx: &mut Cx, req: Req) -> Result<S::Response, S::Error> {
        let now = std::time::Instant::now();
        let resp = self.0.call(cx, req).await;
        tracing::info!("Request took {}ms", now.elapsed().as_millis());
        resp
    }
}

// 我们给这个 Service 包装一层 Layer ,便于 server 、 client 调用
pub struct LogLayer;

impl<S> volo::Layer<S> for LogLayer {
    type Service = LogService<S>;

    fn layer(self, inner: S) -> Self::Service {
        LogService(inner)
    }
}

3、在 Server/Client 中加入日志中间件

use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt};

// 只有注册 subscriber 后, 才能在控制台上看到日志输出
tracing_subscriber::registry().with(fmt::layer()).init();


// client

use lazy_static::lazy_static;
use std::net::SocketAddr;
use volo_demo::LogLayer;
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt};

lazy_static! {
    static ref CLIENT: volo_gen::volo::demo::ItemServiceClient = {
        let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
        volo_gen::volo::demo::ItemServiceClientBuilder::new("volo_demo")
            .layer_inner(LogLayer)
            .address(addr)
            .build()
    };
}

#[volo::main]
async fn main() {

    // 只有注册 subscriber 后, 才能在控制台上看到日志输出
    tracing_subscriber::registry().with(fmt::layer()).init();
    let req = volo_gen::volo::demo::GetItemRequest { id: 1024 };
    let resp = CLIENT.clone().get_item(req).await;
    match resp {
        Ok(info) => tracing::info!("{:?}", info),
        Err(e) => tracing::error!("{:?}", e),
    }
}

 4、自定义函数返回


#[volo::async_trait]
impl volo_gen::volo::demo::ItemService for S {
    // 这部分是我们需要增加的代码
    async fn get_item(
        &self,
        _req: volo_grpc::Request<volo_gen::volo::demo::GetItemRequest>,
    ) -> core::result::Result<
        volo_grpc::Response<volo_gen::volo::demo::GetItemResponse>,
        volo_grpc::Status,
    > {
        // 默认返回空
        // Ok(volo_grpc::Response::new(Default::default()))

        // 返回自定义数据
        Ok(volo_grpc::Response::new(
            volo_gen::volo::demo::GetItemResponse {
                item: Some(volo_gen::volo::demo::Item {
                    id: 1024,
                    title: "hello".to_string(),
                    content: "just for test.".to_string(),
                    extra: Default::default(),
                }),
            },
        ))
    }
}

 5、测试

可以看到 gRPC 调用成功,并且日志输出了自定义的返回值! 

六、跨语言通讯

Client : Rust 语言

Server : C# 语言

1、公共 IDL

syntax = "proto3";

option csharp_namespace = "PrinterGrpcService";

package greet;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply);
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings.
message HelloReply {
  string message = 1;
}

2、Client 端

按照上述流程,编写 client.rs 

use lazy_static::lazy_static;
use std::net::SocketAddr;
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt};
use myapp::libs::my_tracing::LogLayer;

lazy_static! {
    static ref CLIENT: volo_gen::greet::GreeterClient = {
        let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
        volo_gen::greet::GreeterClientBuilder::new("greet-client")
            .layer_inner(LogLayer)
            .address(addr)
            .build()
    };
}

#[volo::main]
async fn main() {
    // 只有注册 subscriber 后, 才能在控制台上看到日志输出
    tracing_subscriber::registry().with(fmt::layer()).init();
    let req = volo_gen::greet::HelloRequest { name: "yushanma".to_string() };
    let resp = CLIENT.clone().say_hello(req).await;
    match resp {
        Ok(info) => tracing::info!("{:?}", info),
        Err(e) => tracing::error!("{:?}", e),
    }
}

2、Server 端

使用 .Net Core 框架创建模板

生成的代码文件跟 volo 如出一辙:定义 IDL,定义服务函数,启动 Server 监听请求。

启动服务端后再启动客户端测试:请求成功!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/97035.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

React学习26(react-redux优化 工作使用)

项目结构 优化说明 1&#xff09;容器组件和UI组件混合成一个文件 2&#xff09;无需自己给容器传递store&#xff0c;在index.js入口文件给包裹一个Provider <Provider store {store}><App/> </Provider> 3&#xff09;使用了react-redux后也不用自己在…

Python入门教程:基本运算符

1.运算符 计算机可以进行的运算有很多种&#xff0c;可不只加减乘除这么简单&#xff0c;运算按种类可分为算数运算、比较运算、逻辑运算、赋值运算、成员运算、身份运算、位运算&#xff0c;今天我们暂只学习算数运算、比较运算、逻辑运算、赋值运算、成员运算 2.算数运算 …

数据聚合、数据同步

文章目录数据聚合Bucket聚合语法聚合结果排序限定聚合范围Metric聚合语法RestAPI实现聚合数据同步发送MQ消息接收MQ消息数据聚合 Bucket聚合语法 GET /hotel/_search {"size": 0, // 设置size为0&#xff0c;结果中 不包含文档&#xff0c;只包含聚合结果~"…

Spark-内核(集群管理器、通讯架构、任务调度机制、Shuffle、内存管理)

文章目录Spark内核Spark部署模式的集群管理器YARN模式运行机制Standalone模式运行机制Spark通讯架构通信架构概述通讯架构解析Spark任务调度机制任务调度概述Stage级调度Spark Task级调度调度策略本地化调度失败重试与黑名单机制Spark Shuffle解析ShuffleMapStage与ResultStage…

SpringCloud微服务之Zuul网关

SpringCloud微服务之Zuul网关 家庭生活中经常有这样的感悟&#xff0c;家中的财政大权在老婆手里&#xff0c;想要花个小钱买个冰棍&#xff0c;得跟老婆请示&#xff0c;想要出个远门看看北京猿人&#xff0c;得跟老婆请示&#xff0c;想不要脸面去个夜店看看别的妞好在哪里&…

代码随想录第九天

专题&#xff1a;字符串 题目&#xff1a;字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。 比如&#xff0c;输入字符串"abcdefg"和数字2&#xff0c;该函数将返回左旋转两位得到的结果"cdefgab&…

ADI Blackfin DSP处理器-BF533的开发详解56:CVBS输入-DSP和ADV7180的MDMA用法(含源码)

硬件准备 ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 CVBS IN视频输出 代码实现功能 电视视频是奇场合偶场交替传输的&#xff0c;所以通过 CVBSIN 模块采集到的图像如上实验所看到的&#xff0c;是…

CentOS 7.6 安装与配置 MySql 5.7.40

1 通过wget下载MySql的rpm # wget https://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm2 检查是否存在MySql的repo # cd /etc/yum.repos.d/ # ls CentOS-Base.repo CentOS-Epel.repo可以看到没有MySql的repo 3 安装MySql的repo&#xff0c;并查看是否安…

React 生命周期

React 生命周期 这篇文章&#xff0c;我们来聊一聊在React中的生命周期。首先我们明确一点&#xff0c;在React中&#xff0c;函数式组件是没有生命周期的。谈到生命周期&#xff0c;都是关于类组件的&#xff01; 生命周期官方网址 React.Component – React (docschina.or…

macOS Monterey 12.6.2 (21G320) Boot ISO 原版可引导镜像

macOS Monterey 12.6&#xff0c;皆为安全更新&#xff0c;不再赘述。 macOS Monterey 12.6&#xff0c;发布于 2022 年 9 月 12 日&#xff08;北京时间今日凌晨&#xff09;&#xff0c;本次为安全更新。 今日&#xff08;2022-07-21&#xff09;凌晨&#xff0c;Apple 终于…

CentOS 8:SSH远程登录

SSH远程登录 SSH远程登录&#xff0c;也是 C / S 模式 服务端&#xff1a;sshd &#xff0c;默认是启动的 systemctl status sshd systemctl start sshd SecureCRT 是打开了一个远程终端 注意&#xff1a;在终端环境里&#xff0c;不可以启动GUI程序 例如&#xff0c;在 …

【单片机】DS1302时钟/蜂鸣器

目录 一、DS1302时钟 1、DS1302时钟的介绍 2、DS1302时钟寄存器 3、DS1302时序图 4、BCD码 5、写一个时钟 6、写一个可调时钟 二、蜂鸣器 1、蜂鸣器的介绍​编辑 2、三极管放大驱动蜂鸣器 2.1NPN三极管工作原理&#xff08;基极电流和发射电流均流向集电区&#xff…

Python图像识别实战(二):实现批量图像读取和像素转换(附源码和实现效果)

前面我介绍了可视化的一些方法以及机器学习在预测方面的应用&#xff0c;分为分类问题&#xff08;预测值是离散型&#xff09;和回归问题&#xff08;预测值是连续型&#xff09;&#xff08;具体见之前的文章&#xff09;。 从本期开始&#xff0c;我将做一个关于图像识别的…

Dubbo 3 Dubbo 快速入门 3.2 Dubbo 快速入门 3.2.3 服务消费者

Dubbo 【黑马程序员Dubbo快速入门&#xff0c;Java分布式框架dubbo教程】 3 Dubbo 快速入门 文章目录Dubbo3 Dubbo 快速入门3.2 Dubbo 快速入门3.2.3 服务消费者3.2 Dubbo 快速入门 3.2.3 服务消费者 OK&#xff0c;之前我们 已经完成了 服务提供 者的改造 现在来改造 服务…

智能电网中采用博弈论的方法(Python代码实现)

目录 1 概述 2 系统模型 2.1 单价模型 2.2 效用模型和 能源消费者&#xff08;EC&#xff09; 的目标 2.3 成本模型和 中央电站&#xff08;CPS&#xff09; 的目标 2.4 优化问题 3 优化的能源管理模型 4 模型的特性 4.1 均衡的存在 4.2 决策过程 5 算法 6 数值的…

vector详解(不定长数组)

目录 什么是vector vector的介绍 模板的声明方式 vector的基本操作 push_back insert erase 遍历vector 小明爱数列-练习题 思路 什么是vector vector的介绍 和我一样,我猜你们看到题目时心里都会产生一个疑问"什么是vector?(大佬除外)".vector其实是…

一起Talk Android吧(第四百四十四回:UI控件之DatePicker)

文章目录概念介绍使用方法内容总结各位看官们大家好&#xff0c;上一回中咱们说的例子是"UI控件之NumberPicker",这一回中说的例子是"UI控件之DatePicker"。闲话休提&#xff0c;言归正转&#xff0c;让我们一起Talk Android吧&#xff01; 概念介绍 看官…

CSS实现文字扫光特效

上大学的玩 ae 的时候&#xff0c;就曾遇到过这个特效。偶然在百度看到了类似特效&#xff0c;没想到竟然能用 css 实现&#xff0c;所以就研究了一下&#xff0c;文字扫光效果如下&#xff1a; 实现思路&#xff1a; 光效移动效果&#xff0c;可以通过 background-image 设置…

RCNN网络源码解读(Ⅲ) --- finetune训练过程

目录 0.回顾 1.finetune二分类代码解释&#xff08;finetune.py&#xff09; 1.1 load_data&#xff08;定义获取数据的方法&#xff09; 1.2 CustomFineTuneDataset类 1.3 custom_batch_sampler类&#xff08; custom_batch_sampler.py&#xff09; 1.4 训练train_mod…

JVM.......未完待续

一、了解 JVM JVM ( Java Virtual Machine )&#xff0c;又称之为 Java虚拟机。JVM 的运行与操作系统无关&#xff0c;能够实现跨平台&#xff0c;只要是安装了JVM的机器&#xff0c;都能运行Java程序&#xff0c;Java语言最重要的特点 "跨平台运行"&#xff0c;也…