Redis阻塞隊列原理學習

來源:互聯網
上載者:User

標籤:

1.redis介紹

Redis是一個開源的使用ANSI C語言編寫、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。

2.阻塞隊列

使用非阻塞隊列的時候有一個很大問題就是:它不會對當前線程產生阻塞,那麼在面對類似消費者-生產者的模型時,就必須額外地實現同步策略以及線程間喚醒策略,這個實現起來就非常麻煩。但是有了阻塞隊列就不一樣了,它會對當前線程產生阻塞,比如一個線程從一個空的阻塞隊列中取元素,此時線程會被阻塞直到阻塞隊列中有了元素。當隊列中有元素後,被阻塞的線程會自動被喚醒(不需要我們編寫代碼去喚醒)。這樣提供了極大的方便性。

3.redis的阻塞隊列

redis提供遠端存取模式,本文以redis遠程阻塞從隊列中取資料為例,介紹redis阻塞實現原理。

/*----------------------------------------------------------------------------- * Blocking POP operations *----------------------------------------------------------------------------*//* This is how the current blocking POP works, we use BLPOP as example: * - If the user calls BLPOP and the key exists and contains a non empty list *   then LPOP is called instead. So BLPOP is semantically the same as LPOP *   if blocking is not required. * - If instead BLPOP is called and the key does not exists or the list is *   empty we need to block. In order to do so we remove the notification for *   new data to read in the client socket (so that we‘ll not serve new *   requests if the blocking request is not served). Also we put the client *   in a dictionary (db->blocking_keys) mapping keys to a list of clients *   blocking for this keys. * - If a PUSH operation against a key with blocked clients waiting is *   performed, we mark this key as "ready", and after the current command, *   MULTI/EXEC block, or script, is executed, we serve all the clients waiting *   for this list, from the one that blocked first, to the last, accordingly *   to the number of elements we have in the ready list. */

  在調用阻塞隊列取操作時,隊列中無資料時才會真正的觸發代碼的阻塞分支。在用戶端,redis對新來的讀請求刪除了標記(通知),這樣知道阻塞的請求在獲得服務前,新來的讀請求都不能夠被正常的服務。

blpop

void blpopCommand(redisClient *c) {    blockingPopGenericCommand(c,REDIS_HEAD);}

  隊列的左右出隊對應隊列(list)的表頭和表尾。

blockingPopGenericCommand

/* Blocking RPOP/LPOP */void blockingPopGenericCommand(redisClient *c, int where) {    robj *o;    mstime_t timeout;    int j;    if (getTimeoutFromObjectOrReply(c,c->argv[c->argc-1],&timeout,UNIT_SECONDS)        != REDIS_OK) return;    for (j = 1; j < c->argc-1; j++) {        o = lookupKeyWrite(c->db,c->argv[j]);        if (o != NULL) {            if (o->type != REDIS_LIST) {                addReply(c,shared.wrongtypeerr);                return;            } else {                if (listTypeLength(o) != 0) {                    /* Non empty list, this is like a non normal [LR]POP. */                    char *event = (where == REDIS_HEAD) ? "lpop" : "rpop";                    robj *value = listTypePop(o,where);                    redisAssert(value != NULL);                    addReplyMultiBulkLen(c,2);                    addReplyBulk(c,c->argv[j]);                    addReplyBulk(c,value);                    decrRefCount(value);                    notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,                                        c->argv[j],c->db->id);                    if (listTypeLength(o) == 0) {                        dbDelete(c->db,c->argv[j]);                        notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",                                            c->argv[j],c->db->id);                    }                    signalModifiedKey(c->db,c->argv[j]);                    server.dirty++;                    /* Replicate it as an [LR]POP instead of B[LR]POP. */                    rewriteClientCommandVector(c,2,                        (where == REDIS_HEAD) ? shared.lpop : shared.rpop,                        c->argv[j]);                    return;                }            }        }    }    /* If we are inside a MULTI/EXEC and the list is empty the only thing     * we can do is treating it as a timeout (even with timeout 0). */    if (c->flags & REDIS_MULTI) {        addReply(c,shared.nullmultibulk);        return;    }    /* If the list is empty or the key does not exists we must block */    //這裡才是阻塞的關鍵        blockForKeys(c, c->argv + 1, c->argc - 2, timeout, NULL);}

  

阻塞

/* Set a client in blocking mode for the specified key, with the specified * timeout */void blockForKeys(redisClient *c, robj **keys, int numkeys, mstime_t timeout, robj *target) {    dictEntry *de;    list *l;    int j;    c->bpop.timeout = timeout;    c->bpop.target = target;    if (target != NULL) incrRefCount(target);    for (j = 0; j < numkeys; j++) {        /* If the key already exists in the dict ignore it. */        if (dictAdd(c->bpop.keys,keys[j],NULL) != DICT_OK) continue;        incrRefCount(keys[j]);        /* And in the other "side", to map keys -> clients */        de = dictFind(c->db->blocking_keys,keys[j]);        if (de == NULL) {            int retval;            /* For every key we take a list of clients blocked for it */            l = listCreate();            retval = dictAdd(c->db->blocking_keys,keys[j],l);            incrRefCount(keys[j]);            redisAssertWithInfo(c,keys[j],retval == DICT_OK);        } else {            l = dictGetVal(de);        }        listAddNodeTail(l,c);    }    blockClient(c,REDIS_BLOCKED_LIST);}

  

 

Redis阻塞隊列原理學習

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.