原文鏈接: https://mp.weixin.qq.com/s/IBynkex-FHhvJ3tmizvJhA
站在用戶的角度思考問題,與客戶深入溝通,找到蘇州網(wǎng)站設計與蘇州網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗,讓設計與互聯(lián)網(wǎng)技術結合,創(chuàng)造個性化、用戶體驗好的作品,建站類型包括:網(wǎng)站設計制作、成都網(wǎng)站設計、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣、域名申請、雅安服務器托管、企業(yè)郵箱。業(yè)務覆蓋蘇州地區(qū)。redis大家在項目中經(jīng)常會使用到。官網(wǎng)也提供了多語言的客戶端供大家操作redis,如下圖所示
但是,大家有思考過,這些語言操作redis背后的原理么?其實,某些大神會說
只要按照redis的協(xié)議,發(fā)送指定數(shù)據(jù)給redis,監(jiān)聽返回值即可。
確實,本質原理就是如上面那句話所說。博主也是以這種思路,去看了一下JAVA端的開源組件jedis的源碼,然后取其精華,寫了一個段能操作redis的demo,希望大家能有所收獲。
jedis的github地址為:
https://github.com/xetorthio/jedis
有興趣的童鞋,也可以自行去閱讀。需要說明的是,這畢竟不是源碼分析系列文章,不是帶你去看jedis的源碼。只是借鑒思路,寫一個能操作redis的程序。
首先,我先說一下操作思路,如下圖所示
說明一下,上面的第四步,就是我們自己要寫的操作redis的小demo。
這個程序很easy,度娘一下出來一大把
import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.net.ServerSocket; import java.net.Socket; public class SocketServer { public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(6379); Socket socket = server.accept(); byte[] chars = new byte[64]; socket.getInputStream().read(chars); System.out.println(new String(chars)); } }
我這里用的是JAVA語言的jedis,大家自己也可以用其他的任意語言組件,目的是為了采集客戶端在操作redis時,發(fā)送出的數(shù)據(jù)
import redis.clients.jedis.Jedis; public class RedisTest { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.set("eat", "I want to eat"); } }
在這里運行一下第二步的代碼,查看第一步的代碼輸出的數(shù)據(jù),如下所示
*3 $3 SET $3 eat $13 I want to eat
那么,這組數(shù)據(jù)是什么含義呢?
我們去官網(wǎng)進行查詢。原來,redis的客戶端和服務端采取了一種RESP協(xié)議。相應文檔地址如下
https://redis.io/topics/protocol
RESP設計巧妙,它的前景在于下面三個方面:
Simple to implement.
Fast to parse.
Human readable.
那么+、-、*、:、$這些符號是什么意思呢?
官網(wǎng)有這么一段話
In RESP, the type of some data depends on the first byte: For Simple Strings the first byte of the reply is "+"
For Errors the first byte of the reply is "-"
For Integers the first byte of the reply is ":"
For Bulk Strings the first byte of the reply is "$"
For Arrays the first byte of the reply is "*"
Additionally RESP is able to represent a Null value using a special variation of Bulk Strings or Array as specified later.
In RESP different parts of the protocol are always terminated with "\r\n" (CRLF).
翻譯過來
(1)簡單字符串Simple Strings, 以 "+"加號 開頭
(2)錯誤Errors, 以"-"減號 開頭
(3)整數(shù)型Integer, 以 ":" 冒號開頭
(4)大字符串類型Bulk Strings, 以 "$"美元符號開頭,長度限制512M
(5)組類型Arrays,以 "*"星號開頭
并且,協(xié)議的每部分都是以 "\r\n" (CRLF) 結尾的。
OK,那我們剛才的那一串的數(shù)據(jù)的意思就是(沒有看到""\r\n",是因為已經(jīng)轉義了,所以無法看到):
*3 數(shù)組包含3個元素,分別是SET、eat、I want to eat $3 是一個字符串,且字符串長度為3 SET 字符串的內容 $3 是一個字符串,且字符串長度為3 eat 字符串的內容 $13 是一個字符串,且字符串長度為13 I want to eat 字符串的內容
提問,如果是get命令,那么傳輸?shù)腞ESP的內容長什么樣?
比如有一個命令get eat,那么此時的內容如下所示
*2 $3 GET $3 eat
沒有\(zhòng)r\n是因為已經(jīng)轉義了,所以沒看到。其他的命令,可以自行測試。
OK,經(jīng)過上面的鋪墊。我們如果要對redis做一個set操作,則構造set命令的RESP協(xié)議內容,并且利用socket編程,將這串內容發(fā)送給redis即可。這里用java的socket編程實現(xiàn),用其他語言也是一樣的。
我們有一個類 RedisClient.java
代碼如下
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class RedisClient { private Socket socket; private OutputStream outputStream; private InputStream inputStream; public RedisClient(String host, int port){ try { this.socket = new Socket(host,port); this.outputStream = this.socket.getOutputStream(); this.inputStream = this.socket.getInputStream(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public String set(final String key, String value) { StringBuilder sb = new StringBuilder(); //雖然輸出的時候,會被轉義,然而我們傳送的時候還是要帶上\r\n sb.append("*3").append("\r\n"); sb.append("$3").append("\r\n"); sb.append("SET").append("\r\n"); sb.append("$").append(key.length()).append("\r\n"); sb.append(key).append("\r\n"); sb.append("$").append(value.length()).append("\r\n"); sb.append(value).append("\r\n"); byte[] bytes= new byte[1024]; try { outputStream.write(sb.toString().getBytes()); inputStream.read(bytes); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return new String(bytes); } public static void main(String[] args) { RedisClient redisClient = new RedisClient("127.0.0.1", 6379); String result = redisClient.set("eat", "please eat"); System.out.println(result); } }
上面的public String set(final String key, String value)方法中,顯示了,我們假如需要對redis進行set操作,需要傳輸?shù)腞ESP協(xié)議的內容。記住,一定要帶\r\n字符作為結尾
OK,運行上述代碼,你會發(fā)現(xiàn)你可以往redis中set數(shù)據(jù)了,并且控制臺輸出如下
+OK
提問,你自己會封裝get命令么?
本文以一種循序漸進的方式帶領大家寫了一個能操作redis的demo,希望大家有所收獲。