使用Nodejs?實(shí)現(xiàn)一個(gè)簡(jiǎn)單的?Redis客戶端(推薦)
0. 寫在前面
大家如果有去看過(guò)nodejs所支持的官方庫(kù)的話,應(yīng)該會(huì)驚訝于它所提供了非常完善的網(wǎng)絡(luò)庫(kù),不僅是應(yīng)用層,傳輸層,等等基礎(chǔ)的協(xié)議,我們可以按照事件驅(qū)動(dòng)的邏輯編寫清晰易懂的網(wǎng)絡(luò)應(yīng)用,網(wǎng)絡(luò)服務(wù)。這也是本文為什么選擇Nodejs編寫的原因。
1. 背景映入
大家在使用一些數(shù)據(jù)庫(kù)軟件的時(shí)候常常會(huì)使用遠(yuǎn)程連接
mysql -h xxx.xxx.xxx.xx -u xzzz -p
這里也指明了ip地址,但是很明顯這里可不是http協(xié)議在服務(wù),而是更加底層的協(xié)議 - 傳輸層協(xié)議,具體來(lái)說(shuō)是TCP協(xié)議(Transmission Control Protocol)。通信的示意圖如下:
所以很自然的想到,數(shù)據(jù)庫(kù)的客戶端一定經(jīng)過(guò)如下流程,從而與遠(yuǎn)程相連接:
graph TB身份驗(yàn)證 --> 運(yùn)輸層連接建立運(yùn)輸層連接建立 --> 客戶端服務(wù)端輸入輸出綁定_通道客戶端服務(wù)端輸入輸出綁定_通道 --> 連接中斷連接中斷 --> 雙方退出釋放資源
所以我們可以嘗試向服務(wù)端發(fā)送這樣的請(qǐng)求消息,建立與服務(wù)端的連接,發(fā)送一些數(shù)據(jù),接受一些數(shù)據(jù),最后斷開連接。
2. 數(shù)據(jù)庫(kù)選擇
這里為了簡(jiǎn)單起見,我們考慮不需要身份驗(yàn)證的redis數(shù)據(jù)庫(kù)來(lái)作為此次實(shí)驗(yàn)的服務(wù)端。
如果大家是mac,或者linux倒是可以直接安裝,如果是windows的話,推薦使用docker進(jìn)行安裝,這里給出一行docker命令。
docker run --name redis-server -p 6379:6379 -d redis:latest
3. Nodejs TCP連接
在nodejs中支持TCP連接的是net模塊, 其中使用createConnection(config)
或者直接new Socket(config)
來(lái)初始化一個(gè)TCP連接。
上面兩個(gè)函數(shù)不論哪一個(gè)都會(huì)返回socket實(shí)例,如果連接正常的話,就可以通過(guò)這個(gè)socket發(fā)送消息了。
當(dāng)服務(wù)端redis接收到消息之后也會(huì)返回相應(yīng)的消息,在本機(jī)客戶端通過(guò)對(duì)數(shù)據(jù)的校驗(yàn),檢查后,觸發(fā)相應(yīng)的操作(是拒絕還是接受服務(wù)端的響應(yīng))。
4. 代碼編寫
知道了原理之后,我這里直接把代碼貼出來(lái)
- RedisSocket: 繼承自Socket
class RedisSocket extends Socket { constructor(config: RedisClientConfig) { super(); this.connect(config.port, config.host); } // Set public set(key: string, value: string | number): Promise<Buffer> { return new Promise((resolve, reject) => { this.write(`SET ${key} ${value}\n`); const fetchAns = (chunk: Buffer) => { if (chunk.toString().includes("OK")) { resolve(chunk); this.off("data", fetchAns); // 在交付完成之后使用off 把函數(shù)取消綁定 } else { reject("error! can't set data"); } } this.on("data", fetchAns); }) } // Get public get(key: string): Promise<Buffer> { return new Promise((resolve, reject) => { try { this.write(`GET ${key}\n`); const fetchAns = (chunk: Buffer) => { resolve(chunk); this.off("data", fetchAns); // 在交付完成之后使用off 把函數(shù)取消綁定 } this.on("data", fetchAns); } catch(err) { reject(err); } }) } // 斷開TCP public close() { this.end(); } }
這個(gè)類將用來(lái)處理建立好后的連接的
- RedisClient
class RedisClient { private config: RedisClientConfig; constructor(config: RedisClientConfig) { this.config = config; // 配置項(xiàng) } // 獲取redis實(shí)例 getConnection(): Promise<RedisSocket> { return new Promise((resolve, reject) => { const socket = new RedisSocket(this.config); socket.on("connect", () => { resolve(socket); }); socket.on("error", (err) => { reject(err); }); }); } }
這個(gè)類用來(lái)建立與服務(wù)端的連接,使用getConnection()
方法,將會(huì)交付一個(gè)redisSocket,使用這個(gè)Socket可以直接向server發(fā)送和接受數(shù)據(jù)。
5. 實(shí)驗(yàn)
import { RedisClient, RedisSocket } from "./src/Client"; const Redis = new RedisClient({ host: "localhost", port: 6379 }); Redis.getConnection().then((socket: RedisSocket) => { socket.set("Mushroom", "Cookie"); socket.set("Mici", "Icmi").then( () => { socket.get("Mushroom").then((data: Buffer) => { console.log(data.toString()); socket.close(); }) }); })
這里使用RedisClient建立與本地redis的連接,隨后通過(guò)getConnection()獲取到連接實(shí)例,并通過(guò)這個(gè)連接實(shí)例設(shè)置了兩個(gè)數(shù)據(jù),以及獲取了一數(shù)據(jù)并打印了出來(lái)。
> pnpm dev > $6 // 這里的$6你也許會(huì)感到奇怪,不過(guò)我們很快就會(huì)知道這是什么 > Cookie
6. wireshark 抓包分析
這一次請(qǐng)求就是一整個(gè)完整的TCP流程,
在這其中TCP保證數(shù)據(jù)的可靠傳輸,而RESP(REdis Serialization Protocol)把數(shù)據(jù)封裝成一個(gè)fragment段,發(fā)送到下面的TCP
服務(wù)端相應(yīng)的時(shí)候也是如此,會(huì)把數(shù)據(jù)封裝起來(lái)發(fā)送到TCP中轉(zhuǎn)發(fā)出去。
看看發(fā)送方的RESP
看看響應(yīng)的RESP
所以知道了嗎?沒錯(cuò),6其實(shí)就是長(zhǎng)度那一部分強(qiáng)行轉(zhuǎn)化為字符串的結(jié)果,所以在現(xiàn)在很多流行的redis客戶端中如ioredis都對(duì)RESP報(bào)文做了非常完備的解析,這使得開發(fā)者能夠非常絲滑的與redis服務(wù)端交互。(感謝這些開發(fā)者做的一切?。?/p>
7. 雜與代碼
到此這篇關(guān)于用Nodejs 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 Redis客戶端的文章就介紹到這了,更多相關(guān)Nodejs實(shí)現(xiàn)Redis客戶端內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Node.js利用node-git-server快速搭建git服務(wù)器
本篇文章主要介紹了詳解Node.js利用node-git-server快速搭建git服務(wù)器,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-09-09Node.js+Express+Vue+MySQL+axios的項(xiàng)目搭建全過(guò)程
這篇文章主要介紹了Node.js+Express+Vue+MySQL+axios的項(xiàng)目搭建全過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12Node.js+Express.js+TS實(shí)現(xiàn)簡(jiǎn)單圖床腳本
在這篇博客文章中,我將介紹如何使用 TypeScript 和 Express 框架來(lái)編寫一個(gè)簡(jiǎn)單的圖床腳本,可以將本地圖片上傳到服務(wù)器,并返回圖片的 URL,這樣,你就可以在 Markdown 文檔中方便地引用圖片,而不用擔(dān)心圖片的存儲(chǔ)和管理問(wèn)題2023-10-10NodeJS 實(shí)現(xiàn)多語(yǔ)言的示例代碼
這篇文章主要介紹了NodeJS 實(shí)現(xiàn)多語(yǔ)言的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-09-09nodejs使用socket5進(jìn)行代理請(qǐng)求的實(shí)現(xiàn)
這篇文章主要介紹了nodejs使用socket5進(jìn)行代理請(qǐng)求的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02node.js基于socket.io快速實(shí)現(xiàn)一個(gè)實(shí)時(shí)通訊應(yīng)用
這篇文章主要介紹了node.js基于socket.io快速實(shí)現(xiàn)一個(gè)實(shí)時(shí)通訊應(yīng)用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04Node.js 回調(diào)函數(shù)實(shí)例詳解
這篇文章主要介紹了Node.js 回調(diào)函數(shù)實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-07-07