99热99这里只有精品6国产,亚洲中文字幕在线天天更新,在线观看亚洲精品国产福利片 ,久久久久综合网

歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋果6袋
6
麥子學(xué)院

Redis學(xué)習(xí)之通信協(xié)議詳解

發(fā)布時(shí)間:2017-06-24 13:11  回復(fù):0  查看:2393   最后回復(fù):2017-06-24 13:11  
本文和大家分享的主要是redis 通信協(xié)議相關(guān)內(nèi)容,一起來(lái)看看吧,希望對(duì)大家 學(xué)習(xí)redis有所幫助。
  幾乎所有的主流編程語(yǔ)言都有Redis 的客戶端,不考慮 Redis 非常流行的原因,如果站在技術(shù)的角度看原因還有兩個(gè):
  1.  客戶端與服務(wù)端之間的通信協(xié)議是在  TCP 協(xié)議  之上構(gòu)建的。
  客戶端和服務(wù)器通過 TCP  連接來(lái)進(jìn)行數(shù)據(jù)交互, 服務(wù)器默認(rèn)的端口號(hào)為  6379  。
  客戶端和服務(wù)器發(fā)送的命令或數(shù)據(jù)一律以  \r\n  CRLF )結(jié)尾。
  1. Redis 制定了  RESP REdis Serialization Protocol , Redis 序列化協(xié)議)實(shí)現(xiàn)客戶端與服務(wù)端的正常交互,這種協(xié)議簡(jiǎn)單高效,既能夠被機(jī)器解析,又容易被人類識(shí)別。
   發(fā)送命令
  RESP  在  Redis 1.2  版本中引入, 并最終在  Redis 2.0  版本成為  Redis  服務(wù)器通信的標(biāo)準(zhǔn)方式。
  在這個(gè)協(xié)議中,  所有發(fā)送至 Redis  服務(wù)器的參數(shù)都是二進(jìn)制安全( binary safe )的。
  RESP  的規(guī)定一條命令的格式如下:
  *< 參數(shù)數(shù)量> CR LF
  $< 參數(shù) 1  的字節(jié)數(shù)量 > CR LF< 參數(shù) 1  的數(shù)據(jù) > CR LF
  ...
  $< 參數(shù) N  的字節(jié)數(shù)量 > CR LF< 參數(shù) N  的數(shù)據(jù) > CR LF
   命令本身也作為協(xié)議的其中一個(gè)參數(shù)來(lái)發(fā)送。
  例如我們經(jīng)常執(zhí)行的 SET  命令,在命令行中我們輸入如下:
   SET  key  value
  使用 RESP  協(xié)議規(guī)定的格式:
  *3
  $3
  SET
  $3 #  這里  key  一共三個(gè)字節(jié)
  key
  $5 #  這里  value  一共五個(gè)字節(jié)
  value
  這個(gè)命令的實(shí)際協(xié)議值如下:
   "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n"
   回復(fù)
  Redis  命令會(huì)返回多種不同類型的回復(fù)。
  通過檢查服務(wù)器發(fā)回?cái)?shù)據(jù)的第一個(gè)字節(jié),  可以確定這個(gè)回復(fù)是什么類型:
  ·  狀態(tài)回復(fù)( status reply )的第一個(gè)字節(jié)是  "+"
  ·  錯(cuò)誤回復(fù)( error reply )的第一個(gè)字節(jié)是  "-"
  ·  整數(shù)回復(fù)( integer reply )的第一個(gè)字節(jié)是  ":"
  ·  批量回復(fù)( bulk reply )的第一個(gè)字節(jié)是  "$"
  ·  多條批量回復(fù)( multi bulk reply )的第一個(gè)字節(jié)是  "*"
  我們知道redis-cli 只能看到最終的執(zhí)行結(jié)果,那是因?yàn)?/span> redis-cli 本身就按照 RESP 進(jìn)行結(jié)果解析的,所以看不到中間結(jié)果, redis-cli.c  源碼對(duì)命令結(jié)果的解析結(jié)構(gòu)如下:
   static sds  cliFormatReplyTTY(redisReply *r,  char *prefix) {
  sds  out = sdsempty(); switch (r->type) {
  //  處理錯(cuò)誤回復(fù) case REDIS_REPLY_ERROR: out = sdscatprintf( out,"(error) %s\n", r->str); break;//  處理狀態(tài)回復(fù) caseREDIS_REPLY_STATUS: out = sdscat( out,r->str); out = sdscat( out,"\n"); break;//  處理整數(shù)回復(fù) case REDIS_REPLY_INTEGER: out= sdscatprintf( out,"(integer) %lld\n",r->integer); break;//  處理字符串回復(fù) case REDIS_REPLY_STRING:/* If you are producing output for the standard output we want
  * a more interesting output with quoted characters and so forth */ out = sdscatrepr( out,r->str,r->len); out = sdscat( out,"\n"); break;//  處理  nil case REDIS_REPLY_NIL: out = sdscat( out,"(nil)\n"); break;//  處理多回復(fù) caseREDIS_REPLY_ARRAY: if (r->elements == 0) { out = sdscat( out,"(empty list or set)\n");
  else {
  unsigned  int i, idxlen = 0; char _prefixlen[16]; char _prefixfmt[16];
  sds _prefix;
  sds tmp;
  /* Calculate chars needed to represent the largest index */
  i = r->elements; do {
  idxlen++;
  i /= 10;
  while(i);
  /* Prefix for nested multi bulks should grow with idxlen+2 spaces */
  memset(_prefixlen,' ',idxlen+2);
  _prefixlen[idxlen+2] = '\0';
  _prefix = sdscat(sdsnew(prefix),_prefixlen);
  /* Setup prefix format for every entry */
  snprintf(_prefixfmt, sizeof(_prefixfmt),"%%s%%%ud) ",idxlen);
   for (i = 0; i < r->elements; i++) {/* Don't use the prefix for the first element, as the parent
  * caller already prepended the index number. */ out = sdscatprintf( out,_prefixfmt,i == 0 ? "" : prefix,i+1);
  /* Format the multi bulk entry */
  tmp = cliFormatReplyTTY(r->element ,_prefix);out = sdscatlen(out,tmp,sdslen(tmp));
  sdsfree(tmp);
  }
  sdsfree(_prefix);
  }break;default:
  fprintf(stderr,"Unknown reply type: %d\n", r->type);
  exit(1);
  }return out;
  }
  在 發(fā)送命令 一節(jié)中使用的格式除了用作命令請(qǐng)求協(xié)議之外, 也用在命令的回復(fù)協(xié)議中: 這種只有一個(gè)參數(shù)的回復(fù)格式被稱為批量回復(fù)(Bulk Reply 
  統(tǒng)一協(xié)議請(qǐng)求原本是用在回復(fù)協(xié)議中, 用于將列表的多個(gè)項(xiàng)返回給客戶端的, 這種回復(fù)格式被稱為 多條批量回復(fù)(Multi Bulk Reply 。
  一個(gè)多條批量回復(fù)以 *\r\n 為前綴, 后跟多條不同的批量回復(fù), 其中 argc 為這些批量回復(fù)的數(shù)量。
  狀態(tài)回復(fù)
  一個(gè)狀態(tài)回復(fù)(或者單行回復(fù),single line reply)是一段以 "+" 開始、 "\r\n" 結(jié)尾的單行字符串。
  以下是一個(gè)狀態(tài)回復(fù)的例子:
  +OK
  客戶端庫(kù)應(yīng)該返回 "+" 號(hào)之后的所有內(nèi)容。 比如在在上面的這個(gè)例子中, 客戶端就應(yīng)該返回字符串 "OK" 。
  狀態(tài)回復(fù)通常由那些不需要返回?cái)?shù)據(jù)的命令返回,這種回復(fù)不是二進(jìn)制安全的,它也不能包含新行。
  狀態(tài)回復(fù)的額外開銷非常少,只需要三個(gè)字節(jié)(開頭的 "+" 和結(jié)尾的 CRLF)。
  錯(cuò)誤回復(fù)
  錯(cuò)誤回復(fù)和狀態(tài)回復(fù)非常相似, 它們之間的唯一區(qū)別是, 錯(cuò)誤回復(fù)的第一個(gè)字節(jié)是 "-"  而狀態(tài)回復(fù)的第一個(gè)字節(jié)是 "+" 。
  錯(cuò)誤回復(fù)只在某些地方出現(xiàn)問題時(shí)發(fā)送: 比如說(shuō), 當(dāng)用戶對(duì)不正確的數(shù)據(jù)類型執(zhí)行命令, 或者執(zhí)行一個(gè)不存在的命令, 等等。
  一個(gè)客戶端庫(kù)應(yīng)該在收到錯(cuò)誤回復(fù)時(shí)產(chǎn)生一個(gè)異常。
  以下是兩個(gè)錯(cuò)誤回復(fù)的例子:
  -ERR unknown command 'foobar'
  -WRONGTYPE Operation against a key holding the wrong kind of value
  在 "-" 之后,直到遇到第一個(gè)空格或新行為止,這中間的內(nèi)容表示所返回錯(cuò)誤的類型。
  ERR 是一個(gè)通用錯(cuò)誤,而 WRONGTYPE 則是一個(gè)更特定的錯(cuò)誤。 一個(gè)客戶端實(shí)現(xiàn)可以為不同類型的錯(cuò)誤產(chǎn)生不同類型的異常, 或者提供一種通用的方式, 讓調(diào)用者可以通過提供字符串形式的錯(cuò)誤名來(lái)捕捉(trap)不同的錯(cuò)誤。
  不過這些特性用得并不多, 所以并不是特別重要, 一個(gè)受限的(limited)客戶端可以通過簡(jiǎn)單地返回一個(gè)邏輯假(false)來(lái)表示一個(gè)通用的錯(cuò)誤條件。
  整數(shù)回復(fù)
  整數(shù)回復(fù)就是一個(gè)以 ":" 開頭, CRLF 結(jié)尾的字符串表示的整數(shù)。
  比如說(shuō), ":0\r\n"  ":1000\r\n" 都是整數(shù)回復(fù)。
  返回整數(shù)回復(fù)的其中兩個(gè)命令是 INCR  LASTSAVE  被返回的整數(shù)沒有什么特殊的含義, INCR 返回鍵的一個(gè)自增后的整數(shù)值,  LASTSAVE 則返回一個(gè) UNIX 時(shí)間戳, 返回值的唯一限制是這些數(shù)必須能夠用 64 位有符號(hào)整數(shù)表示。
  整數(shù)回復(fù)也被廣泛地用于表示邏輯真和邏輯假: 比如 EXISTS  SISMEMBER 都用返回值 1 表示真, 0 表示假。
  其他一些命令, 比如 SADD 、 SREM  SETNX , 只在操作真正被執(zhí)行了的時(shí)候, 才返回 1, 否則返回 0 
  以下命令都返回整數(shù)回復(fù): SETNX 、 DEL 、 EXISTS 、 INCR 、 INCRBY  DECR  DECRBY 、 DBSIZE 、LASTSAVE 、 RENAMENX 、 MOVE  LLEN 、 SADD  SREM 、 SISMEMBER  SCARD 。
  批量回復(fù)
  服務(wù)器使用批量回復(fù)來(lái)返回二進(jìn)制安全的字符串,字符串的最大長(zhǎng)度為 512 MB 。
  客戶端:GET mykey
  服務(wù)器:foobar
  服務(wù)器發(fā)送的內(nèi)容中:
  · 第一字節(jié)為 "$" 符號(hào) - 接下來(lái)跟著的是表示實(shí)際回復(fù)長(zhǎng)度的數(shù)字值 之后跟著一個(gè) CRLF - 再后面跟著的是實(shí)際回復(fù)數(shù)據(jù) 最末尾是另一個(gè) CRLF
  對(duì)于前面的 GET 命令,服務(wù)器實(shí)際發(fā)送的內(nèi)容為:
  "$6\r\nfoobar\r\n"
  如果被請(qǐng)求的值不存在, 那么批量回復(fù)會(huì)將特殊值 -1 用作回復(fù)的長(zhǎng)度值, 就像這樣:
  客戶端:GET non-existing-key
  服務(wù)器:$-1
  這種回復(fù)稱為空批量回復(fù)(NULL Bulk Reply)。
  當(dāng)請(qǐng)求對(duì)象不存在時(shí),客戶端應(yīng)該返回空對(duì)象,而不是空字符串: 比如 Ruby 庫(kù)應(yīng)該返回 nil,  C 庫(kù)應(yīng)該返回 NULL (或者在回復(fù)對(duì)象中設(shè)置一個(gè)特殊標(biāo)志), 諸如此類。
  多條批量回復(fù)
  像 LRANGE 這樣的命令需要返回多個(gè)值, 這一目標(biāo)可以通過多條批量回復(fù)來(lái)完成。
  多條批量回復(fù)是由多個(gè)回復(fù)組成的數(shù)組, 數(shù)組中的每個(gè)元素都可以是任意類型的回復(fù), 包括多條批量回復(fù)本身。
  多條批量回復(fù)的第一個(gè)字節(jié)為 "*" , 后跟一個(gè)字符串表示的整數(shù)值, 這個(gè)值記錄了多條批量回復(fù)所包含的回復(fù)數(shù)量, 再后面是一個(gè) CRLF 
  客戶端: LRANGE mylist 0 3
  服務(wù)器: *4
  服務(wù)器: $3
  服務(wù)器: foo
  服務(wù)器: $3
  服務(wù)器: bar
  服務(wù)器: $5
  服務(wù)器: Hello
  服務(wù)器: $5
  服務(wù)器: World
  在上面的示例中,服務(wù)器發(fā)送的所有字符串都由 CRLF 結(jié)尾。
  正如你所見到的那樣, 多條批量回復(fù)所使用的格式, 和客戶端發(fā)送命令時(shí)使用的統(tǒng)一請(qǐng)求協(xié)議的格式一模一樣。 它們之間的唯一區(qū)別是:
  · 統(tǒng)一請(qǐng)求協(xié)議只發(fā)送批量回復(fù)。
  · 而服務(wù)器應(yīng)答命令時(shí)所發(fā)送的多條批量回復(fù),則可以包含任意類型的回復(fù)。
  以下例子展示了一個(gè)多條批量回復(fù), 回復(fù)中包含四個(gè)整數(shù)值, 以及一個(gè)二進(jìn)制安全字符串:
  *5\r\n
  :1\r\n
  :2\r\n
  :3\r\n
  :4\r\n
  $6\r\n
  foobar\r\n
  在回復(fù)的第一行, 服務(wù)器發(fā)送 *5\r\n , 表示這個(gè)多條批量回復(fù)包含 5 條回復(fù), 再后面跟著的則是 條回復(fù)的正文。
  多條批量回復(fù)也可以是空白的(empty), 就像這樣:
  客戶端: LRANGE nokey 0 1
  服務(wù)器: *0\r\n
  無(wú)內(nèi)容的多條批量回復(fù)(null multi bulk reply)也是存在的, 比如當(dāng) BLPOP 命令的阻塞時(shí)間超過最大時(shí)限時(shí), 它就返回一個(gè)無(wú)內(nèi)容的多條批量回復(fù), 這個(gè)回復(fù)的計(jì)數(shù)值為 -1 
  客戶端: BLPOP key 1
  服務(wù)器: *-1\r\n
  客戶端庫(kù)應(yīng)該區(qū)別對(duì)待空白多條回復(fù)和無(wú)內(nèi)容多條回復(fù): 當(dāng) Redis 返回一個(gè)無(wú)內(nèi)容多條回復(fù)時(shí), 客戶端庫(kù)應(yīng)該返回一個(gè) null 對(duì)象, 而不是一個(gè)空數(shù)組。
  多條批量回復(fù)中的空元素
  多條批量回復(fù)中的元素可以將自身的長(zhǎng)度設(shè)置為 -1 , 從而表示該元素不存在, 并且也不是一個(gè)空白字符串(empty string)。
  當(dāng) SORT 命令使用 GET pattern 選項(xiàng)對(duì)一個(gè)不存在的鍵進(jìn)行操作時(shí), 就會(huì)發(fā)生多條批量回復(fù)中帶有空白元素的情況。
  以下例子展示了一個(gè)包含空元素的多重批量回復(fù):
  服務(wù)器: *3
  服務(wù)器: $3
  服務(wù)器: foo
  服務(wù)器: $-1
  服務(wù)器: $3
  服務(wù)器: bar
  其中, 回復(fù)中的第二個(gè)元素為空。
  對(duì)于這個(gè)回復(fù), 客戶端庫(kù)應(yīng)該返回類似于這樣的回復(fù):
  ["foo", nil, "bar"]
  多命令和 pipline
  客戶端可以通過 pipline , 在一次寫入操作中發(fā)送多個(gè)命令:
  · 在發(fā)送新命令之前, 無(wú)須閱讀前一個(gè)命令的回復(fù)。
  · 多個(gè)命令的回復(fù)會(huì)在最后一并返回。
  內(nèi)聯(lián)命令
  當(dāng)你需要和 Redis 服務(wù)器進(jìn)行溝通, 但又找不到 redis-cli  而手上只有 telnet 的時(shí)候, 你可以通過 Redis 特別為這種情形而設(shè)的內(nèi)聯(lián)命令格式來(lái)發(fā)送命令。
  以下是一個(gè)客戶端和服務(wù)器使用內(nèi)聯(lián)命令來(lái)進(jìn)行交互的例子:
  客戶端: PING
  服務(wù)器: +PONG
  以下另一個(gè)返回整數(shù)值的內(nèi)聯(lián)命令的例子:
  客戶端: EXISTS somekey
  服務(wù)器: :0
  因?yàn)闆]有了統(tǒng)一請(qǐng)求協(xié)議中的 "*" 項(xiàng)來(lái)聲明參數(shù)的數(shù)量, 所以在 telnet 會(huì)話輸入命令的時(shí)候, 必須使用空格來(lái)分割各個(gè)參數(shù),服務(wù)器在接收到數(shù)據(jù)之后, 會(huì)按空格對(duì)用戶的輸入進(jìn)行分析(parse), 并獲取其中的命令參數(shù)。
  高性能 Redis 協(xié)議分析器
  盡管 Redis 的協(xié)議非常利于人類閱讀, 定義也很簡(jiǎn)單, 但這個(gè)協(xié)議的實(shí)現(xiàn)性能仍然可以和二進(jìn)制協(xié)議一樣快。
  因?yàn)?/span> Redis 協(xié)議將數(shù)據(jù)的長(zhǎng)度放在數(shù)據(jù)正文之前, 所以程序無(wú)須像 JSON 那樣, 為了尋找某個(gè)特殊字符而掃描整個(gè) payload , 也無(wú)須對(duì)發(fā)送至服務(wù)器的 payload 進(jìn)行轉(zhuǎn)義(quote)。
  程序可以在對(duì)協(xié)議文本中的各個(gè)字符進(jìn)行處理的同時(shí), 查找 CR 字符, 并計(jì)算出批量回復(fù)或多條批量回復(fù)的長(zhǎng)度, 就像這樣:
  #include
  int main(void) {
  unsigned char *p = "$123\r\n";int len = 0;
  p++;while(*p != '\r') {
  len = (len*10)+(*p - '0');
  p++;
  }
  /* Now p points at '\r', and the len is in bulk_len. */printf("%d\n", len);return 0;
  }
  得到了批量回復(fù)或多條批量回復(fù)的長(zhǎng)度之后, 程序只需調(diào)用一次 read 函數(shù), 就可以將回復(fù)的正文數(shù)據(jù)全部讀入到內(nèi)存中, 而無(wú)須對(duì)這些數(shù)據(jù)做任何的處理。
  在回復(fù)最末尾的 CR 和 LF 不作處理,丟棄它們。
  Redis 協(xié)議的實(shí)現(xiàn)性能可以和二進(jìn)制協(xié)議的實(shí)現(xiàn)性能相媲美, 并且由于 Redis 協(xié)議的簡(jiǎn)單性, 大部分高級(jí)語(yǔ)言都可以輕易地實(shí)現(xiàn)這個(gè)協(xié)議, 這使得客戶端軟件的 bug 數(shù)量大大減少。
  Linux 下 使用 nc 命令操作 Redis
  [coderknock ~]# nc 127.0.0.1 6379
  set hello world
  +OK                          #狀態(tài)回復(fù)
  sethx
  -ERR unknown command 'sethx' #錯(cuò)誤回復(fù):由于sethx這條命令不存在,那么返回結(jié)果就是"-"號(hào)加上錯(cuò)誤消息
  incr counter:1                           #整數(shù)回復(fù):當(dāng)命令的執(zhí)行結(jié)果是整數(shù)時(shí),返回結(jié)果就是整數(shù)回復(fù),例如 increxists、del、dbsize返回結(jié)果都是整數(shù)
  get hello
  $5                           #字符串回復(fù):當(dāng)命令的執(zhí)行結(jié)果是字符串時(shí),返回結(jié)果就是字符串回復(fù)。
  world                        #實(shí)際返回的是 $5\r\nworld\r\n
  mset java jedis python redis-py
  +OK
  mget java python             #多條字符串回復(fù):當(dāng)命令的執(zhí)行結(jié)果是多條字符串時(shí),返回結(jié)果就是多條字符串回復(fù)
  *2
  $5
  jedis
  $8
  redis-py
  get not_exist_key            #無(wú)論是字符串回復(fù)還是多條字符串回復(fù),如果有 nil 值,那么會(huì)返回$-1
  $-1
  mget hello not_exist_key java
  *3
  $5
  world
  $-1
  $5
  jedis
  Python Socket 操作 Redis
  使用 socket 操作 Redis
  import socket
  # AF_INET指定使用 IPv4 協(xié)議
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.SOL_TCP)
  s.connect(('127.0.0.1', 6379))
  print('get connected from', '127.0.0.1')# 驗(yàn)證密碼
  s.send(b'*2\r\n$4\r\nAUTH\r\n$8\r\nadmin123\r\n')
  ra = s.recv(512)
  print(ra)# 發(fā)送一條信息
  s.send(b'*3\r\n$3\r\nSET\r\n$8\r\ntestRESP\r\n$10\r\nRESPpython\r\n')
  ra = s.recv(512)
  print(ra)
  s.close()
  執(zhí)行結(jié)果:
  get connected from 127.0.0.1
  b'+OK\r\n'
  b'+OK\r\n'
  我們從命令行中查詢:
  redis> GET testRESP"RESPpython"
  可以看到正確的向 Redis 中插入了鍵值。
來(lái)源:網(wǎng)絡(luò)

您還未登錄,請(qǐng)先登錄

熱門帖子

最新帖子

?