实现跨语言通信:Rust 和 Thrift 的最佳实践
前言
在分布式系统中,服务之间高效且安全的通信至关重要。Apache Thrift 是一个被广泛应用的跨语言 RPC(远程过程调用)框架,它支持多种编程语言,包括 Rust。Rust 以其卓越的性能和内存安全保障,成为越来越多开发者的首选语言。
本文将深入探讨如何在 Rust 项目中集成 Thrift,帮助开发者实现跨服务的高效通信,并且探讨异步编程和 TLS 安全通信的高级实现方式。
什么是 Thrift?
Thrift 是由 Facebook 开发并开源的一个高效的服务框架。它允许你定义数据类型和服务接口,然后生成跨多种编程语言的代码。简单来说,Thrift 提供了一个统一的接口来实现不同语言之间的通信。
集成步骤
你可以通过以下命令安装 Thrift 编译器:
# For macOS using Homebrew
brew install thrift# For Ubuntu
sudo apt-get install thrift-compiler
1. 定义 Thrift 文件
首先,你需要定义一个 .thrift 文件,描述你的数据结构和服务接口。创建一个文件 example.thrift:
namespace rs examplestruct User {1: i32 id,2: string name,3: i32 age
}service UserService {User getUser(1: i32 id),void saveUser(1: User user)
}
这个文件描述了一个 User 结构体和一个 UserService 服务。
2. 生成 Rust 代码
使用 Thrift 编译器生成 Rust 代码:
thrift --gen rs example.thrift
这将会在当前目录下生成一个 gen-rs 目录,里面包含了 Thrift 为 Rust 生成的代码。
3. 在 Rust 项目中使用 Thrift
创建一个新的 Rust 项目:
cargo new rust_thrift_example
cd rust_thrift_example
编辑 Cargo.toml 文件,添加 Thrift 依赖:
[dependencies]
thrift = "0.14.1"
将生成的 gen-rs 文件夹复制到 src 目录下,以便在项目中使用。
接下来,我们编写一个简单的客户端和服务器。
服务器端代码
在 src 目录下创建一个新文件 server.rs,编写服务器端代码:
use thrift::protocol::{TBinaryInputProtocol, TBinaryOutputProtocol};
use thrift::server::{TServer, TSimpleServer};
use thrift::transport::{TBufferedReadTransport, TBufferedWriteTransport, TIoChannel, TTcpChannel, TTcpListener, TTransport};mod gen_rs {pub mod example;
}use gen_rs::example::{User, UserServiceSyncProcessor};struct UserServiceHandler;impl UserServiceSyncProcessor for UserServiceHandler {fn getUser(&self, id: i32) -> thrift::Result<User> {Ok(User { id, name: format!("User{}", id), age: 30 })}fn saveUser(&self, user: User) -> thrift::Result<()> {println!("User saved: {:?}", user);Ok(())}
}fn main() -> thrift::Result<()> {let listener = TTcpListener::new("127.0.0.1:9090")?;let server = TSimpleServer::new(UserServiceHandler,TBinaryInputProtocol::new,TBinaryOutputProtocol::new,TBufferedReadTransport::new,TBufferedWriteTransport::new,listener,);println!("Starting the server...");server.serve()?;Ok(())
}
客户端代码
在 src 目录下创建一个新文件 client.rs,编写客户端代码:
use thrift::protocol::{TBinaryInputProtocol, TBinaryOutputProtocol};
use thrift::transport::{TBufferedReadTransport, TBufferedWriteTransport, TTcpChannel, TTransport};mod gen_rs {pub mod example;
}use gen_rs::example::{UserServiceSyncClient};fn main() -> thrift::Result<()> {let mut transport = TTcpChannel::new();transport.open("127.0.0.1:9090")?;let (i_prot, o_prot) = (TBinaryInputProtocol::new(TBufferedReadTransport::new(transport.try_clone()?)),TBinaryOutputProtocol::new(TBufferedWriteTransport::new(transport)),);let client = UserServiceSyncClient::new(i_prot, o_prot);let user = client.getUser(1)?;println!("Got user: {:?}", user);client.saveUser(user)?;Ok(())
}
4. 运行服务器和客户端
首先,编译并运行服务器:
cargo run --bin server
然后,在另一个终端窗口中运行客户端:
cargo run --bin client
你应该会看到客户端从服务器获取到用户信息并将其保存。
实际应用
在上面的教程中,我们已经成功地实现了一个基础的 Thrift 服务和客户端。但是,在实际应用中,我们可能会遇到更多的需求和挑战。接下来,我们将深入探讨一些常见的需求和解决方案。
异步编程
随着现代应用对性能和并发的要求越来越高,异步编程变得越来越重要。Rust 提供了强大的异步编程支持,我们可以利用这些特性来提升 Thrift 服务的性能。
使用 tokio 和 async 实现异步 Thrift 服务
tokio 是一个用于异步编程的强大框架。我们可以结合 tokio 和 Rust 的 async 特性来实现异步 Thrift 服务。
首先,确保在 Cargo.toml 文件中添加 tokio 依赖:
[dependencies]
thrift = "0.14.1"
tokio = { version = "1", features = ["full"] }然后,修改服务器端代码以支持异步:
use thrift::protocol::{TBinaryInputProtocol, TBinaryOutputProtocol};
use thrift::server::TServer;
use thrift::transport::{TBufferedReadTransport, TBufferedWriteTransport, TIoChannel, TTcpChannel, TTcpListener};
use tokio::net::TcpListener;
use tokio::sync::Mutex;
use std::sync::Arc;mod gen_rs {pub mod example;
}use gen_rs::example::{User, UserServiceAsyncProcessor};struct UserServiceHandler;#[async_trait::async_trait]
impl UserServiceAsyncProcessor for UserServiceHandler {async fn getUser(&self, id: i32) -> thrift::Result<User> {Ok(User { id, name: format!("User{}", id), age: 30 })}async fn saveUser(&self, user: User) -> thrift::Result<()> {println!("User saved: {:?}", user);Ok(())}
}#[tokio::main]
async fn main() -> thrift::Result<()> {let listener = TcpListener::bind("127.0.0.1:9090").await?;let handler = Arc::new(Mutex::new(UserServiceHandler));loop {let (socket, _) = listener.accept().await?;let handler = handler.clone();tokio::spawn(async move {let (i_prot, o_prot) = (TBinaryInputProtocol::new(TBufferedReadTransport::new(TTcpChannel::new(socket))),TBinaryOutputProtocol::new(TBufferedWriteTransport::new(TTcpChannel::new(socket))),);let processor = UserServiceAsyncProcessor::new(handler);let mut server = TServer::new(i_prot, o_prot, processor);server.serve().await.unwrap();});}
}
在这个示例中,我们使用 tokio::net::TcpListener 来异步监听连接,并使用 tokio::spawn 来处理每个连接,从而实现并发处理。
使用 TLS 加密通信
在生产环境中,安全性是极为重要的考量。我们可以使用 TLS (传输层安全) 来加密 Thrift 服务的通信。
配置 TLS
首先,确保在 Cargo.toml 文件中添加 tokio-rustls 依赖:
[dependencies]
thrift = "0.14.1"
tokio = { version = "1", features = ["full"] }
tokio-rustls = "0.22"
rustls = "0.20"
然后,生成自签名证书或使用受信任的证书。为了简化演示,我们使用自签名证书:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
接着,修改服务器端代码以支持 TLS:
use thrift::protocol::{TBinaryInputProtocol, TBinaryOutputProtocol};
use thrift::server::TServer;
use thrift::transport::{TBufferedReadTransport, TBufferedWriteTransport, TIoChannel, TTcpChannel};
use tokio::net::TcpListener;
use tokio::sync::Mutex;
use tokio_rustls::rustls::{ServerConfig, NoClientAuth, Certificate, PrivateKey};
use tokio_rustls::TlsAcceptor;
use tokio_rustls::rustls::internal::pemfile::{certs, rsa_private_keys};
use std::sync::Arc;
use std::fs::File;
use std::io::{BufReader, self};mod gen_rs {pub mod example;
}use gen_rs::example::{User, UserServiceAsyncProcessor};struct UserServiceHandler;#[async_trait::async_trait]
impl UserServiceAsyncProcessor for UserServiceHandler {async fn getUser(&self, id: i32) -> thrift::Result<User> {Ok(User { id, name: format!("User{}", id), age: 30 })}async fn saveUser(&self, user: User) -> thrift::Result<()> {println!("User saved: {:?}", user);Ok(())}
}#[tokio::main]
async fn main() -> thrift::Result<()> {let mut config = ServerConfig::new(NoClientAuth::new());let cert_file = &mut BufReader::new(File::("cert.pem")?);let key_file = &mut BufReader::new(File::open("key.pem")?);let cert_chain = certs(cert_file).map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))?;let mut keys = rsa_private_keys(key_file).map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid key"))?;config.set_single_cert(cert_chain, keys.remove(0)).map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid key"))?;let acceptor = TlsAcceptor::from(Arc::new(config));let listener = TcpListener::bind("127.0.0.1:9090").await?;let handler = Arc::new(Mutex::new(UserServiceHandler));loop {let (socket, _) = listener.accept().await?;let handler = handler.clone();let acceptor = acceptor.clone();tokio::spawn(async move {let tls_socket = acceptor.accept(socket).await.unwrap();let (i_prot, o_prot) = (TBinaryInputProtocol::new(TBufferedReadTransport::new(TTcpChannel::new(tls_socket))),TBinaryOutputProtocol::new(TBufferedWriteTransport::new(TTcpChannel::new(tls_socket))),);let processor = UserServiceAsyncProcessor::new(handler);let mut server = TServer::new(i_prot, o_prot, processor);server.serve().await.unwrap();});}
}
使用 TLS 的客户端代码类似,只需在创建连接时使用 tokio-rustls 的 TlsConnector 来进行加密连接。
总结
本文详细介绍了如何在 Rust 项目中集成 Apache Thrift,以及如何通过异步编程和 TLS 实现更高效和安全的服务通信。Rust 和 Thrift 的结合为开发者提供了一种可靠的跨语言通信解决方案,能够满足现代分布式系统的高性能和高安全性需求。希望通过本文的教程,开发者能够更好地理解和应用 Rust 和 Thrift,实现高效的分布式系统开发。
相关文章:
实现跨语言通信:Rust 和 Thrift 的最佳实践
前言 在分布式系统中,服务之间高效且安全的通信至关重要。Apache Thrift 是一个被广泛应用的跨语言 RPC(远程过程调用)框架,它支持多种编程语言,包括 Rust。Rust 以其卓越的性能和内存安全保障,成为越来越…...
js判断空对象
1. 使用 Object.keys() 方法 Object.keys(obj) 方法返回一个包含对象可枚举属性名称的数组。如果返回的数组长度为 0,表示对象为空。 const isEmpty (obj) > Object.keys(obj).length 0;// 示例 const emptyObject {}; const nonEmptyObject { key: value …...

