1. 程式人生 > >Redis 客戶端 Hiredis 簡介

Redis 客戶端 Hiredis 簡介

學習總結一下官方釋出的C版本客戶端 hiredis,瞭解hiredis 客戶端大致實現細節。在理解程式碼之間需要了解通訊協議的特點,我上一篇轉載的文章已經有過介紹,大家可以去看一下。

hiredis 提供了同步、非同步訪問,非同步 API 需要與一些事件庫協同工作,主要看一下同步API的實現。

hiredis 與服務端通訊的API比較簡單,主要有這幾個步驟:

  • 建立連線
  • 傳送命令
  • 等待結果並處理
  • 釋放連線

一、相關資料結構

redisContext 儲存連線建立後的上下文。 err 儲存錯誤碼,如果為0表示沒錯,如果非0那麼錯誤說明儲存在 errstr 中;fd是連線建立後的套接字;flags

表示連線的標識;obuf 儲存要向 redis-server 傳送的命令內容;reader 用來讀取從服務端返回的訊息,redisReader中的buf成員用來儲存內容;connection_type 表示連線型別,有兩種分別是REDIS_CONN_TCPREDIS_CONN_UNIXtimeout 是連線時指定的超時時間。

/* Context for a connection to Redis */
typedef struct redisContext {
    int err; /* Error flags, 0 when there is no error */
    char
errstr[128]; /* String representation of error when applicable */ int fd; int flags; char *obuf; /* Write buffer */ redisReader *reader; /* Protocol reader */ enum redisConnectionType connection_type; struct timeval *timeout; struct { char *host; char *source_addr; int
port; } tcp; struct { char *path; } unix_sock; } redisContext;
/* This is the reply object returned by redisCommand() */
typedef struct redisReply {
    int type; /* REDIS_REPLY_* */
    long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
    size_t len; /* Length of string */
    char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
    size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
    struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
} redisReply;

redisReply 這個結構是儲存傳送命令後得到的返回結果,type 表示伺服器返回結果的型別,比如 REDIS_REPLY_STRING,表示返回一個 string,此時內容就儲存在 str 這個成員中,其他成員類似。有下面這幾種型別:

#define REDIS_REPLY_STRING 1
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3
#define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5
#define REDIS_REPLY_ERROR 6

(二)相關 api

建立連線

redisContext *redisConnect(const char *ip, int port);
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
redisContext *redisConnectNonBlock(const char *ip, int port);
redisContext *redisConnectBindNonBlock(const char *ip, int port,
                                       const char *source_addr);
redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
                                                const char *source_addr);
redisContext *redisConnectUnix(const char *path);
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
redisContext *redisConnectUnixNonBlock(const char *path);
redisContext *redisConnectFd(int fd);

這些 api 都是建立連線的,可以根據需求或者條件選擇合適的。連線建立成功返回 redisContext,將連線資訊放到這個結構中,通過 err 成員來判斷是否建立成功。

傳送命令並等待結果

redisCommandredisAppendCommand 系列函式用來向伺服器傳送命令。

redisCommand -> redisvCommand -> redisvAppendCommand -> __redisAppendCommand

int __redisAppendCommand(redisContext *c, const char *cmd, size_t len) {
    sds newbuf;

    newbuf = sdscatlen(c->obuf,cmd,len);
    if (newbuf == NULL) {
        __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
        return REDIS_ERR;
    }

    c->obuf = newbuf;
    return REDIS_OK;
}

這個函式組裝傳送的命令到redisContextobuf 中,obuf 可以動態擴容,所以不用擔心溢位;接下來在函式 redisCommand 中呼叫 __redisBlockForReply,繼而呼叫 redisGetReply 這個函式來獲取返回結果。

int redisGetReply(redisContext *c, void **reply) {
    int wdone = 0;
    void *aux = NULL;

    /* Try to read pending replies */
    if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
        return REDIS_ERR;

    /* For the blocking context, flush output buffer and read reply */
    if (aux == NULL && c->flags & REDIS_BLOCK) {
        /* Write until done */
        do {
            if (redisBufferWrite(c,&wdone) == REDIS_ERR)
                return REDIS_ERR;
        } while (!wdone);

        /* Read until there is a reply */
        do {
            if (redisBufferRead(c) == REDIS_ERR)
                return REDIS_ERR;
            if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
                return REDIS_ERR;
        } while (aux == NULL);
    }

    /* Set reply object */
    if (reply != NULL) *reply = aux;
    return REDIS_OK;
}

