4

私はセッションデータを保存するためにconnect-redisでnodejsを使用しています。

セッションにユーザーデータを保存し、セッションの存続期間中使用します。

セッションデータを変更する2つのリクエスト間に競合状態が発生する可能性があることに気づきました。

redis-lockを使用してセッションをロックしようとしましたが、少し問題があります。

セッション全体をロックしたくはありませんが、代わりに特定のセッション変数のみをロックします。

私はそれが不可能であることに気づき、それを解決するための方向性について考えました。

セッションオブジェクトを使用してユーザーデータを保存するのをやめ、変数を直接Redisに保存して、使用する前にロックします。

動作することはわかっていますが、セッションオブジェクトを介してredisにアクセスするだけでなく、すべてのオブジェクトを手動で管理する必要があります。

ベストプラクティスと提案を教えていただけますか?

ありがとう、リオール

4

1 に答える 1

5

さて、あなた自身のストレージを実装することはあなたのためのオプションかもしれません。このドキュメントは、あなたがする必要があるのは、、、および(最後の段落を参照)の3つのメソッドを実装することだけであることを示して.get.setます.destroy。これは次のようになります(node-redisライブラリを使用し、元のconnect-redisストアを少し変更します):

var redis = require("redis"),
    redis_client = redis.createClient(),
    session_prefix = 'session::',
    lock_suffix = '::lock',
    threshold = 5000,
    wait_time = 250,
    oneDay = 86400;

/* If timeout is greater then threshold, then we assume that
   one of the Redis Clients is dead and he cannot realese
   the lock. */

function CustomSessionStore(opts) {
    opts = opts || {};
    var self = this;
    self.ttl = opts.ttl; // <---- used for setting timeout on session

    self.lock = function(sid, callback) {
        callback = callback || function(){};
        var key = session_prefix + sid + lock_suffix;
        // try setting the lock with current Date
        redis_client.setnx(key, Date.now( ), function(err, res) {
            // some error handling?
            if (res) {
                // Everything's fine, call callback.
                callback();
                return;
            }

            // setnx failed, look at timeout
            redis_client.get(key, function(err, res) {
                // some error handling?
                if (parseInt(res) + threshold > Date.now( )) {
                    // timeout, release the old lock and lock it
                    redis_client.getset(key, Date.now( ), function(err, date) {
                        if (parseInt(date) + threshold > Date.now()) {
                            // ups, some one else was faster in acquiring lock
                            setTimeout(function() {
                                self.lock(sid, callback);
                            }, wait_time);
                            return;
                        }
                        callback();
                    });
                    return;
                }
                // it is not time yet, wait and try again later
                setTimeout(function() {
                    self.lock(sid, callback);
                }, wait_time);
            });
        });
    };

    self.unlock = function(sid, callback) {
        callback = callback || function(){};
        var key = session_prefix + sid + lock_suffix;
        redis_client.del(key, function(err) {
            // some error handling?
            callback();
        });
    };

    self.get = function(sid, callback) {
        callback = callback || function(){};
        var key = session_prefix + sid;
        // lock the session
        self.lock(sid, function() {
            redis_client.get(key, function(err, data) {
                if (err) {
                    callback(err);
                    return;
                }
                try {
                    callback(null, JSON.parse(data));
                } catch(e) {
                    callback(e);
                }
            });
        });
    };

    self.set = function(sid, data, callback) {
        callback = callback || function(){};
        try {
            // ttl used for expiration of session
            var maxAge = sess.cookie.maxAge
              , ttl = self.ttl
              , sess = JSON.stringify(sess);

            ttl = ttl || ('number' == typeof maxAge
                  ? maxAge / 1000 | 0
                  : oneDay);

        } catch(e) {
            callback(e);
            return;
        }
        var key = session_prefix + sid;
        redis_client.setex(key, ttl, data, function(err) {
            // unlock the session
            self.unlock(sid, function(_err) {
                callback(err || _err);
            });
        });
    };

    self.destroy = function(sid, callback) {
        var key = session_prefix + sid;
        redis_client.del(key, function(err) {
            redis_client.unlock(sid, function(_err) {
                callback(err || _err);
            });
        });
    };
}

補足.lock:とのエラー処理は実装していません.unlock。これはあなたにお任せします!:)いくつかの小さな間違いがあるかもしれませんが(私は現在NodeJSを持っておらず、これを私の記憶から書いています:D)、あなたはその考えを理解する必要があります。これは、 Redisのロック/ロック解除の使用方法に関する説明を含むリンクです。setnx

もう1つの注意:ルートが例外をスローした場合、Redisセッションのロックが解除されないため、ルートに対してカスタムエラー処理を行うことをお勧めします。この.setメソッドは常にルートの最後のものとして呼び出され.getます。これは、Expressがルートの最初で呼び出すメソッドとは逆です(そのため、でロック.getおよびロック解除し.setます)。それでも5秒間だけロックされるので、問題になる必要はありません。ニーズ(特に変数)thresholdに合わせて調整することを忘れないでください。wait_time

最後の注意:このメカニズムでは、リクエストハンドラーはユーザーごとに次々にのみ起動します。これは、ユーザーごとに同時ハンドラーを実行できないことを意味します。これは問題になる可能性があるため、別のアイデアは、セッションの外部でデータを保持し、手動でロック/ロック解除を処理することです。結局のところ、手動​​で処理しなければならないことがいくつかあります。

お役に立てば幸いです。幸運を!

于 2012-07-10T22:19:46.950 に答える