用Nodejs 實現一個簡單的 Redis客戶端

目錄

  • 0. 寫在前面
  • 1. 背景映入
  • 2. 數據庫選擇
  • 3. Nodejs TCP連接
  • 3. 代碼編寫
  • 4. 實驗
  • 5. wireshark 抓包分析
  • 6. 雜與代碼
0. 寫在前面
大家如果有去看過nodejs所支持的官方庫的話 , 應該會驚訝于它所提供了非常完善的網絡庫,不僅是應用層,傳輸層,等等基礎的協議,我們可以按照事件驅動的邏輯編寫清晰易懂的網絡應用 , 網絡服務 。這也是本文為什么選擇Nodejs編寫的原因 。
1. 背景映入大家在使用一些數據庫軟件的時候常常會使用遠程連接
mysql -h xxx.xxx.xxx.xx -u xzzz -p這里也指明了ip地址 , 但是很明顯這里可不是http協議在服務,而是更加底層的協議 - 傳輸層協議,具體來說是TCP協議(Transmission Control Protocol) 。通信的示意圖如下:
用Nodejs 實現一個簡單的 Redis客戶端

文章插圖
所以很自然的想到,數據庫的客戶端一定經過如下流程,從而與遠程相連接:
graph TB 身份驗證 --> 運輸層連接建立 運輸層連接建立 --> 客戶端服務端輸入輸出綁定_通道 客戶端服務端輸入輸出綁定_通道 --> 連接中斷 連接中斷 --> 雙方退出釋放資源所以我們可以嘗試向服務端發送這樣的請求消息,建立與服務端的連接 , 發送一些數據,接受一些數據 , 最后斷開連接 。
2. 數據庫選擇這里為了簡單起見,我們考慮不需要身份驗證的redis數據庫來作為此次實驗的服務端 。如果大家是mac,或者linux倒是可以直接安裝,如果是windows的話 , 推薦使用docker進行安裝,這里給出一行docker命令 。
docker run--name redis-server -p 6379:6379 -d redis:latest3. Nodejs TCP連接在nodejs中支持TCP連接的是net模塊, 其中使用createConnection(config)或者直接new Socket(config)來初始化一個TCP連接 。上面兩個函數不論哪一個都會返回socket實例,如果連接正常的話,就可以通過這個socket發送消息了 。
用Nodejs 實現一個簡單的 Redis客戶端

文章插圖
用Nodejs 實現一個簡單的 Redis客戶端

文章插圖
當服務端redis接收到消息之后也會返回相應的消息,在本機客戶端通過對數據的校驗,檢查后 , 觸發相應的操作(是拒絕還是接受服務端的響應) 。
3. 代碼編寫知道了原理之后,我這里直接把代碼貼出來
  • RedisSocket: 繼承自Socket
class RedisSocket extends Socket {constructor(config: RedisClientConfig) {super();this.connect(config.port, config.host);} // Setpublic 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 把函數取消綁定} else {reject("error! can't set data");}}this.on("data", fetchAns);})} // Getpublic 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 把函數取消綁定}this.on("data", fetchAns);} catch(err) {reject(err);}})} // 斷開TCPpublic close() {this.end();}}
這個類將用來處理建立好后的連接的
  • RedisClient
class RedisClient {private config: RedisClientConfig;constructor(config: RedisClientConfig) {this.config = config; // 配置項} // 獲取redis實例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);});});}}
這個類用來建立與服務端的連接,使用getConnection()方法,將會交付一個redisSocket,使用這個Socket可以直接向server發送和接受數據 。
4. 實驗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的連接,隨后通過getConnection()獲取到連接實例,并通過這個連接實例設置了兩個數據,以及獲取了一數據并打印了出來 。

推薦閱讀