自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Redis:我是如何與客戶端進行通信的

網(wǎng)絡 網(wǎng)絡管理 Redis
我把創(chuàng)造出來的這種協(xié)議稱為 RESP (REdis Serialization Protocol)協(xié)議,它工作在 TCP 協(xié)議的上層,作為我和客戶端之間進行通訊的標準形式。

[[406813]]

江湖上說,天下武功,無堅不摧,唯快不破,這句話簡直是為我量身定制。

我是一個Redis服務,最引以為傲的就是我的速度,我的 QPS 能達到10萬級別。

在我的手下有數(shù)不清的小弟,他們會時不時到我這來存放或者取走一些數(shù)據(jù),我管他們叫做客戶端,還給他們起了英文名叫 Redis-client。

有時候一個小弟會來的非常頻繁,有時候一堆小弟會同時過來,但是,即使再多的小弟我也能管理的井井有條。

有一天,小弟們問我。

想當年,為了不讓小弟們拖垮我傲人的速度,在設計和他們的通信協(xié)議時,我絞盡腦汁,制定了下面的三條原則:

  • 實現(xiàn)簡單
  • 針對計算機來說,解析速度快
  • 針對人類來說,可讀性強

為什么這么設計呢?先來看看一條指令發(fā)出的過程,首先在客戶端需要對指令操作進行封裝,使用網(wǎng)絡進行傳輸,最后在服務端進行相應的解析、執(zhí)行。

圖片

這一過程如果設計成一種非常復雜的協(xié)議,那么封裝、解析、傳輸?shù)倪^程都將非常耗時,無疑會降低我的速度。什么,你問我為什么要遵循最后一條規(guī)則?算是對于程序員們的饋贈吧,我真是太善良了。

我把創(chuàng)造出來的這種協(xié)議稱為 RESP (REdis Serialization Protocol)協(xié)議,它工作在 TCP 協(xié)議的上層,作為我和客戶端之間進行通訊的標準形式。

說到這,我已經(jīng)有點迫不及待想讓你們看看我設計出來的杰作了,但我好歹也是個大哥,得擺點架子,不能我主動拿來給你們看。

所以我建議你直接使用客戶端發(fā)出一條向服務器的命令,然后取出這條命令對應的報文來直觀的看一下。話雖如此,不過我已經(jīng)被封裝的很嚴實了,正常情況下你是看不到我內(nèi)部進行通訊的具體報文的,所以,你可以偽裝成一個Redis的服務端,來截獲小弟們發(fā)給我的消息。