visionpro官方示例分析(一) 模板匹配工具 缺陷检测工具
1.需求:找出图像中的这个图形。 2.步骤 使用CogPMAlignTool工具,该工具是模板匹配工具,见名知意,所谓模板匹配工具就是说先使用该工具对一张图像建立模板,然后用这个模板在其他图像上进行匹配,匹配上了就说…...

PyCharm中Python项目打包并运行到服务器的简明指南
目录 一、准备工作 二、创建并设置Python项目 创建新项目 配置项目依赖 安装PyInstaller 三、打包项目 打包为可执行文件 另一种打包方式(使用setup.py) 四、配置服务器环境 五、上传可执行文件到服务器 六、在服务器上运行项目 配置SSH解释…...

cocos creator 3.8 合成大西瓜Demo 11
界面上的Node节点: 背景 警戒线 三面墙 初始位置节点 水果容器 先分组吧,墙 地板 水果 创建预制体 先挂一个脚本 刚体碰撞器先弄上再说 import { _decorator, Component, Node } from cc; const { ccclass, property } _decorator;ccclass(FruitData) e…...

Vue前端开发-动态插槽
不仅父组件可以通过插槽方式访问并控制子组件传入的数据,而且可以控制传入父组件时插槽的名称,从而使不同的插槽根据名称的不同,使用场景也不同,例如在一个小区详细页中,可以根据小区类型,调用不同名称的详…...

