Node-redisにおけるquit()の優雅な脱退についてforEachの問題に遭遇

7776 ワード

Redisのnodeクライアントの公式ドキュメントにクライアントがredisとの接続を切断する方法は2つあります.
  • client.end()いわゆる「二言三言で直接暴力を断ち切る」.公式ドキュメントではend()メソッドをこのように記述しています:
  • Forcibly close the connection to the Redis server. Note that this does not wait until all replies have been parsed. If you want to exit cleanly, call client.quit() as mentioned above. You should set flush to true, if you are not absolutely sure you do not care about any other commands. If you set flush to false all still running commands will silently fail.
    大体end()メソッドは、すべての回答が解析されるまでredisとの接続を切断することはありません.彼はすぐにデータベースとの接続を切断します.優雅に終了するにはquit()を選択する必要があります.
  • quit()メソッドは、公式ドキュメント上でこのように記述されている:
  • This sends the quit command to the redis server and ends cleanly right after all running commands were properly handled. If this is called while reconnecting (and therefore no connection to the redis server exists) it is going to end the connection right away instead of resulting in further reconnections! All offline commands are going to be flushed with an error in that case.
    quitコマンドを呼び出してすべての実行コマンドを正しく処理した後、quitコマンドをredisサーバに送信し、完全に正しく終了することを目的としています.つまり、優雅な終了とは、再接続時に呼び出される(すなわちredisサーバの接続が存在しない)と、接続が直ちに終了し、さらなる再接続を招くことなく、すべてのオフラインコマンドがリフレッシュされ、エラーが発生することです.
    いわゆる優雅な脱退があるから、次のような書き方があります.
    var redis = require("redis");
    var client = redis.createClient({detect_buffers: true});
     
    client.set("foo_rand000000000000", "OK");
     
    // This will return a JavaScript String 
    client.get("foo_rand000000000000", function (err, reply) {
        console.log(reply.toString()); // Will print `OK` 
    });
     
    // This will return a Buffer since original key is specified as a Buffer 
    client.get(new Buffer("foo_rand000000000000"), function (err, reply) {
        console.log(reply.toString()); // Will print `` 
    });
    client.quit();
    
    

    このコードのclient.set()とclient.get()は間違いなく非同期のコードであり、ここではclient.end()はclientを置き換えます.quit()は、コマンドが正しく処理されない前にデータベースが切断され、エラーが発生することは間違いありません.quit()は、すべてのコマンドの返信を処理してから、データベースとの接続を安全に切断します.
    Redisのquit()とend()は、node-mysqlの2つのデータベースから切断される方法end()とdestroy()の2つに対応するクラス比である.
    次は私がコードを書くときにquitが優雅に退出すると思っていた問題で、元のコードは以下の通りです.
    /**
     *                 ,              1,              ;
     *   members     sortedSet    
     *                SISMEMBER           
     *       ’zscore key memberName‘         null       
     * @param {String}   keyName
     * @param {Array}  
     * @param {Function}
     */
    
    redisDB.addToSortedSet = (keyName, args, callback) => {
            let client = redisDB.connect();
            args.forEach((element, index) => {
                let params = [];
                params.push(keyName, element);
                client.zscore(params, (err, results) => {
                    if (err) {
                        logger.writeDebug(err);
                        callback(err);
                    } else {
                        if (results) { //      
                            let params = [];
                            params.push(keyName, 1, element);
                            client.zincrby(params, (err, res) => {
                                if (err) {
                                    logger.writeDebug(err);
                                    callback(err);
                                } else {
                                    callback(null, res);
                                }
                            })
    
                        } else { //         ,     0
                            let params = [];
                            params.push(keyName, 0, element);
                            client.zadd(params, (err, res) => {
                                if (err) {
                                    logger.writeDebug(err);
                                    callback(err);
                                } else {
                                    callback(null, res);
                                }
                            })
    
                        }
                    }
                })
            });
         client.quit();
        }
    

    このコードの主な構造は、nodeとRedisの接続を確立した後、一対の配列のforEachサイクルに従い、再循環体にネストされた2層の非同期コールバック関数があることです.(Tips:この段落を書くときは配列内の要素がredisの秩序集合にすでに存在するかどうかを判断する必要があります.秩序集合はSet構造のように要素がSetのメンバーかどうかを直接判断する命令があるわけではありません(sismember )ので、zscore key memberNameを使ってメンバーが存在する場合はこのメンバーに対応するscoreを返し、このメンバーが存在しない場合はnullを返します.存在するか否かを判断する)
    このコードは、呼び出しをテストして2つのデータを挿入したときに、このようなエラーを報告しました.
    { AbortError: 
    Stream connection ended and command aborted.
    It might have been processed.
      code: 'NR_CLOSED',
      command: 'ZINCRBY',}
    
    

    私の接続が切れたと言って、ZINCRBYに実行したときにコマンドが中断しました.......
    「優雅な脱退」も完全に安心させるものではなかったのか......△ネタを振ってみたい...
    私は新聞の間違いをよく見て、私は2つのデータベースにデータが存在していることを挿入して、最後に行くべきなのはすべて最も奥の層のZINCRBYのその非同期の分岐で、間違いを報告したのはforEachの中の2つの数のデータはすべてZINCRBYに実行してStream connection ended and command aborted.に報告したのです
    当時、私の頭の中で最初の単純な考えはforEachが非同期であるかどうかであった.それは似ているからである.quit()が優雅に脱退したのはredis自身のコマンドコールバックであり、他の非同期コールバックは気にしないが、forEachは関数を伝えたが、それ自体は非同期ではなく、テストコードは以下の通りであった.
    let args=[2,3,4];
    args.forEach( (element, index)=> {
        console.log(element);
    });
    console.log(1);
    

    印刷結果:
    2
    3
    4
    1
    

    想像していた非同期ではありません.必ずforEachの中で1階だけ非同期の状況を試してみましょう.試験が終わったら必ず問題が一致します.
    redisDB.addToSortedSet = (keyName, args, callback) => {
            let client = redisDB.connect();
            args.forEach((element, index) => {
                let params = [];
                params.push(keyName, element);
                client.zscore(params, (err, results) => {
                    if (err) {
                        logger.writeDebug(err);
                        callback(err);
                    } else {
                        console.log(results);
                    }
                })
            });
           client.quit();
        }
    

    今回の実行後は問題なく、2つの要素があるargsを伝えて、存在するためとても優雅に2人のメンバーの点数を返して、優雅な配慮も限られているようで、私はあなたの1階のすべての命令だけを担当してすべてあなたに結果をあげて、更に1階をネストしてあなたを管理しません:)
    それで...
    コードを次のように変更します.
    redisDB.addToSortedSet = (keyName, args, callback) => {
            let client = redisDB.connect();
            args.forEach((element, index) => {
                let params = [];
                params.push(keyName, element);
                client.zscore(params, (err, results) => {
                    if (err) {
                        logger.writeDebug(err);
                        callback(err);
                    } else {
                        if (results) { //      
                            let params = [];
                            params.push(keyName, 1, element);
                            client.zincrby(params, (err, res) => {
    
                                client.quit();
    
                                if (err) {
                                    logger.writeDebug(err);
                                    callback(err);
                                } else {
                                    callback(null, res);
                                }
                            })
    
                        } else { //         ,     0
                            let params = [];
                            params.push(keyName, 0, element);
                            client.zadd(params, (err, res) => {
    
                                client.quit();
    
                                if (err) {
                                    logger.writeDebug(err);
                                    callback(err);
                                } else {
                                    callback(null, res);
                                }
                            })
    
                        }
                    }
                })
            });
        }
    
    

    これで大丈夫だ......最下層非同期関数のコールバックでクライアントを呼び出す.quit()は、リクエストがすべて処理された後に「優雅に切れる」ことを保証します(最後に言いたいのは、初めてブログサイトに出力するのは、以前wikiで書いた片面とは違うかもしれませんが、これからもブログをよく書く習慣を身につけます.水平の限られた表現も向上しなければならないかもしれないので、皆さんの批判を受けて、一緒に勉強の道で一緒に進歩したいと思っています!)