krpc-rust 一個(gè)最像RPC框架的Rust-RPC框架
https://github.com/kwsc98/krpc-rust
剛剛學(xué)習(xí)Rust語(yǔ)言或者沒(méi)怎么了解Rust-RPC框架的同學(xué),可能以為又是一個(gè)標(biāo)題黨了,但實(shí)際上了解過(guò)這部分的同學(xué)都知道,目前來(lái)說(shuō)主流的Rust-RPC框架和實(shí)際定義的RPC框架還是有著很大的差別。我們先看一下隔壁Java是如何實(shí)現(xiàn)的,就拿本項(xiàng)目Java版本 krpc-java 舉例,有興趣學(xué)習(xí)Java-RPC框架的同學(xué)別忘了點(diǎn)個(gè)Star~
krpc
實(shí)現(xiàn)一個(gè)基于netty單路復(fù)用網(wǎng)絡(luò)模型的rpc框架,支持spring-boot啟動(dòng),支持zookeeper,nacos注冊(cè)中心。
以SpringBoot的方式啟動(dòng)
接口示例
public interface ExampeService {
ResponseDTO doRun(RequestDTO requestDTO);
}
Customer
配置application.properties
krpc.registeredPath = nacos://114.116.3.221:8848
@KrpcResource(version = "1.0.1",timeout = 1000)
private ExampeService exampeService;
Provider
配置application.properties
krpc.registeredPath = nacos://114.116.3.221:8848
krpc.port = 8082
@KrpcService(version = "1.0.1")
public class ExampeServiceImpl implements ExampeService {
@Override
public ResponseDTO doRun(RequestDTO requestDTO) {
ResponseDTO responseDTO = new ResponseDTO();
responseDTO.setDate(new Date(requestDTO.getDate().getTime() + (long) requestDTO.getNum() * 60 * 60 * 1000));
return responseDTO;
}
}
我們看到只需要定義一個(gè)接口,然后Server端來(lái)實(shí)現(xiàn)這個(gè)接口,Client端給接口加一個(gè)注解,就可以進(jìn)行RPC的調(diào)用了,這是因?yàn)镴ava擁有一個(gè)大殺器就是運(yùn)行時(shí)反射,可以很輕松的在運(yùn)行時(shí)對(duì)類(lèi)進(jìn)行增強(qiáng),但是同樣這也是Java的一大缺點(diǎn)就是因?yàn)檫\(yùn)行時(shí)存在導(dǎo)致程序執(zhí)行降低,那么以高性能著稱(chēng)Rust當(dāng)然不存在運(yùn)行時(shí),但因此也缺少了運(yùn)行時(shí)反射這一功能,那么目前主流的Rust-RPC框架是怎么解決這個(gè)問(wèn)題的?目前市面上有兩大產(chǎn)品分別是阿里的Dubbo和字節(jié)的Volo,首先我們看 Dubbo 怎么做的吧。
Dubbo 快速入門(mén)章節(jié)中介紹了Dubbo Rust的使用方法,其實(shí)主要是分為三個(gè)部分
第一部分定義接口,dubbo目前支持很多協(xié)議,其中還支持gRPC協(xié)議,其中Rust版本就是通過(guò)ProtoBuf協(xié)議來(lái)實(shí)現(xiàn)接口
第二部則是通過(guò)定義文件實(shí)現(xiàn)相關(guān)的Rust代碼,因?yàn)镽ust沒(méi)有運(yùn)行時(shí),所以Client調(diào)用時(shí)沒(méi)有辦法通過(guò)動(dòng)態(tài)代理的方式生成client類(lèi),而dubbo的解決方法就是通過(guò)定義接口內(nèi)容生成相關(guān)的Client調(diào)用代碼,來(lái)"降低"使用者的使用成本。
第三部分則是編寫(xiě)相關(guān)的Server端代碼邏輯,然后通過(guò)生成的Client代碼進(jìn)行RPC調(diào)用
字節(jié)的Volo其實(shí)大體也是這個(gè)思路,通過(guò)IDL定義接口,然后通過(guò)腳手架腳本生成調(diào)用相關(guān)代碼。有興趣的同學(xué)可以看一下 Volo-grpc 的快速開(kāi)始
總結(jié)下來(lái)就是通過(guò)接口定義使用腳本生成調(diào)用代碼和服務(wù)接口,然后進(jìn)行Server端業(yè)務(wù)實(shí)現(xiàn)和Client調(diào)用。
這樣看下來(lái)Dubbo和Volo的實(shí)現(xiàn),尤其對(duì)比Java版本的實(shí)現(xiàn)來(lái)說(shuō),是不是離真正的RPC框架還有很大的差距,包括還存在很多的問(wèn)題,比如
- 必須使用RPC接口的一些規(guī)范,比如響應(yīng)什么錯(cuò)誤碼。
// #[async_trait]
#[async_trait]
impl Greeter for GreeterServerImpl {
async fn greet(
&self,
request: Request<GreeterRequest>,
) -> Result<Response<GreeterReply>, dubbo::status::Status> {
println!("GreeterServer::greet {:?}", request.metadata);
Ok(Response::new(GreeterReply {
message: "hello, dubbo-rust".to_string(),
}))
}
}
- Client的調(diào)用時(shí)請(qǐng)求體必須包裝成代碼生成的樣式。
let req = volo_gen::proto_gen::hello::HelloRequest {
name: FastStr::from_static_str("Volo"),
};
- 關(guān)鍵是如果想修改請(qǐng)求響應(yīng)字段或者新增接口時(shí),那我們必現(xiàn)通過(guò)腳本來(lái)重新生成所有代碼,包括Client端和Server端,我們都知道對(duì)于軟件質(zhì)量有要求的公司,在改動(dòng)代碼時(shí)都必需評(píng)估影響范圍然后交由測(cè)試,那么是不是意味則我們有一些小的調(diào)整的話就得讓測(cè)試進(jìn)行全部的測(cè)試?
那么Rust沒(méi)有運(yùn)行時(shí)反射是不是也是個(gè)"缺點(diǎn)"?目前來(lái)看確實(shí)是這樣的,兩大廠都只能交出這么一個(gè)不令我們滿意的答案,Java有反射這個(gè)大殺器才在微服務(wù)領(lǐng)域獨(dú)領(lǐng)風(fēng)騷,那Rust有什么辦法可以在微服務(wù)領(lǐng)域也挑戰(zhàn)Java呢?那就不得不提Rust宏這個(gè)核彈級(jí)武器了。
Rust 宏
Rust宏大家都戲稱(chēng)可以通過(guò)宏來(lái)實(shí)現(xiàn)另一種編程語(yǔ)言,可見(jiàn)宏的強(qiáng)大之處,我們都知道宏是作用于編譯期,那么我們就拿宏來(lái)實(shí)現(xiàn)一個(gè)編譯期的反射不就行了嗎?事實(shí)也的確可以,說(shuō)了上面那么大一段廢話,下面接入正題,看一看 krpc-rust
是如果進(jìn)行RPC調(diào)用的
Server
use krpc_core::server::KrpcServer;
use krpc_macro::krpc_server;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Default, Debug)]
struct ReqDto {
str: String,
}
#[derive(Serialize, Deserialize, Default)]
struct ResDto {
str: String,
}
#[derive(Clone)]
struct TestServer {
_db: String,
}
krpc_server! {
TestServer,
"1.0.0",
async fn do_run1(&self,res : ReqDto) -> ResDto {
println!("{:?}" ,res);
return ResDto { str : "TestServer say hello 1".to_string()};
}
async fn do_run2(&self,res : ReqDto) -> ResDto {
println!("{:?}" ,res);
return ResDto { str : "TestServer say hello 2".to_string()};
}
}
#[tokio::main(worker_threads = 512)]
async fn main() {
let server: TestServer = TestServer {
_db: "我是一個(gè)DB數(shù)據(jù)庫(kù)".to_string(),
};
KrpcServer::build()
.set_port("8081")
.add_rpc_server(Box::new(server))
.run()
.await;
}
Client
use krpc_core::client::KrpcClient;
use krpc_macro::krpc_client;
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
lazy_static! {
static ref CLI: KrpcClient = KrpcClient::build("http://127.0.0.1:8081".to_string());
}
#[derive(Serialize, Deserialize, Default, Debug)]
struct ReqDto {
str: String,
}
#[derive(Serialize, Deserialize, Default,Debug)]
struct ResDto {
str: String,
}
struct TestServer;
krpc_client! {
CLI,
TestServer,
"1.0.0",
async fn do_run1(&self,res : ReqDto) -> ResDto
async fn do_run2(&self,res : ReqDto) -> ResDto
}
#[tokio::main(worker_threads = 512)]
async fn main() {
let client = TestServer;
let res = client.do_run1(ReqDto{str : "client say hello 1".to_string()}).await;
println!("{:?}",res);
let res = client.do_run2(ReqDto{str : "client say hello 2".to_string()}).await;
println!("{:?}",res);
}
我們直接運(yùn)行一下來(lái)看
這是不是才是RPC框架因有的樣子?看到這里的同學(xué)是不是得本項(xiàng)目點(diǎn)個(gè)Star感謝支持,這個(gè)項(xiàng)目是一個(gè)很好的學(xué)習(xí)項(xiàng)目,同時(shí)也希望通過(guò)這個(gè)項(xiàng)目能讓Rust在微服務(wù)領(lǐng)域同樣有所發(fā)展。得益于Rust零抽象成本的概念,本項(xiàng)目當(dāng)然也以高性能為目標(biāo),那我們就簡(jiǎn)單做個(gè)壓力測(cè)試唄,因?yàn)镈ubbo目前開(kāi)源的版本示例我弄了一會(huì)兒沒(méi)跑起來(lái)…那么我們就和Volo比一下。
https://github.com/kwsc98/krpc-rust
本次壓測(cè)機(jī)器是MacBook Pro M1 16 + 512
壓測(cè)內(nèi)容是四百萬(wàn)請(qǐng)求,異步線程數(shù)client端和server端各512,因?yàn)镽PC調(diào)用時(shí)IO密集型所有多開(kāi)一些線程。下面是測(cè)試腳本
krpc-rust
測(cè)試結(jié)果 四百萬(wàn)請(qǐng)求,平均47秒跑完,每秒8.5w+QTS?。?!而且內(nèi)存占用也比較穩(wěn)定
只能說(shuō)不愧是Rust,Java表示實(shí)名制羨慕...
接著我們看Volo的表現(xiàn)。
額。。。出現(xiàn)點(diǎn)狀況,測(cè)試100并發(fā)的時(shí)候還挺好好使,但是壓測(cè)時(shí)內(nèi)存和耗時(shí)異常高,因?yàn)闉榱藟簻y(cè)關(guān)掉了日志打印,那么打開(kāi)日志再看一下,結(jié)果
設(shè)置到500并發(fā)時(shí),socket連接就出現(xiàn)了錯(cuò)誤,500個(gè)請(qǐng)求只成功了139個(gè),可能目前Volo還存在一些問(wèn)題,不過(guò)影響不大,我們已經(jīng)證明了Rust在微服務(wù)領(lǐng)域其實(shí)是有機(jī)會(huì)干掉Java的。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-817408.html
如果感興趣的同學(xué)可以一起討論學(xué)習(xí),目前本項(xiàng)目還有需要工作需要做,比如說(shuō)異常的處理,多組件的支持,服務(wù)的注冊(cè)和發(fā)現(xiàn),但是框架骨架已搭建并驗(yàn)證完畢,未來(lái)可期~文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-817408.html
到了這里,關(guān)于Rust微服務(wù)殺手級(jí)應(yīng)用終于來(lái)了!一個(gè)最像RPC框架的Rust-RPC框架的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!