本系列是關(guān)于用Rust構(gòu)建一個KV Server的系列文章,內(nèi)容包括用tokio做底層異步網(wǎng)絡(luò)通訊、使用toml文件做配置、protobuf做傳輸協(xié)議、內(nèi)存/RockDB做數(shù)據(jù)存儲、事件通知、優(yōu)雅關(guān)機、并發(fā)連接限制及測量監(jiān)控等。
在上一篇文章中,我們用tokio實現(xiàn)了客戶端和服務(wù)器的基本框架并設(shè)置了toml格式的配置文件。在這一篇文章中,我們參考Redis的命令:GET、SET、PUBLISH和SUBSCRIBE,使用Protobuf來實現(xiàn)客戶端與服務(wù)器之間的通信協(xié)議層。為了處理Protobuf,我們加入了post庫。同時加入了tracing庫用于日志處理。Cargo.toml如下:
在上一篇文章中,我們用tokio實現(xiàn)了客戶端和服務(wù)器的基本框架并設(shè)置了toml格式的配置文件。在這一篇文章中,我們參考Redis的命令:GET、SET、PUBLISH和SUBSCRIBE,使用Protobuf來實現(xiàn)客戶端與服務(wù)器之間的通信協(xié)議層。為了處理Protobuf,我們加入了post庫。同時加入了tracing庫用于日志處理。Cargo.toml如下:
Protobuf在項目根目錄下新建cmd.proto,加入如下代碼:[dependencies]
anyhow = "1"
tokio = { version = "1.19", features = ["full"] }
serde = { version = "1", features = ["derive"] }
toml = "0.5"
tracing = "0.1"
tracing-subscriber = "0.3"
bytes = "1"
prost = "0.11"
[build-dependencies]
prost-build = "0.11"
1syntax="proto3";
2
3packagecmd;
4
5//命令請求
6messageCmdRequest{
7oneofreq_data{
8Getget=1;
9Setset=2;
10Publishpublish=3;
11Subscribesubscribe=4;
12Unsubscribeunsubscribe=5;
13}
14}
15
16//服務(wù)器的響應(yīng)
17messageCmdResponse{
18uint32status=1;
19stringmessage=2;
20bytesvalue=3;
21}
22
23//請求值命令
24messageGet{
25stringkey=1;
26}
27
28//存儲值命令
29messageSet{
30stringkey=1;
31bytesvalue=2;
32uint32expire=3;
33}
34
35//向Topic發(fā)布值命令
36messagePublish{
37stringtopic=1;
38bytesvalue=2;
39}
40
41//訂閱Topic命令
42messageSubscribe{
43stringtopic=1;
44}
45
46//取消訂閱命令
47messageUnsubscribe{
48stringtopic=1;
49uint32id=2;
50}
在src目錄下創(chuàng)建pb目錄,在根目錄下創(chuàng)建build.rs文件,加入如下代碼:
1fnmain(){
2letmutconf=prost_build::new();
3conf.bytes(&["."]);
4conf.type_attribute(".","#[derive(PartialOrd)]");
5conf.out_dir("src/pb")
6.compile_protos(&["cmd.proto"],&["."])
7.unwrap();
8}
在src/pb目錄下已經(jīng)自動生成了cmd.rs文件。在src/pb目錄下創(chuàng)建mod.rs文件,加入如下代碼:
1usebytes::Bytes;
2
3usecrate::{cmd_request::ReqData,CmdRequest,Get,Publish,Set,Subscribe,Unsubscribe};
4
5pubmodcmd;
6
7implCmdRequest{
8//GET命令
9pubfnget(key:implInto) ->Self{
10Self{
11req_data:Some(ReqData::Get(Get{key:key.into()})),
12}
13}
14
15//SET命令
16pubfnset(key:implInto,value:Bytes,expire:u32) ->Self{
17Self{
18req_data:Some(ReqData::Set(Set{
19key:key.into(),
20value,
21expire,
22})),
23}
24}
25
26//PUBLISH命令
27pubfnpublish(topic:implInto,value:Bytes) ->Self{
28Self{
29req_data:Some(ReqData::Publish(Publish{
30topic:topic.into(),
31value,
32})),
33}
34}
35
36//訂閱命令
37pubfnsubscribe(topic:implInto) ->Self{
38Self{
39req_data:Some(ReqData::Subscribe(Subscribe{
40topic:topic.into(),
41})),
42}
43}
44
45//解除訂閱命令
46pubfnunsubscribe(topic:implInto,id:u32) ->Self{
47Self{
48req_data:Some(ReqData::Unsubscribe(Unsubscribe{
49topic:topic.into(),
50id,
51})),
52}
53}
54}
55
56implCmdResponse{
57pubfnnew(status:u32,message:String,value:Bytes)->Self{
58Self{
59status,
60message,
61value,
62}
63}
64}
在 src/lib.rs 中,引入pb模塊:
1modpb;
2pubusepb::*;
客戶端 & 服務(wù)器我們使用tokio-util庫的Frame里的LengthDelimitedCodec(根據(jù)長度進行編解碼)對protobuf協(xié)議進行封包解包。在Cargo.toml里加入tokio-util依賴:修改src/bin/kv_server.rs代碼:[dependencies]
......
futures = "0.3"
tokio-util = {version = "0.7", features = ["codec"]}
......
1#[tokio::main]
2asyncfnmain()->Result<(),Box> {
3tracing_subscriber::init();
4
5......
6
7loop{
8......
9
10tokio::spawn(asyncmove{
11//使用Frame的LengthDelimitedCodec進行編解碼操作
12letmutstream=Framed::new(stream,LengthDelimitedCodec::new());
13whileletSome(Ok(mutbuf))=stream.next().await{
14//對客戶端發(fā)來的protobuf請求命令進行拆包
15letcmd_req=CmdRequest::decode(&buf[..]).unwrap();
16info!("Receiveacommand:{:?}",cmd_req);
17
18buf.clear();
19
20//對protobuf的請求響應(yīng)進行封包,然后發(fā)送給客戶端。
21letcmd_res=CmdResponse::new(200,"success".to_string(),Bytes::default());
22cmd_res.encode(&mutbuf).unwrap();
23stream.send(buf.freeze()).await.unwrap();
24}
25info!("Client{:?}disconnected",addr);
26});
27}
28}
修改src/bin/kv_client.rs代碼:
1#[tokio::main]
2asyncfnmain()->Result<(),Box> {
3tracing_subscriber::init();
4
5......
6
7//使用Frame的LengthDelimitedCodec進行編解碼操作
8letmutstream=Framed::new(stream,LengthDelimitedCodec::new());
9letmutbuf=BytesMut::new();
10
11//創(chuàng)建GET命令
12letcmd_get=CmdRequest::get("mykey");
13cmd_get.encode(&mutbuf).unwrap();
14
15//發(fā)送GET命令
16stream.send(buf.freeze()).await.unwrap();
17info!("Send info successed!");
18
19//接收服務(wù)器返回的響應(yīng)
20whileletSome(Ok(buf))=stream.next().await{
21letcmd_res=CmdResponse::decode(&buf[..]).unwrap();
22info!("Receivearesponse:{:?}",cmd_res);
23}
24
25Ok(())
26}
我們打開二個終端,分別輸入以下命令:服務(wù)器執(zhí)行結(jié)果:RUST_LOG=info cargo run --bin kv_server
RUST_LOG=info cargo run --bin kv_client
客戶端執(zhí)行結(jié)果:INFO kv_server: Listening on 127.0.0.1:19999 ......
INFO kv_server: Client: 127.0.0.1:50655 connected
INFO kv_server: Receive a command: CmdRequest { req_data: Some(Get(Get { key: "mykey" })) }
INFO kv_client: Send info successed!
INFO kv_client: Receive a response: CmdResponse { status: 200, message: "success", value: b"" }
服務(wù)器和客戶端都正常處理了收到的請求和響應(yīng)。 下一篇文章我們將在服務(wù)器端使用內(nèi)存來存儲客戶端發(fā)送過來的數(shù)據(jù)。 完整代碼:https://github.com/Justin02180218/kv_server_rust
審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。
舉報投訴
-
通信協(xié)議
+關(guān)注
關(guān)注
28文章
861瀏覽量
40274 -
服務(wù)器
+關(guān)注
關(guān)注
12文章
9029瀏覽量
85207 -
客戶端
+關(guān)注
關(guān)注
1文章
290瀏覽量
16663
原文標題:用Rust實現(xiàn)KV Server-2 協(xié)議層
文章出處:【微信號:Rust語言中文社區(qū),微信公眾號:Rust語言中文社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
一個服務(wù)器,多個客戶端,怎么向指定的客戶端發(fā)數(shù)據(jù)
我用labview做服務(wù)器,單片機做客戶端,客戶端幾百個,怎么區(qū)分客戶端,給指定的客戶發(fā)發(fā)數(shù)據(jù)
發(fā)表于 06-01 09:26
如何實現(xiàn)服務(wù)器對指定客戶端的監(jiān)聽?
我現(xiàn)在能做出的是單服務(wù)器接收單客戶端的采集信息,想要實現(xiàn)RT所述,有沒有大佬指點一二!!!(最好是有源碼)
發(fā)表于 05-29 16:17
怎么使用Paho來實現(xiàn)和MQTT服務(wù)器的基本通信?
MQTT協(xié)議的全稱叫“消息隊列遙測傳輸”協(xié)議。它是一個輕量級的通信協(xié)議。旨在為低帶寬、高延時、不穩(wěn)定網(wǎng)絡(luò)中的物聯(lián)網(wǎng)設(shè)備提供消息傳輸服務(wù)。它運行在TCP/IP
發(fā)表于 09-11 11:52
BTC設(shè)備服務(wù)器的系統(tǒng)搭建
?:Node.js通信協(xié)議:HTTP、Socket3. Node.js 實現(xiàn) Socket 通信________________________________________Socket 主要作?是
發(fā)表于 09-24 09:05
4412開發(fā)板Qt網(wǎng)絡(luò)編程-TCP實現(xiàn)服務(wù)器和客戶端
Protocol)是一種面向連接的,可靠的,基于字節(jié)流的傳輸層通信協(xié)議,傳輸數(shù)據(jù)穩(wěn)定可靠。在 help 索引中搜索到如圖 兩個重要類:服務(wù)器編程中兩個類都會用到,客戶端編程中只會用到
發(fā)表于 04-28 15:33
當(dāng)WiFi信號變低時,服務(wù)器和客戶端之間的TCP通信丟失,如何使客戶端重新連接?
大家好,
當(dāng) WiFi 信號變低時,服務(wù)器和客戶端之間的 TCP 通信丟失,比如超過 -80dBm。一旦客戶端斷開連接,它就無法重新連接并正
發(fā)表于 05-15 07:31
服務(wù)器和客戶端之間的TCP通信丟失怎么處理?
嗨,
當(dāng) WiFi 信號變低時,比如超過 -80dBm,我面臨服務(wù)器和客戶端之間的 TCP 通信丟失。一旦客戶端斷開連接,它就無法重新連接并
發(fā)表于 05-16 08:19
瘦客戶端的服務(wù)器和網(wǎng)絡(luò)技術(shù)
基于服務(wù)器的計算代表著一種應(yīng)用程序部署方法。 對以下基于服務(wù)器的計算環(huán)境而言,瘦客戶端是理想的客戶端設(shè)備: 借助 Windows 2000 或 Windows Server 2003
發(fā)表于 07-02 16:46
?17次下載
TCP通信服務(wù)器端和客戶端同機互傳的簡單示例程序免費下載
本文檔的主要內(nèi)容詳細介紹的是TCP通信服務(wù)器端和客戶端同機互傳的簡單示例程序免費下載初學(xué)者學(xué)習(xí)。本例子是本計算機labview程序之間的通信,如果要想
發(fā)表于 10-25 08:00
?9次下載
Linux下TCP網(wǎng)絡(luò)編程-創(chuàng)建服務(wù)器與客戶端
這篇文章介紹在Linux下的socket編程,完成TCP服務(wù)器、客戶端的創(chuàng)建,實現(xiàn)數(shù)據(jù)通信。
MQTT中服務(wù)端和客戶端
MQTT 是一種基于客戶端-服務(wù)端架構(gòu)(C/S)的消息傳輸協(xié)議,所以在 MQTT 協(xié)議通信中,有兩個最為重要的角色,它們便是
服務(wù)端如何控制客戶端之間的信息通訊
服務(wù)端如何通過“主題”來控制客戶端之間的信息通訊,看下圖實例: 在以上圖示中一共有三個 MQTT 客戶端,它們分別是開發(fā)板、手機和電腦。MQTT 服
服務(wù)器Server和客戶端Client的區(qū)別
例如在使用TCP通訊建立連接時采用客戶端服務(wù)器模式,這種模式又常常被稱為主從式架構(gòu),簡稱為C/S結(jié)構(gòu),屬于一種網(wǎng)絡(luò)通訊架構(gòu),將通訊的雙方以客戶端(Client )與服務(wù)器 (Serve
評論