實現(xiàn)起來也很簡單,我和小弟之間是基于 Socket 進行通訊,所以在本地先啟動一個ServerSocket,用來監(jiān)聽Redis服務的6379端口:

  1. public static void server() throws IOException { 
  2.     ServerSocket serverSocket = new ServerSocket(6379); 
  3.     Socket socket = serverSocket.accept(); 
  4.     byte[] bytes = new byte[1024]; 
  5.     InputStream input = socket.getInputStream(); 
  6.     while(input.read(bytes)!=0){ 
  7.         System.out.println(new String(bytes)); 
  8.     } 

然后啟動redis-cli客戶端,發(fā)送一條命令:

  1. set key1 value1 

這時,偽裝的服務端就會收到報文了,在控制臺打印了:

  1. *3 
  2. $3 
  3. set 
  4. $4 
  5. key1 
  6. $6 
  7. value1 

看到這里,隱隱約約看到了剛才輸入的幾個關鍵字,但是還有一些其他的字符,要怎么解釋呢,是時候讓我對協(xié)議報文中的格式進行一下揭秘了。

我對小弟們說了,對大哥說話的時候得按規(guī)矩來,這樣吧,你們在請求的時候要遵循下面的規(guī)則:

  1. *<參數(shù)數(shù)量> CRLF 
  2. $<參數(shù)1的字節(jié)長度> CRLF 
  3. <參數(shù)1的數(shù)據(jù)> CRLF 
  4. $<參數(shù)2的字節(jié)長度> CRLF 
  5. <參數(shù)2的數(shù)據(jù)> CRLF 
  6. ... 
  7. $<參數(shù)N的字節(jié)長度> CRLF 
  8. <參數(shù)N的數(shù)據(jù)> CRLF 

首先解釋一下每行末尾的CRLF,轉(zhuǎn)換成程序語言就是\r\n,也就是回車加換行??吹竭@里,你也就能夠明白為什么控制臺打印出的指令是豎向排列了吧。

在命令的解析過程中,set、key1、value1會被認為是3個參數(shù),因此參數(shù)數(shù)量為3,對應第一行的*3。

第一個參數(shù)set,長度為3對應$3;第二個參數(shù)key1,長度為4對應$4;第三個參數(shù)value1,長度為6對應$6。在每個參數(shù)長度的下一行對應真正的參數(shù)數(shù)據(jù)。

看到這,一條指令被轉(zhuǎn)換為協(xié)議報文的過程是不是就很好理解了?

圖片

當小弟對我發(fā)送完請求后,作為大哥,我就要對小弟的請求進行指令回復了,而且我得根據(jù)回復內(nèi)容進行一下分類,要不然小弟該搞不清我的指示了。

簡單字符串

簡單字符串回復只有一行回復,回復的內(nèi)容以+作為開頭,不允許換行,并以\r\n結(jié)束。有很多指令在執(zhí)行成功后只會回復一個OK,使用的就是這種格式,能夠有效的將傳輸、解析的開銷降到最低。

錯誤回復

在RESP協(xié)議中,錯誤回復可以當做簡單字符串回復的變種形式,它們之間的格式也非常類似,區(qū)別只有第一個字符是以-作為開頭,錯誤回復的內(nèi)容通常是錯誤類型及對錯誤描述的字符串。

錯誤回復出現(xiàn)在一些異常的場景,例如當發(fā)送了錯誤的指令、操作數(shù)的數(shù)量不對時,都會進行錯誤回復。在客戶端收到錯誤回復后,會將它與簡單字符串回復進行區(qū)分,視為異常。

整數(shù)回復

整數(shù)回復的應用也非常廣泛,它以:作為開頭,以\r\n結(jié)束,用于返回一個整數(shù)。例如當執(zhí)行incr后返回自增后的值,執(zhí)行l(wèi)len返回數(shù)組的長度,或者使用exists命令返回的0或1作為判斷一個key是否存在的依據(jù),這些都使用了整數(shù)回復。

批量回復

批量回復,就是多行字符串的回復。它以$作為開頭,后面是發(fā)送的字節(jié)長度,然后是\r\n,然后發(fā)送實際的數(shù)據(jù),最終以\r\n結(jié)束。如果要回復的數(shù)據(jù)不存在,那么回復長度為-1。

多條批量回復

當服務端要返回多個值時,例如返回一些元素的集合時,就會使用多條批量回復。它以*作為開頭,后面是返回元素的個數(shù),之后再跟隨多個上面講到過的批量回復。

到這里,基本上我和小弟之間的通訊協(xié)議就介紹完了。剛才你嘗試了偽裝成一個服務端,這會再來試一試直接寫一個客戶端來直接和我進行交互吧。

  1. private static void client() throws IOException { 
  2.     String CRLF="\r\n"
  3.  
  4.     Socket socket=new Socket("localhost", 6379); 
  5.     try (OutputStream out = socket.getOutputStream()) { 
  6.         StringBuffer sb=new StringBuffer(); 
  7.         sb.append("*3").append(CRLF) 
  8.                 .append("$3").append(CRLF).append("set").append(CRLF) 
  9.                 .append("$4").append(CRLF).append("key1").append(CRLF) 
  10.                 .append("$6").append(CRLF).append("value1").append(CRLF); 
  11.         out.write(sb.toString().getBytes()); 
  12.         out.flush(); 
  13.  
  14.         try (InputStream inputStream = socket.getInputStream()) { 
  15.             byte[] buff = new byte[1024]; 
  16.             int len = inputStream.read(buff); 
  17.             if (len > 0) { 
  18.                 String ret = new String(buff, 0, len); 
  19.                 System.out.println("Recv:" + ret); 
  20.             } 
  21.         } 
  22.     } 

運行上面的代碼,控制臺輸出:

  1. Recv:+OK 

上面模仿了客戶端發(fā)出set命令的過程,并收到了回復。依此類推,你也可以自己封裝其他的命令,來實現(xiàn)一個自己的Redis客戶端,作為小弟,來和我進行通信。

不過記住,要叫我大哥。

 

責任編輯:姜華 來源: 碼農(nóng)參上
相關推薦

2010-03-18 17:39:46

Java Socket

2009-08-21 15:59:22

服務端與客戶端通信

2009-08-21 16:14:52

服務端與客戶端通信

2010-01-05 14:32:01

JSON 數(shù)據(jù)

2010-06-01 14:35:16

SVN倉庫導出

2018-01-12 09:20:55

2010-12-17 10:16:33

OpenVAS

2010-05-31 10:11:32

瘦客戶端

2014-12-08 14:54:56

hadoop框架線程網(wǎng)絡

2009-08-01 22:47:58

2013-03-20 11:01:37

Redis客戶端連接

2020-09-14 11:27:32

工具代碼開發(fā)

2021-09-22 15:46:29

虛擬桌面瘦客戶端胖客戶端

2010-03-18 17:47:07

Java 多客戶端通信

2010-03-16 15:50:59

Java遠程控制

2010-12-31 14:23:57

Exchange Se

2013-03-13 10:51:44

瘦客戶端VDI

2010-05-23 23:36:38

思科統(tǒng)一通信

2021-08-01 23:18:21

Redis Golang命令

2020-05-29 17:20:04

MySQL中間件數(shù)據(jù)庫
點贊
收藏

51CTO技術棧公眾號