使用easyexcel导出复杂模板,同时使用bean,map,list填充
背景 在使用easyexcel导出时,如果遇到一个模板中同时存在 一部分是实体类中的字段,另外部分是列表的字段,需要特殊处理一下,比如下面的模板: 这里面 user, addr 是实体类(或者map)…...
最大值(Java Python JS C++ C )
题目描述 给定一组整数(非负),重排顺序后输出一个最大的整数。 示例1 输入:[10,9] 输出:910 说明:输出结果可能非常大,所以你需要返回一个字符串而不是整数。 输入描述 数字组合 输出描述 最大的整数 示例1 输入 10 9输出 910解题思路 题目要求 是:给定一…...

17.5k Star,ThingsBoard 一款开源、免费、功能全面的物联网 IoT 平台 -慧知开源充电桩平台
项目介绍 ThingsBoard是一个开源、免费、功能全面、灵活易用的物联网(IoT)平台,专注于数据收集、处理、可视化以及设备管理。它提供了一个全面的解决方案,用于构建和管理物联网应用。支持从各种设备收集数据,通过内置…...
《C++ 与神经网络:自动微分在反向传播中的高效实现之道》
在深度学习蓬勃发展的今天,神经网络成为了众多领域的核心技术驱动力。而反向传播算法作为训练神经网络的关键手段,其背后的自动微分技术的高效实现尤为重要,特别是在 C 这样追求性能与内存控制极致的编程语言环境下。 神经网络通过大量的参数…...
【CSS】设置文本超出N行省略
文章目录 基本使用 这种方法主要是针对Webkit浏览器,因此可能在一些非Chrome浏览器中不适用。 基本使用 例如:设置文本超出两行显示省略号。 核心代码: .ellipsis-multiline {display: -webkit-box; -webkit-box-orient: vertical; /* 设置…...

open-instruct - 训练开放式指令跟随语言模型
文章目录 关于 open-instruct设置训练微调偏好调整RLVR 污染检查开发中仓库结构 致谢 关于 open-instruct github : https://github.com/allenai/open-instruct 这个仓库是我们对在公共数据集上对流行的预训练语言模型进行指令微调的开放努力。我们发布这个仓库,并…...

DI依赖注入详解
DI依赖注入 声明了一个成员变量(对象)之后,在该对象上面加上注解AutoWired注解,那么在程序运行时,该对象自动在IOC容器中寻找对应的bean对象,并且将其赋值给成员变量,完成依赖注入。 AutoWire…...

TDengine在debian安装
参考官网文档: 官网安装文档链接 从列表中下载获得 Deb 安装包; TDengine-server-3.3.4.3-Linux-x64.deb (61 M) 进入到安装包所在目录,执行如下的安装命令: sudo dpkg -i TDengine-server-<version>-Linux-x64.debNOTE 当…...

【C#设计模式(15)——命令模式(Command Pattern)】
前言 命令模式的关键通过将请求封装成一个对象,使命令的发送者和接收者解耦。这种方式能更方便地添加新的命令,如执行命令的排队、延迟、撤销和重做等操作。 代码 #region 基础的命令模式 //命令(抽象类) public abstract class …...
XGBoost库介绍:提升机器学习模型的性能
XGBoost库介绍:提升机器学习模型的性能 在机器学习领域,模型的准确性和训练效率是最为关注的两大因素。特别是在处理大量数据和复杂任务时,传统的机器学习算法可能无法满足高效和准确性的需求。XGBoost(eXtreme Gradient Boostin…...
网络安全构成要素
一、防火墙 组织机构内部的网络与互联网相连时,为了避免域内受到非法访问的威胁,往往会设置防火墙。 使用NAT(NAPT)的情况下,由于限定了可以从外部访问的地址,因此也能起到防火墙的作用。 二、IDS入侵检…...

SpringMVC——SSM整合
SSM整合 创建工程 在pom.xml中导入坐标 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_…...

Windows系统电脑安装TightVNC服务端结合内网穿透实现异地远程桌面
文章目录 前言1. 安装TightVNC服务端2. 局域网VNC远程测试3. Win安装Cpolar工具4. 配置VNC远程地址5. VNC远程桌面连接6. 固定VNC远程地址7. 固定VNC地址测试 前言 在追求高效、便捷的数字化办公与生活的今天,远程桌面服务成为了连接不同地点、不同设备之间的重要桥…...

【ubuntu24.04】GTX4700 配置安装cuda
筛选显卡驱动显卡驱动 NVIDIA-Linux-x86_64-550.135.run 而后重启:最新的是12.6 用于ubuntu24.04 ,但是我的4700的显卡驱动要求12.4 cuda...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...

uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...