這個函式分下面幾步實現:
- 首先檢視 redisReader 結構中的 buf 成員是否有資料,有的話則解析出 reply 並返回,否則進入下面;
- 把 obuf 緩衝區的資料全部寫入 c->fd,寫完後釋放 obuf
- 接下來阻塞讀取伺服器返回的結果並將其放入redisContext -> redisReader -> buf 緩衝區中,進入下一步;
- 呼叫 redisGetReplyFromReader 解析 buf 中的資料,並返回reply;獲取到的 redisReply 物件需要呼叫 freeReplyObject 顯式釋放,否則會洩漏。

redisAppendCommand 這個函式將傳送的命令格式化放入 redisContextobuf 中,可以一次傳送多條命令,然後接收一批結果,結果按照發送命令的順序儲存,這裡利用了 pipeline 特性,可以減少網路傳輸的次數,提高IO吞吐量。

釋放連線

使用完一個連線後需要呼叫 redisFree 函式來釋放這個連線,主要是關閉套接字,釋放申請的緩衝區。

void redisFree(redisContext *c) {
    if (c == NULL)
        return;
    if (c->fd > 0)
        close(c->fd);
    sdsfree(c->obuf);
    redisReaderFree(c->reader);
    free(c->tcp.host);
    free(c->tcp.source_addr);
    free(c->unix_sock.path);
    free(c->timeout);
    free(c);
}

一個例子

下面給出一個例子,對這幾個API最基本的使用,測試的時候需要先安裝 redis-server 並啟動,預設埠為 6379,同時需要安裝 hiredis 庫,sudo yum install hiredis-devel,或者原始碼安裝。

/*************************************************************************
    > File Name: redis-cli.c
    > Author: Tanswer_
    > Mail: [email protected]
    > Created Time: Thu Jun 28 15:49:43 2018
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <hiredis/hiredis.h>

int main()
{

    redisContext* c = redisConnect((char*)"127.0.0.1", 6379);
    if(c->err){
        redisFree(c);
        return 0;
    }
    printf("connect redis-server success.\n");

    const char* command = "set good luck";
    redisReply* r = (redisReply*)redisCommand(c, command);
    if(r == NULL){
        redisFree(c);
        return 0;
    }

    if(!(r->type == REDIS_REPLY_STATUS && strcasecmp(r->str, "OK") == 0)){
        printf("Failed to execute command[%s].\n", command);
        freeReplyObject(r);
        redisFree(c);
        return 0;
    }

    freeReplyObject(r);
    printf("Succeed to execute command[%s].\n", command);

    const char* command1 = "strlen good";
    r = (redisReply*)redisCommand(c, command1);
    if(r->type != REDIS_REPLY_INTEGER){
        printf("Failed to execute command[%s].\n", command1);
        freeReplyObject(r);
        redisFree(c);
        return 0;
    }

    int length = r -> integer;
    freeReplyObject(r);
    printf("The length of 'good' is %d.\n", length);
    printf("Succeed to execute command[%s].\n", command1);

    const char* command2 = "get good";
    r = (redisReply*)redisCommand(c, command2);
    if(r -> type != REDIS_REPLY_STRING){
        printf("Failed to execute command[%s].\n", command2);
        freeReplyObject(r);
        redisFree(c);
        return 0;
    }

    printf("The value of 'goo' is %s.\n", r->str);
    freeReplyObject(r);
    printf("Succeed to execute command[%s].\n", command2);

    redisFree(c);

    return 0;
}

相關推薦

Redis 客戶 Hiredis 簡介

學習總結一下官方釋出的C版本客戶端 hiredis,瞭解hiredis 客戶端大致實現細節。在理解程式碼之間需要了解通訊協議的特點,我上一篇轉載的文章已經有過介紹,大家可以去看一下。 hiredis 提供了同步、非同步訪問,非同步 API 需要與一些事件庫協同

Redis C客戶Hiredis代碼分析

-s sta 代碼分析 sge immediate sap pat process serve 初始化 redisContext - Redis連接的上下文 /* Context for a connection to Redis */ typedef struct re

redis記憶體資料庫C客戶hiredis API 中文說明

A)編譯安裝 make make install (/usr/local) make install PREFIX=$HOME/progs(可以自由指定安裝路徑) B)同步的API介面 redisContext *redisConnect(const char *ip, int port

redis的C客戶---hiRedis使用

redis的C客戶端—hiRedis使用 1. 客戶端通訊協議 Redis 制定了 **RESP(REdis Serialization Protocol,Redis 序列 化協議)**實現客戶端與服務端的正常互動,這種協議簡單高效,既能夠被機器解析,又容易被

Linux安裝編譯安裝hiredis,使得Swoole支援非同步Redis客戶

編譯安裝hiredis 使用Redis客戶端,需要安裝hiredis庫。下載hiredis原始碼後,執行 make -j sudo make install sudo ldconfig hiredis下載地址:https://github.com/redis/hiredis/re

Redis-C客戶-HiRedis-(二)

前幾篇介紹了redis以及phpredis,主要是因為我所在的專案組用的是php,而我接下來的一個小任務是用c++寫一個處理儲存在redis裡的業務資料的小工具,在redis官方上看推薦的c++客戶端,只有一個,而且還是2年前的一個臨時專案,而且還要依賴boost,而且看開發者的口氣,實在是覺得不敢用啊~

Redis客戶連線方式Hiredis簡單封裝使用,連線池、遮蔽連線細節

轉:https://blog.csdn.net/gdutliuyun827/article/details/44339007對Hiredis進行了簡單封裝,實現功能:1、API進行統一,對外只提供一個介面;2、遮蔽上層應用對連線的細節處理;3、底層採用佇列的方式保持連線池,儲

常用的Redis客戶的並發模型(轉)

war sta 進程 過程 blog 有效 tro nal 做的 偽代碼模型 # get lock lock = 0 while lock != 1: timestamp = current Unix time + lock timeou

redis 客戶無密碼交互刪除key

redisredis-cli -h www.badiu.com -a **** keys ‘key‘| xargs redis-cli -h www.abidu.com -a **** del-a 密碼www.baidu.com 地址本文出自 “磚家博客” 博客,請務必保留此出處http://wsxx

Redis 通信協議-了解 Redis 客戶實現原理

dubbo redis java 簡介幾乎所有的主流編程語言都有Redis的客戶端(http://redis.io/clients),不考慮Redis非常流行的原因,如果站在技術的角度看原因還有兩個:客戶端與服務端之間的通信協議是在 TCP 協議之上構建的。客戶端和服務器通過 TCP 連接來進行數

全球領先的redis客戶:SFedis

自帶 修改 標準 做了 red 崗位 and 穿透 監控 零、背景   這個客戶端起源於我們一個系統的生產問題。 一、問題的發生   在我們的生產環境上發生了兩次redis服務端連接數達到上限(我們配置的單節點連接數上限為8000)導致無法創建連接的情況。由於這

Redis學習筆記--Redis客戶(三)

本機 -c trace 圖形 tro cli family 毫秒 ati 1.Redis客戶端 1.1 Redis自帶的客戶端   (1)啟動   啟動客戶端命令:[root@kwredis bin]# ./redis-cli -h 127.0.0.1 -p 6379

Redis 客戶連接

-i ace back gpo word color clas tab cli Redis 客戶端連接 Redis 通過監聽一個 TCP 端口或者 Unix socket 的方式來接收來自客戶端的連接,當一個連接建立後,Redis 內部會進行以下一些操作: 首先,客戶端

[ 搭建Redis本地服務器實踐系列三 ] :圖解Redis客戶工具連接Redis服務器

done not 必須 tin 復雜 start exe eas 方便 上一章 [ 搭建Redis本地服務器實踐系列二 ] :圖解CentOS7配置Redis 介紹了Redis的初始化腳本文件及啟動配置文件,並圖解如何以服務的形式來啟動、終止Redis服務,可以說我們的R

阿裏雲專訪Redisson作者Rui Gu:構建開源企業級Redis客戶之路

為什麽 前景 clas 數據 value 計算機行業 編程 alt 階段 摘要: 本文為阿裏雲同學在RedisConf2018上對Redisson開源客戶端作者Rui Gu做的一個專訪,主要介紹了Rui Gu參與開啟Redisson客戶端開發的歷程,同時也詳細介紹了Redi

redis客戶連接,最大連接數查詢與設置

指定 col node 網絡 service服務 限制 style nbsp free ##redis客戶端連接數 redis通過監聽一個TCP端口或socket的方式接收來自客戶端的連接, 當與客戶端建立連接後,redis內部會進行如下操作:(1)客戶端socket會

配置Redis客戶

pro code bsp client edi class cli nts 客戶端 <properties> <jedis.version>2.7.2</jedis.version> </properties> &

windows 系統 使用 redis 客戶

有時會就是想測試一下redis 有沒有用 但是又不想跑專案, 這個時候就很有必要使用 window 的方式來連結redis 其實很簡單 1.再redis 目錄下開啟命令視窗 比較快捷的方式就是在位址列輸入cmd 再回車 2.再命令欄執行  redis-cli.ex

Jedis連線redis客戶

1 單點的redis利用jedis客戶端連線   如何連線 1 //1 利用jedis連線物件操作redis 2 @Test 3 public void test01(){ 4 //構造一個具有連線資訊的jedis物件 5 //確定虛擬機器linux系

Redis---客戶和服務

Redis---客戶端和服務端   文章轉載自:http://redisbook.readthedocs.io/en/latest/internal/redis.html http://www.spongeliu.com/category/linux https://blo